Replace hotkey scopes by focus stack (Part 4 - Inputs) (#12933)
# Replace hotkey scopes by focus stack (Part 4 - Inputs) This PR is the 4th part of a refactoring aiming to deprecate the hotkey scopes api in favor of the new focus stack api which is more robust. Part 1: https://github.com/twentyhq/twenty/pull/12673 Part 2: https://github.com/twentyhq/twenty/pull/12798 Part 3: https://github.com/twentyhq/twenty/pull/12910 In this part, I refactored all inputs in the app so that each input has a unique id which can be used to track the focused element.
This commit is contained in:
@ -64,15 +64,15 @@ const TextInput: React.FC<TextInputProps> = ({
|
||||
placeholder,
|
||||
icon,
|
||||
}) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
return (
|
||||
<StyledContainer fullWidth={fullWidth}>
|
||||
{label && <StyledLabel htmlFor={inputId}>{label}</StyledLabel>}
|
||||
{label && <StyledLabel htmlFor={instanceId}>{label}</StyledLabel>}
|
||||
<StyledInputContainer>
|
||||
{icon && <StyledIcon>{icon}</StyledIcon>}
|
||||
<StyledInput
|
||||
id={inputId}
|
||||
id={instanceId}
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
|
||||
@ -10,7 +10,7 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { Chip, ChipAccent, ChipSize, ChipVariant } from 'twenty-ui/components';
|
||||
import { IconCalendarEvent } from 'twenty-ui/display';
|
||||
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
@ -20,6 +20,8 @@ type CalendarEventDetailsProps = {
|
||||
calendarEvent: CalendarEvent;
|
||||
};
|
||||
|
||||
const INPUT_ID_PREFIX = 'calendar-event-details';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
align-items: flex-start;
|
||||
@ -111,10 +113,14 @@ export const CalendarEventDetails = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(calendarEvent.id, fieldName),
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId: calendarEvent.id,
|
||||
fieldName,
|
||||
prefix: INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell readonly />
|
||||
<RecordInlineCell instanceIdPrefix={INPUT_ID_PREFIX} />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StyledPropertyBox>
|
||||
|
||||
@ -34,8 +34,7 @@ import { useRestoreManyRecords } from '@/object-record/hooks/useRestoreManyRecor
|
||||
import { useIsRecordReadOnly } from '@/object-record/record-field/hooks/useIsRecordReadOnly';
|
||||
import { isInlineCellInEditModeScopedState } from '@/object-record/record-inline-cell/states/isInlineCellInEditModeScopedState';
|
||||
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
import { getRecordTitleCellId } from '@/object-record/record-title-cell/utils/getRecordTitleCellId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { BlockEditor } from '@/ui/input/editor/components/BlockEditor';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
@ -364,11 +363,11 @@ export const ActivityRichTextEditor = ({
|
||||
objectRecordId: activityId,
|
||||
});
|
||||
|
||||
const recordTitleCellId = getRecordTitleCellId(
|
||||
activityId,
|
||||
labelIdentifierFieldMetadataItem?.id,
|
||||
RecordTitleCellContainerType.ShowPage,
|
||||
);
|
||||
const recordTitleCellId = getRecordFieldInputInstanceId({
|
||||
recordId: activityId,
|
||||
fieldName: labelIdentifierFieldMetadataItem?.id,
|
||||
prefix: 'activity-rich-text-editor',
|
||||
});
|
||||
|
||||
const handleBlockEditorFocus = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
|
||||
@ -165,6 +165,7 @@ export const AttachmentRow = ({
|
||||
<AttachmentIcon attachmentType={attachment.type} />
|
||||
{isEditing ? (
|
||||
<TextInput
|
||||
instanceId={`attachment-${attachment.id}-name`}
|
||||
value={attachmentFileName}
|
||||
onChange={handleOnChange}
|
||||
onBlur={handleOnBlur}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledFullWidthMotionDiv = styled(motion.div)`
|
||||
@ -41,6 +41,7 @@ export const SignInUpEmailField = ({
|
||||
}) => (
|
||||
<StyledInputContainer>
|
||||
<TextInput
|
||||
instanceId="sign-in-up-email"
|
||||
autoFocus
|
||||
value={value}
|
||||
placeholder="Email"
|
||||
|
||||
@ -46,6 +46,7 @@ export const SignInUpPasswordField = ({
|
||||
}) => (
|
||||
<StyledInputContainer>
|
||||
<TextInput
|
||||
instanceId="sign-in-up-password"
|
||||
autoFocus
|
||||
value={value}
|
||||
type="password"
|
||||
|
||||
@ -40,6 +40,7 @@ export const ObjectFilterDropdownNumberInput = () => {
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<DropdownMenuInput
|
||||
instanceId="object-filter-dropdown-number-input"
|
||||
ref={handleInputRef}
|
||||
value={objectFilterDropdownFilterValue}
|
||||
autoFocus
|
||||
|
||||
@ -40,6 +40,7 @@ export const ObjectFilterDropdownTextInput = () => {
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<DropdownMenuInput
|
||||
instanceId="object-filter-dropdown-text-input"
|
||||
ref={handleInputRef}
|
||||
value={objectFilterDropdownFilterValue}
|
||||
autoFocus
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardCardBodyContainer } from '@/object-record/record-board/record-board-card/components/RecordBoardCardBodyContainer';
|
||||
import { StopPropagationContainer } from '@/object-record/record-board/record-board-card/components/StopPropagationContainer';
|
||||
import { RECORD_BOARD_CARD_INPUT_ID_PREFIX } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardInputIdPrefix';
|
||||
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
|
||||
import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
|
||||
import {
|
||||
@ -13,7 +14,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon';
|
||||
import { isFieldValueReadOnly } from '@/object-record/record-field/utils/isFieldValueReadOnly';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const RecordBoardCardBody = ({
|
||||
@ -73,14 +74,16 @@ export const RecordBoardCardBody = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldDefinition.metadata.fieldName,
|
||||
'record-board-card',
|
||||
),
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: RECORD_BOARD_CARD_INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
<RecordInlineCell
|
||||
instanceIdPrefix={RECORD_BOARD_CARD_INPUT_ID_PREFIX}
|
||||
/>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StopPropagationContainer>
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const RECORD_BOARD_CARD_INPUT_ID_PREFIX = 'record-board-card';
|
||||
@ -29,7 +29,7 @@ export const FormBooleanFieldInput = ({
|
||||
readonly,
|
||||
VariablePicker,
|
||||
}: FormBooleanFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
const [draftValue, setDraftValue] = useState<
|
||||
| {
|
||||
@ -105,7 +105,7 @@ export const FormBooleanFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -89,7 +89,7 @@ export const FormDateTimeFieldInput = ({
|
||||
isDateTimeInput: !dateOnly,
|
||||
});
|
||||
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
const [draftValue, setDraftValue] = useState<DraftValue>(
|
||||
isStandaloneVariableString(defaultValue)
|
||||
@ -340,7 +340,7 @@ export const FormDateTimeFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -82,7 +82,7 @@ export const FormMultiSelectFieldInput = ({
|
||||
placeholder,
|
||||
testId,
|
||||
}: FormMultiSelectFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
const theme = useTheme();
|
||||
|
||||
const hotkeyScope =
|
||||
@ -268,7 +268,7 @@ export const FormMultiSelectFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly && (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -45,7 +45,7 @@ export const FormNumberFieldInput = ({
|
||||
readonly,
|
||||
error: errorFromProps,
|
||||
}: FormNumberFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
const [errorMessage, setErrorMessage] = useState<string | undefined>(
|
||||
undefined,
|
||||
);
|
||||
@ -117,7 +117,7 @@ export const FormNumberFieldInput = ({
|
||||
|
||||
return (
|
||||
<FormFieldInputContainer>
|
||||
{label ? <InputLabel htmlFor={inputId}>{label}</InputLabel> : null}
|
||||
{label ? <InputLabel htmlFor={instanceId}>{label}</InputLabel> : null}
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInnerContainer
|
||||
@ -126,7 +126,7 @@ export const FormNumberFieldInput = ({
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<StyledInput
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
placeholder={
|
||||
isDefined(placeholder) && !isEmpty(placeholder)
|
||||
? placeholder
|
||||
@ -148,7 +148,7 @@ export const FormNumberFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -31,7 +31,7 @@ export const FormRawJsonFieldInput = ({
|
||||
readonly,
|
||||
VariablePicker,
|
||||
}: FormRawJsonFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
const editor = useTextVariableEditor({
|
||||
placeholder: placeholder ?? 'Enter a JSON object',
|
||||
@ -80,7 +80,7 @@ export const FormRawJsonFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly && (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
multiline
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
|
||||
@ -6,16 +6,16 @@ import { VariablePickerComponent } from '@/object-record/record-field/form-types
|
||||
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useId, useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { IconCircleOff } from 'twenty-ui/display';
|
||||
import { SelectOption } from 'twenty-ui/input';
|
||||
|
||||
type FormSelectFieldInputProps = {
|
||||
label?: string;
|
||||
@ -36,7 +36,7 @@ export const FormSelectFieldInput = ({
|
||||
}: FormSelectFieldInputProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
const hotkeyScope = InlineCellHotkeyScope.InlineCell;
|
||||
|
||||
@ -135,7 +135,7 @@ export const FormSelectFieldInput = ({
|
||||
<FormFieldInputRowContainer>
|
||||
{draftValue.type === 'static' ? (
|
||||
<Select
|
||||
dropdownId={`${inputId}-select-display`}
|
||||
dropdownId={`${instanceId}-select-display`}
|
||||
options={options}
|
||||
value={selectedOption?.value}
|
||||
onChange={onSelect}
|
||||
@ -160,7 +160,7 @@ export const FormSelectFieldInput = ({
|
||||
|
||||
{isDefined(VariablePicker) && !readonly && (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -204,7 +204,7 @@ export const FormSingleRecordPicker = ({
|
||||
)}
|
||||
{isDefined(VariablePicker) && !disabled && (
|
||||
<VariablePicker
|
||||
inputId={variablesDropdownId}
|
||||
instanceId={variablesDropdownId}
|
||||
disabled={disabled}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
objectNameSingularToSelect={objectNameSingular}
|
||||
|
||||
@ -36,7 +36,7 @@ export const FormTextFieldInput = ({
|
||||
readonly,
|
||||
VariablePicker,
|
||||
}: FormTextFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
const editor = useTextVariableEditor({
|
||||
placeholder: placeholder ?? 'Enter text',
|
||||
@ -84,7 +84,7 @@ export const FormTextFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
multiline={multiline}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
|
||||
@ -31,7 +31,7 @@ export const FormUuidFieldInput = ({
|
||||
readonly,
|
||||
VariablePicker,
|
||||
}: FormUuidFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
const instanceId = useId();
|
||||
|
||||
const [draftValue, setDraftValue] = useState<
|
||||
| {
|
||||
@ -91,7 +91,7 @@ export const FormUuidFieldInput = ({
|
||||
|
||||
return (
|
||||
<FormFieldInputContainer>
|
||||
{label ? <InputLabel htmlFor={inputId}>{label}</InputLabel> : null}
|
||||
{label ? <InputLabel htmlFor={instanceId}>{label}</InputLabel> : null}
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInnerContainer
|
||||
@ -99,7 +99,7 @@ export const FormUuidFieldInput = ({
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<StyledInput
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
placeholder={placeholder ?? 'Enter a UUID'}
|
||||
value={draftValue.value}
|
||||
copyButton={false}
|
||||
@ -117,7 +117,7 @@ export const FormUuidFieldInput = ({
|
||||
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
instanceId={instanceId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export type VariablePickerComponent = React.FC<{
|
||||
inputId: string;
|
||||
instanceId: string;
|
||||
disabled?: boolean;
|
||||
multiline?: boolean;
|
||||
onVariableSelect: (variableName: string) => void;
|
||||
|
||||
@ -15,11 +15,11 @@ import {
|
||||
} from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
|
||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
@ -41,9 +41,11 @@ export const useOpenFieldInputEditMode = () => {
|
||||
({
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
prefix,
|
||||
}: {
|
||||
fieldDefinition: FieldDefinition<FieldMetadata>;
|
||||
recordId: string;
|
||||
prefix?: string;
|
||||
}) => {
|
||||
if (
|
||||
isFieldRelationFromManyObjects(fieldDefinition) &&
|
||||
@ -75,9 +77,10 @@ export const useOpenFieldInputEditMode = () => {
|
||||
});
|
||||
|
||||
openActivityTargetCellEditMode({
|
||||
recordPickerInstanceId: getFieldInputInstanceId({
|
||||
recordPickerInstanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix,
|
||||
}),
|
||||
activityTargetObjectRecords,
|
||||
});
|
||||
@ -87,7 +90,8 @@ export const useOpenFieldInputEditMode = () => {
|
||||
if (isFieldRelationToOneObject(fieldDefinition)) {
|
||||
openRelationToOneFieldInput({
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
recordId: recordId,
|
||||
recordId,
|
||||
prefix,
|
||||
});
|
||||
|
||||
return;
|
||||
@ -103,22 +107,25 @@ export const useOpenFieldInputEditMode = () => {
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
objectNameSingular:
|
||||
fieldDefinition.metadata.relationObjectMetadataNameSingular,
|
||||
recordId: recordId,
|
||||
recordId,
|
||||
prefix,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: getFieldInputInstanceId({
|
||||
focusId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix,
|
||||
}),
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: getFieldInputInstanceId({
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix,
|
||||
}),
|
||||
},
|
||||
hotkeyScope: {
|
||||
@ -142,14 +149,17 @@ export const useOpenFieldInputEditMode = () => {
|
||||
const closeFieldInput = ({
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
prefix,
|
||||
}: {
|
||||
fieldDefinition: FieldDefinition<FieldMetadata>;
|
||||
recordId: string;
|
||||
prefix?: string;
|
||||
}) => {
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: getFieldInputInstanceId({
|
||||
focusId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@ -2,11 +2,13 @@ import { useAddressField } from '@/object-record/record-field/meta-types/hooks/u
|
||||
import { FieldAddressDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
|
||||
import { AddressInput } from '@/ui/field/input/components/AddressInput';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
|
||||
export type AddressFieldInputProps = {
|
||||
@ -70,8 +72,13 @@ export const AddressFieldInput = ({
|
||||
setDraftValue(convertToAddress(newAddress));
|
||||
};
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
return (
|
||||
<AddressInput
|
||||
instanceId={instanceId}
|
||||
value={convertToAddress(draftValue)}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
|
||||
@ -6,11 +6,13 @@ import { CurrencyInput } from '@/ui/field/input/components/CurrencyInput';
|
||||
|
||||
import { useCurrencyField } from '../../hooks/useCurrencyField';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
type CurrencyFieldInputProps = {
|
||||
@ -31,6 +33,10 @@ export const CurrencyFieldInput = ({
|
||||
const { draftValue, persistCurrencyField, setDraftValue, defaultValue } =
|
||||
useCurrencyField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const defaultCurrencyCodeWithoutSQLQuotes = (
|
||||
defaultValue as FieldCurrencyValue
|
||||
).currencyCode.replace(/'/g, '') as CurrencyCode;
|
||||
@ -114,6 +120,7 @@ export const CurrencyFieldInput = ({
|
||||
|
||||
return (
|
||||
<CurrencyInput
|
||||
instanceId={instanceId}
|
||||
value={draftValue?.amount?.toString() ?? ''}
|
||||
currencyCode={currencyCode}
|
||||
autoFocus
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { useDateField } from '@/object-record/record-field/meta-types/hooks/useDateField';
|
||||
import { DateInput } from '@/ui/field/input/components/DateInput';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Nullable } from 'twenty-ui/utilities';
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
@ -26,6 +28,10 @@ export const DateFieldInput = ({
|
||||
}: DateFieldInputProps) => {
|
||||
const { fieldValue, setDraftValue } = useDateField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const persistDate = (newDate: Nullable<Date>) => {
|
||||
@ -73,6 +79,7 @@ export const DateFieldInput = ({
|
||||
|
||||
return (
|
||||
<DateInput
|
||||
instanceId={instanceId}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
|
||||
@ -2,8 +2,10 @@ import { DateInput } from '@/ui/field/input/components/DateInput';
|
||||
|
||||
import { FieldInputEvent } from '@/object-record/record-field/meta-types/input/components/NumberFieldInput';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { Nullable } from 'twenty-ui/utilities';
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useDateTimeField } from '../../hooks/useDateTimeField';
|
||||
@ -25,6 +27,10 @@ export const DateTimeFieldInput = ({
|
||||
}: DateTimeFieldInputProps) => {
|
||||
const { fieldValue, setDraftValue } = useDateTimeField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const persistDate = (newDate: Nullable<Date>) => {
|
||||
@ -68,6 +74,7 @@ export const DateTimeFieldInput = ({
|
||||
|
||||
return (
|
||||
<DateInput
|
||||
instanceId={instanceId}
|
||||
onClickOutside={handleClickOutside}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
|
||||
@ -5,11 +5,13 @@ import { DoubleTextInput } from '@/ui/field/input/components/DoubleTextInput';
|
||||
import { FIRST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS } from '@/object-record/record-field/meta-types/input/constants/FirstNamePlaceholder';
|
||||
import { LAST_NAME_PLACEHOLDER_WITH_SPECIAL_CHARACTER_TO_AVOID_PASSWORD_MANAGERS } from '@/object-record/record-field/meta-types/input/constants/LastNamePlaceholder';
|
||||
import { isDoubleTextFieldEmpty } from '@/object-record/record-field/meta-types/input/utils/isDoubleTextFieldEmpty';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
|
||||
type FullNameFieldInputProps = {
|
||||
onClickOutside?: FieldInputClickOutsideEvent;
|
||||
@ -78,8 +80,13 @@ export const FullNameFieldInput = ({
|
||||
setDraftValue(getRequiredDraftValueFromDoubleText(newDoubleText));
|
||||
};
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
return (
|
||||
<DoubleTextInput
|
||||
instanceId={instanceId}
|
||||
firstValue={draftValue?.firstName ?? ''}
|
||||
secondValue={draftValue?.lastName ?? ''}
|
||||
firstValuePlaceholder={
|
||||
|
||||
@ -3,8 +3,8 @@ import styled from '@emotion/styled';
|
||||
import { forwardRef, InputHTMLAttributes, ReactNode, useRef } from 'react';
|
||||
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
|
||||
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
|
||||
import { TEXT_INPUT_STYLE } from 'twenty-ui/theme';
|
||||
import { useCombinedRefs } from '~/hooks/useCombinedRefs';
|
||||
|
||||
const StyledInput = styled.input<{
|
||||
withRightComponent?: boolean;
|
||||
@ -84,6 +84,7 @@ export type MultiItemBaseInputProps = Omit<HTMLInputProps, 'onChange'> & {
|
||||
hasError?: boolean;
|
||||
hasItem: boolean;
|
||||
onChange: (value: string) => void;
|
||||
instanceId: string;
|
||||
};
|
||||
|
||||
export const MultiItemBaseInput = forwardRef<
|
||||
@ -108,6 +109,7 @@ export const MultiItemBaseInput = forwardRef<
|
||||
error = '',
|
||||
hasError = false,
|
||||
hasItem,
|
||||
instanceId,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@ -115,6 +117,7 @@ export const MultiItemBaseInput = forwardRef<
|
||||
const combinedRef = useCombinedRefs(ref, inputRef);
|
||||
|
||||
useRegisterInputEvents({
|
||||
focusId: instanceId,
|
||||
inputRef,
|
||||
inputValue: value,
|
||||
onEnter,
|
||||
|
||||
@ -6,13 +6,15 @@ import {
|
||||
MultiItemBaseInput,
|
||||
MultiItemBaseInputProps,
|
||||
} from '@/object-record/record-field/meta-types/input/components/MultiItemBaseInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { PhoneRecord } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { IconCheck, IconPlus } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
@ -79,7 +81,17 @@ export const MultiItemFieldInput = <T,>({
|
||||
listenerId: hotkeyScope,
|
||||
});
|
||||
|
||||
useScopedHotkeys(Key.Escape, handleDropdownClose, hotkeyScope);
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
useHotkeysOnFocusedElement({
|
||||
focusId: instanceId,
|
||||
keys: [Key.Escape],
|
||||
callback: handleDropdownClose,
|
||||
scope: hotkeyScope,
|
||||
dependencies: [handleDropdownClose],
|
||||
});
|
||||
|
||||
const [isInputDisplayed, setIsInputDisplayed] = useState(false);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
@ -204,6 +216,7 @@ export const MultiItemFieldInput = <T,>({
|
||||
)}
|
||||
{isInputDisplayed || !items.length ? (
|
||||
<MultiItemBaseInput
|
||||
instanceId={instanceId}
|
||||
autoFocus
|
||||
placeholder={placeholder}
|
||||
value={inputValue}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
|
||||
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { MultiSelectInput } from '@/ui/field/input/components/MultiSelectInput';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
|
||||
type MultiSelectFieldInputProps = {
|
||||
onCancel?: () => void;
|
||||
@ -10,18 +11,18 @@ type MultiSelectFieldInputProps = {
|
||||
export const MultiSelectFieldInput = ({
|
||||
onCancel,
|
||||
}: MultiSelectFieldInputProps) => {
|
||||
const { persistField, fieldDefinition, fieldValues, recordId } =
|
||||
useMultiSelectField();
|
||||
const { persistField, fieldDefinition, fieldValues } = useMultiSelectField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelectInput
|
||||
selectableListComponentInstanceId={
|
||||
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
||||
}
|
||||
focusId={getFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
})}
|
||||
focusId={instanceId}
|
||||
options={fieldDefinition.metadata.options}
|
||||
onCancel={onCancel}
|
||||
onOptionSelected={persistField}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { TextInput } from '@/ui/field/input/components/TextInput';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { FieldInputContainer } from '@/ui/field/input/components/FieldInputContainer';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useNumberField } from '../../hooks/useNumberField';
|
||||
|
||||
export type FieldInputEvent = (persist: () => void) => void;
|
||||
@ -25,6 +27,10 @@ export const NumberFieldInput = ({
|
||||
const { fieldDefinition, draftValue, setDraftValue, persistNumberField } =
|
||||
useNumberField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const handleEnter = (newText: string) => {
|
||||
onEnter?.(() => persistNumberField(newText));
|
||||
};
|
||||
@ -55,6 +61,7 @@ export const NumberFieldInput = ({
|
||||
return (
|
||||
<FieldInputContainer>
|
||||
<TextInput
|
||||
instanceId={instanceId}
|
||||
placeholder={fieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={draftValue?.toString() ?? ''}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { isWorkflowRunJsonField } from '@/object-record/record-field/meta-types/utils/isWorkflowRunJsonField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useRef, useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
@ -70,6 +72,9 @@ export const RawJsonFieldInput = ({
|
||||
const hotkeyScope = DEFAULT_CELL_SCOPE.scope;
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
@ -104,32 +109,35 @@ export const RawJsonFieldInput = ({
|
||||
listenerId: hotkeyScope,
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Escape],
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Escape],
|
||||
callback: () => {
|
||||
handleEscape(draftValue ?? '');
|
||||
},
|
||||
hotkeyScope,
|
||||
[handleEscape, draftValue],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [handleEscape, draftValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'tab',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: ['tab'],
|
||||
callback: () => {
|
||||
handleTab(draftValue ?? '');
|
||||
},
|
||||
hotkeyScope,
|
||||
[handleTab, draftValue],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [handleTab, draftValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'shift+tab',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: ['shift+tab'],
|
||||
callback: () => {
|
||||
handleShiftTab(draftValue ?? '');
|
||||
},
|
||||
hotkeyScope,
|
||||
[handleShiftTab, draftValue],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [handleShiftTab, draftValue],
|
||||
});
|
||||
|
||||
const showEditingButton = !isWorkflowRunJsonField({
|
||||
objectMetadataNameSingular:
|
||||
|
||||
@ -10,14 +10,15 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
|
||||
import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField';
|
||||
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
||||
import { useUpdateRelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/hooks/useUpdateRelationFromManyFieldInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { MultipleRecordPicker } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker';
|
||||
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
|
||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
@ -31,10 +32,9 @@ export const RelationFromManyFieldInput = ({
|
||||
onSubmit,
|
||||
}: RelationFromManyFieldInputProps) => {
|
||||
const { fieldDefinition, recordId } = useContext(FieldContext);
|
||||
const recordPickerInstanceId = getFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
});
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const { updateRelation } = useUpdateRelationFromManyFieldInput();
|
||||
const fieldName = fieldDefinition.metadata.fieldName;
|
||||
@ -94,7 +94,7 @@ export const RelationFromManyFieldInput = ({
|
||||
const multipleRecordPickerPickableMorphItemsCallbackState =
|
||||
useRecoilComponentCallbackStateV2(
|
||||
multipleRecordPickerPickableMorphItemsComponentState,
|
||||
recordPickerInstanceId,
|
||||
instanceId,
|
||||
);
|
||||
const { performSearch: multipleRecordPickerPerformSearch } =
|
||||
useMultipleRecordPickerPerformSearch();
|
||||
@ -123,7 +123,7 @@ export const RelationFromManyFieldInput = ({
|
||||
set(multipleRecordPickerPickableMorphItemsCallbackState, newMorphItems);
|
||||
|
||||
multipleRecordPickerPerformSearch({
|
||||
multipleRecordPickerInstanceId: recordPickerInstanceId,
|
||||
multipleRecordPickerInstanceId: instanceId,
|
||||
forceSearchFilter: searchInput,
|
||||
forceSearchableObjectMetadataItems: [relationObjectMetadataItem],
|
||||
forcePickableMorphItems: newMorphItems,
|
||||
@ -132,7 +132,7 @@ export const RelationFromManyFieldInput = ({
|
||||
[
|
||||
createNewRecordAndOpenRightDrawer,
|
||||
relationObjectMetadataItem,
|
||||
recordPickerInstanceId,
|
||||
instanceId,
|
||||
multipleRecordPickerPickableMorphItemsCallbackState,
|
||||
multipleRecordPickerPerformSearch,
|
||||
],
|
||||
@ -142,15 +142,15 @@ export const RelationFromManyFieldInput = ({
|
||||
|
||||
return (
|
||||
<MultipleRecordPicker
|
||||
focusId={recordPickerInstanceId}
|
||||
componentInstanceId={recordPickerInstanceId}
|
||||
focusId={instanceId}
|
||||
componentInstanceId={instanceId}
|
||||
onSubmit={handleSubmit}
|
||||
onChange={(morphItem) => {
|
||||
if (isRelationFromActivityTargets) {
|
||||
updateActivityTargetFromCell({
|
||||
morphItem,
|
||||
activityTargetWithTargetRecords: activityTargetObjectRecords,
|
||||
recordPickerInstanceId,
|
||||
recordPickerInstanceId: instanceId,
|
||||
});
|
||||
} else {
|
||||
updateRelation(morphItem);
|
||||
|
||||
@ -3,14 +3,15 @@ import { useRelationField } from '../../hooks/useRelationField';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/record-field/meta-types/input/hooks/useAddNewRecordAndOpenRightDrawer';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
||||
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { SingleRecordPicker } from '@/object-record/record-picker/single-record-picker/components/SingleRecordPicker';
|
||||
import { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
|
||||
import { SingleRecordPickerRecord } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerRecord';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -29,10 +30,9 @@ export const RelationToOneFieldInput = ({
|
||||
|
||||
const persistField = usePersistField();
|
||||
|
||||
const recordPickerInstanceId = getFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
});
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const handleRecordSelected = (
|
||||
selectedRecord: SingleRecordPickerRecord | null | undefined,
|
||||
@ -67,7 +67,7 @@ export const RelationToOneFieldInput = ({
|
||||
|
||||
const setSingleRecordPickerSelectedId = useSetRecoilComponentStateV2(
|
||||
singleRecordPickerSelectedIdComponentState,
|
||||
recordPickerInstanceId,
|
||||
instanceId,
|
||||
);
|
||||
|
||||
const handleCreateNew = async (searchInput?: string) => {
|
||||
@ -84,8 +84,8 @@ export const RelationToOneFieldInput = ({
|
||||
|
||||
return (
|
||||
<SingleRecordPicker
|
||||
focusId={recordPickerInstanceId}
|
||||
componentInstanceId={recordPickerInstanceId}
|
||||
focusId={instanceId}
|
||||
componentInstanceId={instanceId}
|
||||
EmptyIcon={IconForbid}
|
||||
emptyLabel={'No ' + fieldDefinition.label}
|
||||
onCancel={onCancel}
|
||||
@ -94,7 +94,7 @@ export const RelationToOneFieldInput = ({
|
||||
objectNameSingular={
|
||||
fieldDefinition.metadata.relationObjectMetadataNameSingular
|
||||
}
|
||||
recordPickerInstanceId={recordPickerInstanceId}
|
||||
recordPickerInstanceId={instanceId}
|
||||
layoutDirection={
|
||||
layoutDirection === 'downward'
|
||||
? 'search-bar-on-top'
|
||||
|
||||
@ -3,14 +3,16 @@ import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableE
|
||||
import { useRichTextCommandMenu } from '@/command-menu/hooks/useRichTextCommandMenu';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { lazy, Suspense, useRef } from 'react';
|
||||
import { Suspense, lazy, useRef } from 'react';
|
||||
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
|
||||
import { IconLayoutSidebarLeftCollapse } from 'twenty-ui/display';
|
||||
import { FloatingIconButton } from 'twenty-ui/input';
|
||||
@ -72,6 +74,9 @@ export const RichTextFieldInput = ({
|
||||
} & RichTextFieldInputProps) => {
|
||||
const { editRichText } = useRichTextCommandMenu();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||
onClickOutside?.(() => {}, event);
|
||||
@ -82,6 +87,7 @@ export const RichTextFieldInput = ({
|
||||
};
|
||||
|
||||
useRegisterInputEvents({
|
||||
focusId: instanceId,
|
||||
inputRef: containerRef,
|
||||
inputValue: null,
|
||||
onClickOutside: handleClickOutside,
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
|
||||
import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField';
|
||||
import { SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID } from '@/object-record/record-field/meta-types/input/constants/SelectFieldInputSelectableListComponentInstanceId';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { SelectInput } from '@/ui/field/input/components/SelectInput';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -21,8 +22,11 @@ export const SelectFieldInput = ({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: SelectFieldInputProps) => {
|
||||
const { persistField, fieldDefinition, fieldValue, recordId } =
|
||||
useSelectField();
|
||||
const { persistField, fieldDefinition, fieldValue } = useSelectField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const [filteredOptions, setFilteredOptions] = useState<SelectOption[]>([]);
|
||||
|
||||
@ -46,15 +50,16 @@ export const SelectFieldInput = ({
|
||||
resetSelectedItem();
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Escape],
|
||||
callback: () => {
|
||||
onCancel?.();
|
||||
resetSelectedItem();
|
||||
},
|
||||
DEFAULT_CELL_SCOPE.scope,
|
||||
[onCancel, resetSelectedItem],
|
||||
);
|
||||
scope: DEFAULT_CELL_SCOPE.scope,
|
||||
focusId: instanceId,
|
||||
dependencies: [onCancel, resetSelectedItem],
|
||||
});
|
||||
|
||||
const optionIds = [
|
||||
`No ${fieldDefinition.label}`,
|
||||
@ -67,10 +72,7 @@ export const SelectFieldInput = ({
|
||||
SELECT_FIELD_INPUT_SELECTABLE_LIST_COMPONENT_INSTANCE_ID
|
||||
}
|
||||
selectableItemIdArray={optionIds}
|
||||
focusId={getFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
})}
|
||||
focusId={instanceId}
|
||||
onEnter={(itemId) => {
|
||||
const option = filteredOptions.find(
|
||||
(option) => option.value === itemId,
|
||||
|
||||
@ -3,12 +3,14 @@ import { TextAreaInput } from '@/ui/field/input/components/TextAreaInput';
|
||||
import { usePersistField } from '../../../hooks/usePersistField';
|
||||
import { useTextField } from '../../hooks/useTextField';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
FieldInputClickOutsideEvent,
|
||||
FieldInputEvent,
|
||||
} from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { FieldInputContainer } from '@/ui/field/input/components/FieldInputContainer';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { turnIntoUndefinedIfWhitespacesOnly } from '~/utils/string/turnIntoUndefinedIfWhitespacesOnly';
|
||||
|
||||
export type TextFieldInputProps = {
|
||||
@ -28,6 +30,10 @@ export const TextFieldInput = ({
|
||||
}: TextFieldInputProps) => {
|
||||
const { fieldDefinition, draftValue, setDraftValue } = useTextField();
|
||||
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const persistField = usePersistField();
|
||||
const handleEnter = (newText: string) => {
|
||||
onEnter?.(() => persistField(newText.trim()));
|
||||
@ -59,6 +65,7 @@ export const TextFieldInput = ({
|
||||
return (
|
||||
<FieldInputContainer>
|
||||
<TextAreaInput
|
||||
instanceId={instanceId}
|
||||
placeholder={fieldDefinition.metadata.placeHolder}
|
||||
autoFocus
|
||||
value={draftValue ?? ''}
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useAddressField } from '@/object-record/record-field/meta-types/hooks/useAddressField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldAddressDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import {
|
||||
AddressInput,
|
||||
AddressInputProps,
|
||||
} from '@/ui/field/input/components/AddressInput';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useEffect } from 'react';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
const AddressValueSetterEffect = ({
|
||||
@ -43,21 +45,30 @@ const AddressInputWithContext = ({
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: AddressInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId: recordId ?? '',
|
||||
fieldName: 'Address',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotKeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [instanceId, pushFocusItemToFocusStack]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Address',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
@ -80,6 +91,7 @@ const AddressInputWithContext = ({
|
||||
>
|
||||
<AddressValueSetterEffect value={value} />
|
||||
<AddressInput
|
||||
instanceId={instanceId}
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
@ -116,7 +128,16 @@ const meta: Meta = {
|
||||
title: 'UI/Data/Field/Input/AddressFieldInput',
|
||||
component: AddressInputWithContext,
|
||||
args: {
|
||||
value: 'text',
|
||||
value: {
|
||||
addressStreet1: 'Address 1',
|
||||
addressStreet2: null,
|
||||
addressCity: null,
|
||||
addressState: null,
|
||||
addressPostcode: null,
|
||||
addressCountry: null,
|
||||
addressLat: null,
|
||||
addressLng: null,
|
||||
},
|
||||
onEnter: enterJestFn,
|
||||
onEscape: escapeJestfn,
|
||||
onClickOutside: clickOutsideJestFn,
|
||||
@ -148,7 +169,10 @@ export const Enter: Story = {
|
||||
|
||||
expect(enterJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
await canvas.findByText('Address 1');
|
||||
const addressInput = await canvas.findByDisplayValue('Address 1');
|
||||
|
||||
await userEvent.click(addressInput);
|
||||
|
||||
await userEvent.keyboard('{enter}');
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@ -6,9 +6,11 @@ import { useEffect } from 'react';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useArrayField } from '@/object-record/record-field/meta-types/hooks/useArrayField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { ArrayFieldInput } from '../ArrayFieldInput';
|
||||
|
||||
@ -55,20 +57,33 @@ const ArrayInputWithContext = ({
|
||||
onCancel,
|
||||
onClickOutside,
|
||||
}: ArrayInputWithContextProps) => {
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'tags',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotkeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
'tags',
|
||||
'record-table-cell',
|
||||
),
|
||||
fieldName: 'tags',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -8,7 +8,8 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import {
|
||||
BooleanFieldInput,
|
||||
BooleanFieldInputProps,
|
||||
@ -43,11 +44,11 @@ const BooleanFieldInputWithContext = ({
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Boolean',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId: recordId ?? '',
|
||||
fieldName: 'Boolean',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -2,13 +2,15 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
|
||||
import { useDateTimeField } from '../../../hooks/useDateTimeField';
|
||||
import {
|
||||
@ -52,7 +54,7 @@ const DateFieldValueGater = ({
|
||||
|
||||
type DateFieldInputWithContextProps = DateTimeFieldInputProps & {
|
||||
value: Date;
|
||||
recordId?: string;
|
||||
recordId: string;
|
||||
};
|
||||
|
||||
const DateFieldInputWithContext = ({
|
||||
@ -62,20 +64,28 @@ const DateFieldInputWithContext = ({
|
||||
onEnter,
|
||||
onClickOutside,
|
||||
}: DateFieldInputWithContextProps) => {
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'Date',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotkeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Date',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
@ -91,7 +101,7 @@ const DateFieldInputWithContext = ({
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
},
|
||||
recordId: '123',
|
||||
recordId,
|
||||
isLabelIdentifier: false,
|
||||
isReadOnly: false,
|
||||
}}
|
||||
|
||||
@ -8,9 +8,11 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
|
||||
import { useEmailsField } from '@/object-record/record-field/meta-types/hooks/useEmailsField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldEmailsValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { EmailsFieldInput } from '../EmailsFieldInput';
|
||||
|
||||
@ -57,20 +59,28 @@ const EmailInputWithContext = ({
|
||||
onCancel,
|
||||
onClickOutside,
|
||||
}: EmailInputWithContextProps) => {
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'emails',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotkeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
'emails',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -5,9 +5,11 @@ import { useEffect } from 'react';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useLinksField } from '@/object-record/record-field/meta-types/hooks/useLinksField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { getCanvasElementForDropdownTesting } from 'twenty-ui/testing';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { LinksFieldInput } from '../LinksFieldInput';
|
||||
@ -38,7 +40,7 @@ type LinksInputWithContextProps = {
|
||||
primaryLinkLabel: string | null;
|
||||
secondaryLinks: Array<{ url: string | null; label: string | null }> | null;
|
||||
};
|
||||
recordId?: string;
|
||||
recordId: string;
|
||||
onCancel?: () => void;
|
||||
onClickOutside?: (event: MouseEvent | TouchEvent) => void;
|
||||
};
|
||||
@ -67,21 +69,29 @@ const LinksInputWithContext = ({
|
||||
onCancel,
|
||||
onClickOutside,
|
||||
}: LinksInputWithContextProps) => {
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'Links',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotkeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Links',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
@ -97,7 +107,7 @@ const LinksInputWithContext = ({
|
||||
objectMetadataNameSingular: 'company',
|
||||
},
|
||||
},
|
||||
recordId: recordId ?? '123',
|
||||
recordId,
|
||||
isLabelIdentifier: false,
|
||||
isReadOnly: false,
|
||||
useUpdateRecord: () => [updateRecord, { loading: false }],
|
||||
@ -131,6 +141,7 @@ const meta: Meta = {
|
||||
primaryLinkLabel: null,
|
||||
secondaryLinks: null,
|
||||
},
|
||||
recordId: '123',
|
||||
onCancel: cancelJestFn,
|
||||
onClickOutside: clickOutsideJestFn,
|
||||
},
|
||||
|
||||
@ -2,14 +2,16 @@ import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { useNumberField } from '../../../hooks/useNumberField';
|
||||
@ -27,7 +29,7 @@ const NumberFieldValueSetterEffect = ({ value }: { value: number }) => {
|
||||
|
||||
type NumberFieldInputWithContextProps = NumberFieldInputProps & {
|
||||
value: number;
|
||||
recordId?: string;
|
||||
recordId: string;
|
||||
};
|
||||
|
||||
const NumberFieldInputWithContext = ({
|
||||
@ -39,25 +41,38 @@ const NumberFieldInputWithContext = ({
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: NumberFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'Number',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady) {
|
||||
setHotKeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
setIsReady(true);
|
||||
}
|
||||
}, [isReady, setHotKeyScope]);
|
||||
}, [isReady, pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Number',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'Number',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -9,9 +9,11 @@ import { usePhonesField } from '@/object-record/record-field/meta-types/hooks/us
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { PhonesFieldInput } from '../PhonesFieldInput';
|
||||
|
||||
@ -58,20 +60,28 @@ const PhoneInputWithContext = ({
|
||||
onCancel,
|
||||
onClickOutside,
|
||||
}: PhoneInputWithContextProps) => {
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'phones',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotkeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
'phones',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -2,12 +2,14 @@ import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { FieldRatingValue } from '../../../../types/FieldMetadata';
|
||||
import { useRatingField } from '../../../hooks/useRatingField';
|
||||
@ -29,7 +31,7 @@ const RatingFieldValueSetterEffect = ({
|
||||
|
||||
type RatingFieldInputWithContextProps = RatingFieldInputProps & {
|
||||
value: FieldRatingValue;
|
||||
recordId?: string;
|
||||
recordId: string;
|
||||
};
|
||||
|
||||
const RatingFieldInputWithContext = ({
|
||||
@ -37,20 +39,29 @@ const RatingFieldInputWithContext = ({
|
||||
value,
|
||||
onSubmit,
|
||||
}: RatingFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'Rating',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotKeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Rating',
|
||||
'record-table-cell',
|
||||
),
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -5,7 +5,7 @@ import { useSetRecoilState } from 'recoil';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { RelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
@ -19,9 +19,9 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { RelationType } from '~/generated-metadata/graphql';
|
||||
|
||||
@ -40,7 +40,7 @@ const RelationWorkspaceSetterEffect = () => {
|
||||
};
|
||||
|
||||
const RelationManyFieldInputWithContext = () => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const fieldDefinition = useMemo(
|
||||
() => ({
|
||||
@ -72,7 +72,14 @@ const RelationManyFieldInputWithContext = () => {
|
||||
useEffect(() => {
|
||||
setRecordStoreFieldValue([]);
|
||||
|
||||
setHotKeyScope(DropdownHotkeyScope.Dropdown);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: 'relation-from-many-field-input',
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: 'relation-from-many-field-input',
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
openFieldInput({
|
||||
fieldDefinition,
|
||||
recordId: 'recordId',
|
||||
@ -80,7 +87,7 @@ const RelationManyFieldInputWithContext = () => {
|
||||
}, [
|
||||
fieldDefinition,
|
||||
openFieldInput,
|
||||
setHotKeyScope,
|
||||
pushFocusItemToFocusStack,
|
||||
setRecordStoreFieldValue,
|
||||
]);
|
||||
|
||||
@ -88,10 +95,7 @@ const RelationManyFieldInputWithContext = () => {
|
||||
<div>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getFieldInputInstanceId({
|
||||
recordId: 'recordId',
|
||||
fieldName: 'people',
|
||||
}),
|
||||
instanceId: 'relation-from-many-field-input',
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -18,8 +18,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
@ -68,24 +67,13 @@ const RelationToOneFieldInputWithContext = ({
|
||||
|
||||
useEffect(() => {
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: getFieldInputInstanceId({
|
||||
recordId: '123',
|
||||
fieldName: 'Relation',
|
||||
}),
|
||||
focusId: 'relation-to-one-field-input',
|
||||
component: {
|
||||
type: FocusComponentType.DROPDOWN,
|
||||
instanceId: getFieldInputInstanceId({
|
||||
recordId: '123',
|
||||
fieldName: 'Relation',
|
||||
}),
|
||||
instanceId: 'relation-to-one-field-input',
|
||||
},
|
||||
hotkeyScope: {
|
||||
scope: DropdownHotkeyScope.Dropdown,
|
||||
},
|
||||
memoizeKey: getFieldInputInstanceId({
|
||||
recordId: '123',
|
||||
fieldName: 'Relation',
|
||||
}),
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
memoizeKey: 'relation-to-one-field-input',
|
||||
});
|
||||
}, [pushFocusItemToFocusStack]);
|
||||
|
||||
|
||||
@ -4,8 +4,11 @@ import { useEffect } from 'react';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
@ -35,16 +38,29 @@ const RichTextFieldInputWithContext = ({
|
||||
onClickOutside,
|
||||
onEscape,
|
||||
}: RichTextFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId: targetableObjectId,
|
||||
fieldName: 'richText',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHotKeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
}, [setHotKeyScope]);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
}, [pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'record-field-component-instance-id',
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { useTextField } from '../../../hooks/useTextField';
|
||||
import { TextFieldInput, TextFieldInputProps } from '../TextFieldInput';
|
||||
|
||||
const TextFieldValueSetterEffect = ({ value }: { value: string }) => {
|
||||
const { setFieldValue } = useTextField();
|
||||
|
||||
@ -25,7 +27,7 @@ const TextFieldValueSetterEffect = ({ value }: { value: string }) => {
|
||||
|
||||
type TextFieldInputWithContextProps = TextFieldInputProps & {
|
||||
value: string;
|
||||
recordId?: string;
|
||||
recordId: string;
|
||||
};
|
||||
|
||||
const TextFieldInputWithContext = ({
|
||||
@ -37,26 +39,39 @@ const TextFieldInputWithContext = ({
|
||||
onTab,
|
||||
onShiftTab,
|
||||
}: TextFieldInputWithContextProps) => {
|
||||
const setHotKeyScope = useSetHotkeyScope();
|
||||
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: 'Text',
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady) {
|
||||
setHotKeyScope(DEFAULT_CELL_SCOPE.scope);
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: instanceId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: instanceId,
|
||||
},
|
||||
hotkeyScope: DEFAULT_CELL_SCOPE,
|
||||
});
|
||||
|
||||
setIsReady(true);
|
||||
}
|
||||
}, [isReady, setHotKeyScope]);
|
||||
}, [isReady, pushFocusItemToFocusStack, instanceId]);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'record-field-component-instance-id',
|
||||
instanceId: instanceId,
|
||||
}}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: recordId ?? '123',
|
||||
recordId,
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: 'text',
|
||||
label: 'Text',
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
FieldRelationFromManyValue,
|
||||
FieldRelationValue,
|
||||
} from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { useMultipleRecordPickerOpen } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerOpen';
|
||||
import { useMultipleRecordPickerPerformSearch } from '@/object-record/record-picker/multiple-record-picker/hooks/useMultipleRecordPickerPerformSearch';
|
||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||
@ -11,6 +10,7 @@ import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from
|
||||
import { RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
@ -28,14 +28,17 @@ export const useOpenRelationFromManyFieldInput = () => {
|
||||
fieldName,
|
||||
objectNameSingular,
|
||||
recordId,
|
||||
prefix,
|
||||
}: {
|
||||
fieldName: string;
|
||||
objectNameSingular: string;
|
||||
recordId: string;
|
||||
prefix?: string;
|
||||
}) => {
|
||||
const recordPickerInstanceId = getFieldInputInstanceId({
|
||||
const recordPickerInstanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName,
|
||||
prefix,
|
||||
});
|
||||
|
||||
const fieldValue = snapshot
|
||||
|
||||
@ -2,10 +2,10 @@ import {
|
||||
FieldRelationToOneValue,
|
||||
FieldRelationValue,
|
||||
} from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { getFieldInputInstanceId } from '@/object-record/record-field/utils/getFieldInputInstanceId';
|
||||
import { useSingleRecordPickerOpen } from '@/object-record/record-picker/single-record-picker/hooks/useSingleRecordPickerOpen';
|
||||
import { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
@ -18,10 +18,19 @@ export const useOpenRelationToOneFieldInput = () => {
|
||||
|
||||
const openRelationToOneFieldInput = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
({ fieldName, recordId }: { fieldName: string; recordId: string }) => {
|
||||
const recordPickerInstanceId = getFieldInputInstanceId({
|
||||
({
|
||||
fieldName,
|
||||
recordId,
|
||||
prefix,
|
||||
}: {
|
||||
fieldName: string;
|
||||
recordId: string;
|
||||
prefix?: string;
|
||||
}) => {
|
||||
const recordPickerInstanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName,
|
||||
prefix,
|
||||
});
|
||||
const fieldValue = snapshot
|
||||
.getLoadable<FieldRelationValue<FieldRelationToOneValue>>(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@ -13,6 +13,7 @@ export const useRegisterInputEvents = <T>({
|
||||
onTab,
|
||||
onShiftTab,
|
||||
onClickOutside,
|
||||
focusId,
|
||||
hotkeyScope,
|
||||
}: {
|
||||
inputRef: React.RefObject<any>;
|
||||
@ -23,6 +24,7 @@ export const useRegisterInputEvents = <T>({
|
||||
onTab?: (inputValue: T) => void;
|
||||
onShiftTab?: (inputValue: T) => void;
|
||||
onClickOutside?: (event: MouseEvent | TouchEvent, inputValue: T) => void;
|
||||
focusId: string;
|
||||
hotkeyScope: string;
|
||||
}) => {
|
||||
useListenClickOutside({
|
||||
@ -34,39 +36,43 @@ export const useRegisterInputEvents = <T>({
|
||||
listenerId: hotkeyScope,
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'enter',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Enter],
|
||||
callback: () => {
|
||||
onEnter?.(inputValue);
|
||||
},
|
||||
hotkeyScope,
|
||||
[onEnter, inputValue],
|
||||
);
|
||||
focusId,
|
||||
scope: hotkeyScope,
|
||||
dependencies: [onEnter, inputValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Escape],
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Escape],
|
||||
callback: () => {
|
||||
onEscape?.(inputValue);
|
||||
},
|
||||
hotkeyScope,
|
||||
[onEscape, inputValue],
|
||||
);
|
||||
focusId,
|
||||
scope: hotkeyScope,
|
||||
dependencies: [onEscape, inputValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'tab',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Tab],
|
||||
callback: () => {
|
||||
onTab?.(inputValue);
|
||||
},
|
||||
hotkeyScope,
|
||||
[onTab, inputValue],
|
||||
);
|
||||
focusId,
|
||||
scope: hotkeyScope,
|
||||
dependencies: [onTab, inputValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'shift+tab',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [`${Key.Shift}+${Key.Tab}`],
|
||||
callback: () => {
|
||||
onShiftTab?.(inputValue);
|
||||
},
|
||||
hotkeyScope,
|
||||
[onShiftTab, inputValue],
|
||||
);
|
||||
focusId,
|
||||
scope: hotkeyScope,
|
||||
dependencies: [onShiftTab, inputValue],
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
export const getFieldInputInstanceId = ({
|
||||
recordId,
|
||||
fieldName,
|
||||
}: {
|
||||
recordId: string;
|
||||
fieldName: string;
|
||||
}) => {
|
||||
return `${recordId}-${fieldName}`;
|
||||
};
|
||||
@ -28,11 +28,14 @@ import {
|
||||
} from './RecordInlineCellContext';
|
||||
|
||||
type RecordInlineCellProps = {
|
||||
readonly?: boolean;
|
||||
loading?: boolean;
|
||||
instanceIdPrefix?: string;
|
||||
};
|
||||
|
||||
export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
|
||||
export const RecordInlineCell = ({
|
||||
loading,
|
||||
instanceIdPrefix,
|
||||
}: RecordInlineCellProps) => {
|
||||
const {
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
@ -47,13 +50,28 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
|
||||
|
||||
const onOpenEditMode = onOpenEditModeFromContext
|
||||
? onOpenEditModeFromContext
|
||||
: () => openFieldInput({ fieldDefinition, recordId });
|
||||
: () =>
|
||||
openFieldInput({
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
prefix: instanceIdPrefix,
|
||||
});
|
||||
|
||||
const onCloseEditMode = useCallback(() => {
|
||||
onCloseEditModeFromContext
|
||||
? onCloseEditModeFromContext()
|
||||
: closeFieldInput({ fieldDefinition, recordId });
|
||||
}, [onCloseEditModeFromContext, closeFieldInput, fieldDefinition, recordId]);
|
||||
: closeFieldInput({
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
prefix: instanceIdPrefix,
|
||||
});
|
||||
}, [
|
||||
onCloseEditModeFromContext,
|
||||
closeFieldInput,
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
instanceIdPrefix,
|
||||
]);
|
||||
|
||||
const buttonIcon = useGetButtonIcon();
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import { useContext } from 'react';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useFieldFocus } from '@/object-record/record-field/hooks/useFieldFocus';
|
||||
import { RecordInlineCellValue } from '@/object-record/record-inline-cell/components/RecordInlineCellValue';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
|
||||
import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata';
|
||||
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
|
||||
@ -123,10 +123,10 @@ export const RecordInlineCellContainer = () => {
|
||||
};
|
||||
|
||||
const theme = useTheme();
|
||||
const labelId = `label-${getRecordFieldInputId(
|
||||
const labelId = `label-${getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
)}`;
|
||||
fieldName: fieldDefinition?.metadata?.fieldName,
|
||||
})}`;
|
||||
|
||||
return (
|
||||
<StyledInlineCellBaseContainer
|
||||
|
||||
@ -14,7 +14,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
||||
import { DropdownHotkeyScope } from '@/ui/layout/dropdown/constants/DropdownHotkeyScope';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { useRef } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
@ -41,8 +40,6 @@ export const MultipleRecordPicker = ({
|
||||
componentInstanceId,
|
||||
focusId,
|
||||
}: MultipleRecordPickerProps) => {
|
||||
const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope();
|
||||
|
||||
const selectableListComponentInstanceId =
|
||||
getMultipleRecordPickerSelectableListId(componentInstanceId);
|
||||
|
||||
@ -79,7 +76,6 @@ export const MultipleRecordPicker = ({
|
||||
|
||||
const handleSubmit = () => {
|
||||
onSubmit?.();
|
||||
goBackToPreviousHotkeyScope();
|
||||
resetSelectedItem();
|
||||
resetState();
|
||||
};
|
||||
|
||||
@ -18,7 +18,7 @@ import { useRecordShowContainerActions } from '@/object-record/record-show/hooks
|
||||
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
|
||||
import { RecordDetailDuplicatesSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection';
|
||||
import { RecordDetailRelationSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailRelationSection';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
|
||||
import { useIsInRightDrawerOrThrow } from '@/ui/layout/right-drawer/contexts/RightDrawerContext';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -28,6 +28,8 @@ type FieldsCardProps = {
|
||||
objectRecordId: string;
|
||||
};
|
||||
|
||||
const INPUT_ID_PREFIX = 'fields-card';
|
||||
|
||||
export const FieldsCard = ({
|
||||
objectNameSingular,
|
||||
objectRecordId,
|
||||
@ -139,13 +141,13 @@ export const FieldsCard = ({
|
||||
}}
|
||||
>
|
||||
<ActivityTargetsInlineCell
|
||||
componentInstanceId={getRecordFieldInputId(
|
||||
objectRecordId,
|
||||
fieldMetadataItem.name,
|
||||
isInRightDrawer
|
||||
componentInstanceId={getRecordFieldInputInstanceId({
|
||||
recordId: objectRecordId,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
prefix: isInRightDrawer
|
||||
? 'right-drawer-fields-card'
|
||||
: 'fields-card',
|
||||
)}
|
||||
})}
|
||||
activityObjectNameSingular={
|
||||
objectNameSingular as
|
||||
| CoreObjectNameSingular.Note
|
||||
@ -185,14 +187,17 @@ export const FieldsCard = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
objectRecordId,
|
||||
fieldMetadataItem.name,
|
||||
'fields-card',
|
||||
),
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId: objectRecordId,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
prefix: INPUT_ID_PREFIX,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell loading={recordLoading} />
|
||||
<RecordInlineCell
|
||||
loading={recordLoading}
|
||||
instanceIdPrefix={INPUT_ID_PREFIX}
|
||||
/>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
))}
|
||||
|
||||
@ -29,7 +29,7 @@ import { RecordDetailRecordsListItem } from '@/object-record/record-show/record-
|
||||
import { getRecordFieldCardRelationPickerDropdownId } from '@/object-record/record-show/utils/getRecordFieldCardRelationPickerDropdownId';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
@ -320,14 +320,14 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
relationRecord.id,
|
||||
fieldMetadataItem.name,
|
||||
'record-detail',
|
||||
),
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId: relationRecord.id,
|
||||
fieldName: fieldMetadataItem.name,
|
||||
prefix: 'record-detail',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
<RecordInlineCell instanceIdPrefix="record-detail" />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
),
|
||||
|
||||
@ -0,0 +1 @@
|
||||
export const RECORD_TABLE_CELL_INPUT_ID_PREFIX = 'record-table-cell';
|
||||
@ -1,5 +1,6 @@
|
||||
import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { recordIndexOpenRecordInState } from '@/object-record/record-index/states/recordIndexOpenRecordInState';
|
||||
import { useBuildRecordInputFromFilters } from '@/object-record/record-table/hooks/useBuildRecordInputFromFilters';
|
||||
@ -10,6 +11,7 @@ import { canOpenObjectInSidePanel } from '@/object-record/utils/canOpenObjectInS
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
|
||||
@ -58,11 +60,16 @@ export const useCreateNewIndexRecord = ({
|
||||
isNewRecord: true,
|
||||
});
|
||||
|
||||
openRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
containerType: RecordTitleCellContainerType.ShowPage,
|
||||
});
|
||||
const labelIdentifierFieldMetadataItem =
|
||||
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
|
||||
|
||||
if (isDefined(labelIdentifierFieldMetadataItem)) {
|
||||
openRecordTitleCell({
|
||||
recordId,
|
||||
fieldName: labelIdentifierFieldMetadataItem.name,
|
||||
containerType: RecordTitleCellContainerType.ShowPage,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
@ -74,8 +81,7 @@ export const useCreateNewIndexRecord = ({
|
||||
buildRecordInputFromFilters,
|
||||
createOneRecord,
|
||||
navigate,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
objectMetadataItem.nameSingular,
|
||||
objectMetadataItem,
|
||||
openRecordInCommandMenu,
|
||||
openRecordTitleCell,
|
||||
],
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { RecordTableCellContext } from '@/object-record/record-table/contexts/RecordTableCellContext';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||
import { RecordTableCellFieldContextGeneric } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextGeneric';
|
||||
import { RecordTableCellFieldContextLabelIdentifier } from '@/object-record/record-table/record-table-cell/components/RecordTableCellFieldContextLabelIdentifier';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { ReactNode, useContext } from 'react';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
@ -24,11 +25,11 @@ export const RecordTableCellFieldContextWrapper = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const instanceId = getRecordFieldInputId(
|
||||
const instanceId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
columnDefinition.metadata.fieldName,
|
||||
'record-table-cell',
|
||||
);
|
||||
fieldName: columnDefinition.metadata.fieldName,
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
const isLabelIdentifier = isLabelIdentifierField({
|
||||
fieldMetadataItem: {
|
||||
|
||||
@ -7,6 +7,7 @@ import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldVal
|
||||
import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { FOCUS_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/FocusClickOutsideListenerId';
|
||||
import { RECORD_TABLE_CELL_INPUT_ID_PREFIX } from '@/object-record/record-table/constants/RecordTableCellInputIdPrefix';
|
||||
import { useLeaveTableFocus } from '@/object-record/record-table/hooks/internal/useLeaveTableFocus';
|
||||
import { TableCellPosition } from '@/object-record/record-table/types/TableCellPosition';
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
@ -20,7 +21,7 @@ import { viewableRecordNameSingularState } from '@/object-record/record-right-dr
|
||||
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
|
||||
import { recordTableCellEditModePositionComponentState } from '@/object-record/record-table/states/recordTableCellEditModePositionComponentState';
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
|
||||
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
|
||||
@ -166,6 +167,7 @@ export const useOpenRecordTableCellV2 = (recordTableId: string) => {
|
||||
openFieldInput({
|
||||
fieldDefinition,
|
||||
recordId,
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
});
|
||||
|
||||
setCurrentTableCellInEditModePosition(cellPosition);
|
||||
@ -174,11 +176,11 @@ export const useOpenRecordTableCellV2 = (recordTableId: string) => {
|
||||
value: initialValue,
|
||||
recordId,
|
||||
fieldDefinition,
|
||||
fieldComponentInstanceId: getRecordFieldInputId(
|
||||
fieldComponentInstanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldDefinition.metadata.fieldName,
|
||||
'record-table-cell',
|
||||
),
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: RECORD_TABLE_CELL_INPUT_ID_PREFIX,
|
||||
}),
|
||||
});
|
||||
|
||||
toggleClickOutside(false);
|
||||
|
||||
@ -18,7 +18,7 @@ import { RecordTitleCellFieldDisplay } from '@/object-record/record-title-cell/c
|
||||
import { RecordTitleCellFieldInput } from '@/object-record/record-title-cell/components/RecordTitleCellFieldInput';
|
||||
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
import { getRecordTitleCellId } from '@/object-record/record-title-cell/utils/getRecordTitleCellId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
|
||||
type RecordTitleCellProps = {
|
||||
loading?: boolean;
|
||||
@ -37,54 +37,46 @@ export const RecordTitleCell = ({
|
||||
|
||||
const { closeRecordTitleCell } = useRecordTitleCell();
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
const closeCell = () => {
|
||||
closeRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: fieldDefinition?.fieldMetadataId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
containerType,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
closeCell();
|
||||
persistField();
|
||||
};
|
||||
|
||||
const handleEscape: FieldInputEvent = (persistField) => {
|
||||
closeRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: fieldDefinition?.fieldMetadataId,
|
||||
containerType,
|
||||
});
|
||||
persistField();
|
||||
const handleEscape = () => {
|
||||
closeCell();
|
||||
};
|
||||
|
||||
const handleTab: FieldInputEvent = (persistField) => {
|
||||
closeRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: fieldDefinition?.fieldMetadataId,
|
||||
containerType,
|
||||
});
|
||||
closeCell();
|
||||
persistField();
|
||||
};
|
||||
|
||||
const handleShiftTab: FieldInputEvent = (persistField) => {
|
||||
closeRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: fieldDefinition?.fieldMetadataId,
|
||||
containerType,
|
||||
});
|
||||
closeCell();
|
||||
persistField();
|
||||
};
|
||||
|
||||
const handleClickOutside: FieldInputClickOutsideEvent = (persistField) => {
|
||||
closeRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: fieldDefinition?.fieldMetadataId,
|
||||
containerType,
|
||||
});
|
||||
closeCell();
|
||||
persistField();
|
||||
};
|
||||
|
||||
const recordTitleCellContextValue: RecordTitleCellContextProps = {
|
||||
editModeContent: (
|
||||
<RecordTitleCellFieldInput
|
||||
instanceId={getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: containerType,
|
||||
})}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
@ -93,7 +85,9 @@ export const RecordTitleCell = ({
|
||||
sizeVariant={sizeVariant}
|
||||
/>
|
||||
),
|
||||
displayModeContent: <RecordTitleCellFieldDisplay />,
|
||||
displayModeContent: (
|
||||
<RecordTitleCellFieldDisplay containerType={containerType} />
|
||||
),
|
||||
editModeContentOnly: isFieldInputOnly,
|
||||
loading: loading,
|
||||
isReadOnly,
|
||||
@ -103,11 +97,11 @@ export const RecordTitleCell = ({
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordTitleCellId(
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldDefinition?.fieldMetadataId,
|
||||
containerType,
|
||||
),
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<FieldFocusContextProvider>
|
||||
|
||||
@ -3,9 +3,14 @@ import { isFieldFullName } from '@/object-record/record-field/types/guards/isFie
|
||||
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
|
||||
import { RecordTitleCellSingleTextDisplayMode } from '@/object-record/record-title-cell/components/RecordTitleCellTextFieldDisplay';
|
||||
import { RecordTitleFullNameFieldDisplay } from '@/object-record/record-title-cell/components/RecordTitleFullNameFieldDisplay';
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const RecordTitleCellFieldDisplay = () => {
|
||||
export const RecordTitleCellFieldDisplay = ({
|
||||
containerType,
|
||||
}: {
|
||||
containerType: RecordTitleCellContainerType;
|
||||
}) => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
if (!isFieldText(fieldDefinition) && !isFieldFullName(fieldDefinition)) {
|
||||
@ -15,9 +20,9 @@ export const RecordTitleCellFieldDisplay = () => {
|
||||
return (
|
||||
<>
|
||||
{isFieldText(fieldDefinition) ? (
|
||||
<RecordTitleCellSingleTextDisplayMode />
|
||||
<RecordTitleCellSingleTextDisplayMode containerType={containerType} />
|
||||
) : isFieldFullName(fieldDefinition) ? (
|
||||
<RecordTitleFullNameFieldDisplay />
|
||||
<RecordTitleFullNameFieldDisplay containerType={containerType} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
@ -9,6 +9,7 @@ import { RecordTitleFullNameFieldInput } from '@/object-record/record-title-cell
|
||||
import { TitleInputHotkeyScope } from '@/ui/input/types/TitleInputHotkeyScope';
|
||||
|
||||
type RecordTitleCellFieldInputProps = {
|
||||
instanceId: string;
|
||||
onClickOutside?: (
|
||||
persist: () => void,
|
||||
event: MouseEvent | TouchEvent,
|
||||
@ -21,6 +22,7 @@ type RecordTitleCellFieldInputProps = {
|
||||
};
|
||||
|
||||
export const RecordTitleCellFieldInput = ({
|
||||
instanceId,
|
||||
sizeVariant,
|
||||
onEnter,
|
||||
onEscape,
|
||||
@ -38,6 +40,7 @@ export const RecordTitleCellFieldInput = ({
|
||||
<>
|
||||
{isFieldText(fieldDefinition) ? (
|
||||
<RecordTitleCellTextFieldInput
|
||||
instanceId={instanceId}
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { RecordTitleCellContext } from '@/object-record/record-title-cell/components/RecordTitleCellContext';
|
||||
import { useRecordTitleCell } from '@/object-record/record-title-cell/hooks/useRecordTitleCell';
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
import { Theme, withTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useContext } from 'react';
|
||||
@ -30,7 +30,11 @@ const StyledEmptyText = withTheme(styled.div<{ theme: Theme }>`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`);
|
||||
|
||||
export const RecordTitleCellSingleTextDisplayMode = () => {
|
||||
export const RecordTitleCellSingleTextDisplayMode = ({
|
||||
containerType,
|
||||
}: {
|
||||
containerType: RecordTitleCellContainerType;
|
||||
}) => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const recordValue = useRecoilValue(recordStoreFamilyState(recordId));
|
||||
@ -40,14 +44,12 @@ export const RecordTitleCellSingleTextDisplayMode = () => {
|
||||
|
||||
const { openRecordTitleCell } = useRecordTitleCell();
|
||||
|
||||
const { containerType } = useContext(RecordTitleCellContext);
|
||||
|
||||
return (
|
||||
<StyledDiv
|
||||
onClick={() => {
|
||||
openRecordTitleCell({
|
||||
recordId,
|
||||
fieldMetadataId: fieldDefinition.fieldMetadataId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
containerType,
|
||||
});
|
||||
}}
|
||||
|
||||
@ -11,6 +11,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { turnIntoUndefinedIfWhitespacesOnly } from '~/utils/string/turnIntoUndefinedIfWhitespacesOnly';
|
||||
|
||||
type RecordTitleCellTextFieldInputProps = {
|
||||
instanceId: string;
|
||||
onClickOutside?: FieldInputClickOutsideEvent;
|
||||
onEnter?: FieldInputEvent;
|
||||
onEscape?: FieldInputEvent;
|
||||
@ -21,6 +22,7 @@ type RecordTitleCellTextFieldInputProps = {
|
||||
};
|
||||
|
||||
export const RecordTitleCellTextFieldInput = ({
|
||||
instanceId,
|
||||
sizeVariant,
|
||||
onEnter,
|
||||
onEscape,
|
||||
@ -40,6 +42,7 @@ export const RecordTitleCellTextFieldInput = ({
|
||||
const persistField = usePersistField();
|
||||
|
||||
useRegisterInputEvents<string>({
|
||||
focusId: instanceId,
|
||||
inputRef: wrapperRef,
|
||||
inputValue: draftValue ?? '',
|
||||
onEnter: (inputValue) => {
|
||||
|
||||
@ -2,13 +2,15 @@ import styled from '@emotion/styled';
|
||||
import { ClipboardEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldDoubleText } from '@/object-record/record-field/types/FieldDoubleText';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { splitFullName } from '~/utils/format/spiltFullName';
|
||||
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@ -82,33 +84,39 @@ export const RecordTitleDoubleTextInput = ({
|
||||
|
||||
const [focusPosition, setFocusPosition] = useState<'left' | 'right'>('left');
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
const instanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Enter],
|
||||
callback: () => {
|
||||
onEnter({
|
||||
firstValue: firstInternalValue,
|
||||
secondValue: secondInternalValue,
|
||||
});
|
||||
},
|
||||
hotkeyScope,
|
||||
[onEnter, firstInternalValue, secondInternalValue],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [onEnter, firstInternalValue, secondInternalValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Escape],
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: [Key.Escape],
|
||||
callback: () => {
|
||||
onEscape({
|
||||
firstValue: firstInternalValue,
|
||||
secondValue: secondInternalValue,
|
||||
});
|
||||
},
|
||||
hotkeyScope,
|
||||
[onEscape, firstInternalValue, secondInternalValue],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [onEscape, firstInternalValue, secondInternalValue],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'tab',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: ['tab'],
|
||||
callback: () => {
|
||||
if (focusPosition === 'left') {
|
||||
setFocusPosition('right');
|
||||
secondValueInputRef.current?.focus();
|
||||
@ -119,13 +127,19 @@ export const RecordTitleDoubleTextInput = ({
|
||||
});
|
||||
}
|
||||
},
|
||||
hotkeyScope,
|
||||
[onTab, firstInternalValue, secondInternalValue, focusPosition],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [
|
||||
onTab,
|
||||
firstInternalValue,
|
||||
secondInternalValue,
|
||||
focusPosition,
|
||||
],
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
'shift+tab',
|
||||
() => {
|
||||
useHotkeysOnFocusedElement({
|
||||
keys: ['shift+tab'],
|
||||
callback: () => {
|
||||
if (focusPosition === 'right') {
|
||||
setFocusPosition('left');
|
||||
firstValueInputRef.current?.focus();
|
||||
@ -136,9 +150,15 @@ export const RecordTitleDoubleTextInput = ({
|
||||
});
|
||||
}
|
||||
},
|
||||
hotkeyScope,
|
||||
[onShiftTab, firstInternalValue, secondInternalValue, focusPosition],
|
||||
);
|
||||
scope: hotkeyScope,
|
||||
focusId: instanceId,
|
||||
dependencies: [
|
||||
onShiftTab,
|
||||
firstInternalValue,
|
||||
secondInternalValue,
|
||||
focusPosition,
|
||||
],
|
||||
});
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [containerRef],
|
||||
|
||||
@ -2,8 +2,10 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
|
||||
import { useFullNameFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useFullNameFieldDisplay';
|
||||
import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey';
|
||||
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { TitleInputHotkeyScope } from '@/ui/input/types/TitleInputHotkeyScope';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
|
||||
import { Theme, withTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
@ -33,8 +35,12 @@ const StyledEmptyText = withTheme(styled.div<{ theme: Theme }>`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`);
|
||||
|
||||
export const RecordTitleFullNameFieldDisplay = () => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
export const RecordTitleFullNameFieldDisplay = ({
|
||||
containerType,
|
||||
}: {
|
||||
containerType: string;
|
||||
}) => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const { openInlineCell } = useInlineCell();
|
||||
|
||||
@ -45,14 +51,29 @@ export const RecordTitleFullNameFieldDisplay = () => {
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
const { setHotkeyScopeAndMemorizePreviousScope } = usePreviousHotkeyScope();
|
||||
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
|
||||
|
||||
const recordTitleCellId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName: fieldDefinition.metadata.fieldName,
|
||||
prefix: containerType,
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledDiv
|
||||
onClick={() => {
|
||||
setHotkeyScopeAndMemorizePreviousScope({
|
||||
scope: TitleInputHotkeyScope.TitleInput,
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: recordTitleCellId,
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: recordTitleCellId,
|
||||
},
|
||||
hotkeyScope: {
|
||||
scope: TitleInputHotkeyScope.TitleInput,
|
||||
},
|
||||
memoizeKey: INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY,
|
||||
});
|
||||
|
||||
openInlineCell();
|
||||
}}
|
||||
>
|
||||
|
||||
@ -3,7 +3,7 @@ import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/s
|
||||
import { INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY } from '@/object-record/record-inline-cell/constants/InlineCellHotkeyScopeMemoizeKey';
|
||||
import { isInlineCellInEditModeScopedState } from '@/object-record/record-inline-cell/states/isInlineCellInEditModeScopedState';
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
import { getRecordTitleCellId } from '@/object-record/record-title-cell/utils/getRecordTitleCellId';
|
||||
import { getRecordFieldInputInstanceId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { TitleInputHotkeyScope } from '@/ui/input/types/TitleInputHotkeyScope';
|
||||
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
|
||||
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
|
||||
@ -25,26 +25,30 @@ export const useRecordTitleCell = () => {
|
||||
({ set }) =>
|
||||
({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
fieldName,
|
||||
containerType,
|
||||
}: {
|
||||
recordId: string;
|
||||
fieldMetadataId: string;
|
||||
fieldName: string;
|
||||
containerType: RecordTitleCellContainerType;
|
||||
}) => {
|
||||
set(
|
||||
isInlineCellInEditModeScopedState(
|
||||
getRecordTitleCellId(recordId, fieldMetadataId, containerType),
|
||||
getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
),
|
||||
false,
|
||||
);
|
||||
|
||||
removeFocusItemFromFocusStackById({
|
||||
focusId: getRecordTitleCellId(
|
||||
focusId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
containerType,
|
||||
),
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
});
|
||||
|
||||
goBackToPreviousDropdownFocusId();
|
||||
@ -58,47 +62,47 @@ export const useRecordTitleCell = () => {
|
||||
({ set, snapshot }) =>
|
||||
({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
fieldName,
|
||||
containerType,
|
||||
customEditHotkeyScopeForField,
|
||||
}: {
|
||||
recordId: string;
|
||||
fieldMetadataId: string;
|
||||
fieldName: string;
|
||||
containerType: RecordTitleCellContainerType;
|
||||
customEditHotkeyScopeForField?: HotkeyScope;
|
||||
}) => {
|
||||
if (isDefined(customEditHotkeyScopeForField)) {
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: getRecordTitleCellId(
|
||||
focusId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
containerType,
|
||||
),
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: getRecordTitleCellId(
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
containerType,
|
||||
),
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
},
|
||||
hotkeyScope: customEditHotkeyScopeForField,
|
||||
memoizeKey: INLINE_CELL_HOTKEY_SCOPE_MEMOIZE_KEY,
|
||||
});
|
||||
} else {
|
||||
pushFocusItemToFocusStack({
|
||||
focusId: getRecordTitleCellId(
|
||||
focusId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
containerType,
|
||||
),
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
component: {
|
||||
type: FocusComponentType.OPENED_FIELD_INPUT,
|
||||
instanceId: getRecordTitleCellId(
|
||||
instanceId: getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
containerType,
|
||||
),
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
}),
|
||||
},
|
||||
hotkeyScope: {
|
||||
scope: TitleInputHotkeyScope.TitleInput,
|
||||
@ -107,11 +111,11 @@ export const useRecordTitleCell = () => {
|
||||
});
|
||||
}
|
||||
|
||||
const recordTitleCellId = getRecordTitleCellId(
|
||||
const recordTitleCellId = getRecordFieldInputInstanceId({
|
||||
recordId,
|
||||
fieldMetadataId,
|
||||
containerType,
|
||||
);
|
||||
fieldName,
|
||||
prefix: containerType,
|
||||
});
|
||||
set(isInlineCellInEditModeScopedState(recordTitleCellId), true);
|
||||
|
||||
const recordIndexFieldDefinitions = snapshot
|
||||
@ -119,7 +123,7 @@ export const useRecordTitleCell = () => {
|
||||
.getValue();
|
||||
|
||||
const fieldDefinition = recordIndexFieldDefinitions.find(
|
||||
(field) => field.fieldMetadataId === fieldMetadataId,
|
||||
(field) => field.metadata.fieldName === fieldName,
|
||||
);
|
||||
|
||||
if (!fieldDefinition) {
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/types/RecordTitleCellContainerType';
|
||||
|
||||
export const getRecordTitleCellId = (
|
||||
recordId: string,
|
||||
fieldMetadataId: string,
|
||||
containerType: RecordTitleCellContainerType,
|
||||
) => {
|
||||
return `${recordId}-${fieldMetadataId}-${containerType}`;
|
||||
};
|
||||
@ -1,9 +1,14 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
export const getRecordFieldInputId = (
|
||||
recordId: string,
|
||||
fieldName?: string,
|
||||
prefix?: string,
|
||||
): string => {
|
||||
|
||||
export const getRecordFieldInputInstanceId = ({
|
||||
recordId,
|
||||
fieldName,
|
||||
prefix,
|
||||
}: {
|
||||
recordId: string;
|
||||
fieldName?: string;
|
||||
prefix?: string;
|
||||
}): string => {
|
||||
if (isDefined(prefix)) {
|
||||
return `${prefix}-${recordId}-${fieldName}`;
|
||||
}
|
||||
|
||||
@ -95,6 +95,7 @@ export const SettingsAccountsBlocklistInput = ({
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<TextInput
|
||||
instanceId="settings-accounts-blocklist-input"
|
||||
placeholder="eddy@gmail.com, @apple.com"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
|
||||
@ -44,6 +44,7 @@ export const SetttingsAccountsImapConnectionForm = ({
|
||||
defaultValue={defaultValues?.handle}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextInput
|
||||
instanceId="email-address-imap-connection-form"
|
||||
label={t`Email Address`}
|
||||
placeholder={t`john.doe@example.com`}
|
||||
value={field.value}
|
||||
@ -58,6 +59,7 @@ export const SetttingsAccountsImapConnectionForm = ({
|
||||
defaultValue={defaultValues?.host}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextInput
|
||||
instanceId="host-imap-connection-form"
|
||||
label={t`IMAP Server`}
|
||||
placeholder={t`imap.example.com`}
|
||||
value={field.value}
|
||||
@ -72,6 +74,7 @@ export const SetttingsAccountsImapConnectionForm = ({
|
||||
defaultValue={defaultValues?.port ?? 993}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextInput
|
||||
instanceId="port-imap-connection-form"
|
||||
label={t`IMAP Port`}
|
||||
type="number"
|
||||
placeholder={t`993`}
|
||||
@ -104,6 +107,7 @@ export const SetttingsAccountsImapConnectionForm = ({
|
||||
defaultValue={defaultValues?.password}
|
||||
render={({ field, fieldState }) => (
|
||||
<TextInput
|
||||
instanceId="password-imap-connection-form"
|
||||
label={t`Password`}
|
||||
placeholder={t`••••••••`}
|
||||
type="password"
|
||||
|
||||
@ -156,6 +156,7 @@ export const SettingsAdminGeneral = () => {
|
||||
|
||||
<StyledContainer>
|
||||
<TextInput
|
||||
instanceId="admin-user-lookup"
|
||||
value={userIdentifier}
|
||||
onChange={setUserIdentifier}
|
||||
onInputEnter={handleSearch}
|
||||
|
||||
@ -60,6 +60,8 @@ export const ConfigVariableDatabaseInput = ({
|
||||
onChange(newValues);
|
||||
};
|
||||
|
||||
const jsonArrayTextAreaId = `${label}-json-array`;
|
||||
|
||||
switch (type) {
|
||||
case ConfigVariableType.BOOLEAN:
|
||||
return (
|
||||
@ -134,6 +136,7 @@ export const ConfigVariableDatabaseInput = ({
|
||||
/>
|
||||
) : (
|
||||
<TextArea
|
||||
textAreaId={jsonArrayTextAreaId}
|
||||
label={label}
|
||||
value={
|
||||
Array.isArray(value)
|
||||
|
||||
@ -18,6 +18,7 @@ export const ConfigVariableSearchInput = ({
|
||||
}: ConfigVariableSearchInputProps) => {
|
||||
return (
|
||||
<StyledSearchInput
|
||||
instanceId="config-variable-search"
|
||||
placeholder={t`Search config variables`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import styled from '@emotion/styled';
|
||||
import { castAsNumberOrNull } from '~/utils/cast-as-number-or-null';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { IconMinus, IconPlus } from 'twenty-ui/display';
|
||||
import { IconButton } from 'twenty-ui/input';
|
||||
import { castAsNumberOrNull } from '~/utils/cast-as-number-or-null';
|
||||
|
||||
type SettingsCounterProps = {
|
||||
value: number;
|
||||
@ -77,6 +77,7 @@ export const SettingsCounter = ({
|
||||
disabled={disabled}
|
||||
/>
|
||||
<StyledTextInput
|
||||
instanceId="settings-counter-input"
|
||||
name="counter"
|
||||
fullWidth
|
||||
value={value.toString()}
|
||||
|
||||
@ -29,6 +29,8 @@ export const SettingsDataModelFieldDescriptionForm = ({
|
||||
const { control } =
|
||||
useFormContext<SettingsDataModelFieldDescriptionFormValues>();
|
||||
|
||||
const descriptionTextAreaId = `${fieldMetadataItem?.id}-description`;
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="description"
|
||||
@ -36,6 +38,7 @@ export const SettingsDataModelFieldDescriptionForm = ({
|
||||
defaultValue={fieldMetadataItem?.description}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextArea
|
||||
textAreaId={descriptionTextAreaId}
|
||||
placeholder={t`Write a description`}
|
||||
minRows={4}
|
||||
value={value ?? undefined}
|
||||
|
||||
@ -14,7 +14,6 @@ import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { computeMetadataNameFromLabel } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
|
||||
import {
|
||||
AppTooltip,
|
||||
IconInfoCircle,
|
||||
@ -22,6 +21,7 @@ import {
|
||||
TooltipDelay,
|
||||
} from 'twenty-ui/display';
|
||||
import { Card } from 'twenty-ui/layout';
|
||||
import { computeMetadataNameFromLabel } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils';
|
||||
|
||||
export const settingsDataModelFieldIconLabelFormSchema = (
|
||||
existingOtherLabels: string[] = [],
|
||||
@ -94,6 +94,9 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const labelTextInputId = `${fieldMetadataItem?.id}-label`;
|
||||
const nameTextInputId = `${fieldMetadataItem?.id}-name`;
|
||||
|
||||
const isLabelSyncedWithName =
|
||||
watch('isLabelSyncedWithName') ??
|
||||
(isDefined(fieldMetadataItem)
|
||||
@ -133,6 +136,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
||||
defaultValue={fieldMetadataItem?.label}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId={labelTextInputId}
|
||||
placeholder={t`Employees`}
|
||||
value={value}
|
||||
onChange={(value) => {
|
||||
@ -167,6 +171,7 @@ export const SettingsDataModelFieldIconLabelForm = ({
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<>
|
||||
<TextInput
|
||||
instanceId={nameTextInputId}
|
||||
label={t`API Name`}
|
||||
placeholder={t`employees`}
|
||||
value={value}
|
||||
|
||||
@ -108,6 +108,7 @@ export const SettingsObjectNewFieldSelector = ({
|
||||
{' '}
|
||||
<Section>
|
||||
<StyledSearchInput
|
||||
instanceId="new-field-type-search"
|
||||
LeftIcon={IconSearch}
|
||||
placeholder={t`Search a type`}
|
||||
value={searchQuery}
|
||||
|
||||
@ -117,6 +117,7 @@ export const SettingsDataModelFieldDateForm = ({
|
||||
defaultValue={initialCustomUnicodeDateFormat}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<StyledTextInput
|
||||
instanceId="custom-date-format-input"
|
||||
placeholder={t`Format e.g. d-MMM-y (qqq''yy)`}
|
||||
value={value}
|
||||
onChange={(value) => onChange(value)}
|
||||
|
||||
@ -187,6 +187,7 @@ export const SettingsDataModelFieldRelationForm = ({
|
||||
defaultValue={initialRelationFieldMetadataItem.label}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId="relation-field-label"
|
||||
disabled={disableFieldEdition}
|
||||
placeholder={t`Field name`}
|
||||
value={value}
|
||||
|
||||
@ -99,6 +99,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
/>
|
||||
<AdvancedSettingsWrapper animationDimension="width" hideDot>
|
||||
<StyledOptionInput
|
||||
instanceId={`select-option-value-${option.id}`}
|
||||
value={option.value}
|
||||
onChange={(input) =>
|
||||
onChange({
|
||||
@ -133,6 +134,7 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({
|
||||
}
|
||||
/>
|
||||
<StyledOptionInput
|
||||
instanceId={`select-option-label-${option.id}`}
|
||||
value={option.label}
|
||||
onChange={(label) => {
|
||||
const optionNameHasBeenEdited = !(
|
||||
|
||||
@ -123,6 +123,10 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
});
|
||||
};
|
||||
|
||||
const descriptionTextAreaId = `${objectMetadataItem?.id}-description`;
|
||||
const labelSingularTextInputId = `${objectMetadataItem?.id}-label-singular`;
|
||||
const labelPluralTextInputId = `${objectMetadataItem?.id}-label-plural`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledInputsContainer>
|
||||
@ -150,6 +154,7 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
defaultValue={objectMetadataItem?.labelSingular ?? ''}
|
||||
render={({ field: { onChange, value }, formState: { errors } }) => (
|
||||
<TextInput
|
||||
instanceId={labelSingularTextInputId}
|
||||
// TODO we should discuss on how to notify user about form validation schema issue, from now just displaying red borders
|
||||
noErrorHelper={true}
|
||||
error={errors.labelSingular?.message}
|
||||
@ -181,6 +186,7 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
defaultValue={objectMetadataItem?.labelPlural ?? ''}
|
||||
render={({ field: { onChange, value }, formState: { errors } }) => (
|
||||
<TextInput
|
||||
instanceId={labelPluralTextInputId}
|
||||
// TODO we should discuss on how to notify user about form validation schema issue, from now just displaying red borders
|
||||
noErrorHelper={true}
|
||||
error={errors.labelPlural?.message}
|
||||
@ -210,6 +216,7 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextArea
|
||||
textAreaId={descriptionTextAreaId}
|
||||
placeholder={t`Write a description`}
|
||||
minRows={4}
|
||||
value={value ?? undefined}
|
||||
@ -264,6 +271,7 @@ export const SettingsDataModelObjectAboutForm = ({
|
||||
}) => (
|
||||
<>
|
||||
<TextInput
|
||||
instanceId={`${objectMetadataItem?.id}-${fieldName}`}
|
||||
label={label}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
|
||||
@ -28,7 +28,7 @@ export const ApiKeyInput = ({ apiKey }: ApiKeyInputProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
<TextInput value={apiKey} fullWidth />
|
||||
<TextInput instanceId="api-key-display" value={apiKey} fullWidth />
|
||||
</StyledLinkContainer>
|
||||
<Button
|
||||
Icon={IconCopy}
|
||||
|
||||
@ -56,9 +56,12 @@ export const ApiKeyNameInput = ({
|
||||
return debouncedUpdate.cancel;
|
||||
}, [debouncedUpdate, apiKeyName]);
|
||||
|
||||
const nameTextInputId = `${apiKeyId}-name`;
|
||||
|
||||
return (
|
||||
<StyledComboInputContainer>
|
||||
<TextInput
|
||||
instanceId={nameTextInputId}
|
||||
placeholder="E.g. backoffice integration"
|
||||
onChange={onNameUpdate}
|
||||
fullWidth
|
||||
|
||||
@ -115,6 +115,10 @@ export const SettingsDevelopersWebhookForm = ({
|
||||
{ label: 'Deleted', value: 'deleted', Icon: IconTrash },
|
||||
];
|
||||
|
||||
const descriptionTextAreaId = `${webhookId}-description`;
|
||||
const targetUrlTextInputId = `${webhookId}-target-url`;
|
||||
const secretTextInputId = `${webhookId}-secret`;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<FormProvider {...formConfig}>
|
||||
@ -156,6 +160,7 @@ export const SettingsDevelopersWebhookForm = ({
|
||||
}) => {
|
||||
return (
|
||||
<TextInput
|
||||
instanceId={targetUrlTextInputId}
|
||||
placeholder={t`https://example.com/webhook`}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
@ -177,6 +182,7 @@ export const SettingsDevelopersWebhookForm = ({
|
||||
control={formConfig.control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextArea
|
||||
textAreaId={descriptionTextAreaId}
|
||||
placeholder={t`Write a description`}
|
||||
minRows={4}
|
||||
value={value || ''}
|
||||
@ -242,6 +248,7 @@ export const SettingsDevelopersWebhookForm = ({
|
||||
control={formConfig.control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId={secretTextInputId}
|
||||
placeholder={t`Secret (optional)`}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import styled from '@emotion/styled';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
@ -129,6 +129,7 @@ export const SettingsIntegrationDatabaseConnectionForm = ({
|
||||
render={({ field: { onChange, value } }) => {
|
||||
return (
|
||||
<TextInput
|
||||
instanceId={`${databaseKey}-${name}`}
|
||||
autoComplete="new-password" // Disable autocomplete
|
||||
label={label}
|
||||
value={value}
|
||||
|
||||
@ -117,6 +117,7 @@ export const PlaygroundSetupForm = () => {
|
||||
control={control}
|
||||
render={({ field: { onChange, value }, fieldState: { error } }) => (
|
||||
<TextInput
|
||||
instanceId="playground-api-key"
|
||||
label={t`API Key`}
|
||||
placeholder="Enter your API key"
|
||||
value={value}
|
||||
|
||||
@ -8,6 +8,7 @@ export const EmailField = () => {
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
instanceId={`user-email-${currentUser?.id}`}
|
||||
value={currentUser?.email}
|
||||
disabled
|
||||
fullWidth
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
|
||||
@ -109,9 +109,13 @@ export const NameFields = ({
|
||||
currentWorkspaceMember,
|
||||
]);
|
||||
|
||||
const firstNameTextInputId = `${currentWorkspaceMember?.id}-first-name`;
|
||||
const lastNameTextInputId = `${currentWorkspaceMember?.id}-last-name`;
|
||||
|
||||
return (
|
||||
<StyledComboInputContainer>
|
||||
<TextInput
|
||||
instanceId={firstNameTextInputId}
|
||||
label={t`First Name`}
|
||||
value={firstName}
|
||||
onChange={setFirstName}
|
||||
@ -119,6 +123,7 @@ export const NameFields = ({
|
||||
fullWidth
|
||||
/>
|
||||
<TextInput
|
||||
instanceId={lastNameTextInputId}
|
||||
label={t`Last Name`}
|
||||
value={lastName}
|
||||
onChange={setLastName}
|
||||
|
||||
@ -210,6 +210,7 @@ export const SettingsRoleAssignment = ({
|
||||
/>
|
||||
<StyledSearchContainer>
|
||||
<StyledSearchInput
|
||||
instanceId="role-assignment-member-search"
|
||||
value={searchFilter}
|
||||
onChange={handleSearchChange}
|
||||
placeholder={t`Search an assigned team member...`}
|
||||
|
||||
@ -125,6 +125,7 @@ export const SettingsRolePermissionsObjectLevelObjectPicker = ({
|
||||
<Section>
|
||||
<StyledSearchContainer>
|
||||
<StyledSearchInput
|
||||
instanceId="role-permissions-object-search"
|
||||
value={searchFilter}
|
||||
onChange={handleSearchChange}
|
||||
placeholder={t`Search an object`}
|
||||
|
||||
@ -42,6 +42,9 @@ export const SettingsRoleSettings = ({
|
||||
|
||||
const { openModal } = useModal();
|
||||
|
||||
const descriptionTextAreaId = `${roleId}-description`;
|
||||
const nameTextInputId = `${roleId}-name`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Section>
|
||||
@ -60,6 +63,7 @@ export const SettingsRoleSettings = ({
|
||||
/>
|
||||
</StyledInputContainer>
|
||||
<TextInput
|
||||
instanceId={nameTextInputId}
|
||||
value={settingsDraftRole.label}
|
||||
fullWidth
|
||||
onChange={(value: string) => {
|
||||
@ -73,6 +77,7 @@ export const SettingsRoleSettings = ({
|
||||
/>
|
||||
</StyledInputsContainer>
|
||||
<TextArea
|
||||
textAreaId={descriptionTextAreaId}
|
||||
minRows={4}
|
||||
placeholder={t`Write a description`}
|
||||
value={settingsDraftRole.description || ''}
|
||||
|
||||
@ -39,6 +39,7 @@ export const SettingsRoleLabelContainer = ({
|
||||
return (
|
||||
<StyledHeaderTitle>
|
||||
<TitleInput
|
||||
instanceId="role-label-input"
|
||||
disabled={!settingsDraftRole.isEditable}
|
||||
sizeVariant="md"
|
||||
value={settingsDraftRole.label}
|
||||
|
||||
@ -7,12 +7,12 @@ import { SettingsSSOSAMLForm } from '@/settings/security/components/SSO/Settings
|
||||
import { SettingSecurityNewSSOIdentityFormValues } from '@/settings/security/types/SSOIdentityProvider';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { ReactElement, useMemo } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { IdentityProviderType } from '~/generated/graphql';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title, IconComponent, IconKey } from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { IdentityProviderType } from '~/generated/graphql';
|
||||
|
||||
const StyledInputsContainer = styled.div`
|
||||
display: grid;
|
||||
@ -88,6 +88,7 @@ export const SettingsSSOIdentitiesProvidersForm = () => {
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId="sso-identity-provider-name"
|
||||
autoComplete="off"
|
||||
label={t`Name`}
|
||||
value={value}
|
||||
|
||||
@ -54,6 +54,7 @@ export const SettingsSSOOIDCForm = () => {
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
<TextInput
|
||||
instanceId="sso-oidc-authorized-uri"
|
||||
readOnly={true}
|
||||
label={t`Authorized URI`}
|
||||
value={authorizedUrl}
|
||||
@ -81,6 +82,7 @@ export const SettingsSSOOIDCForm = () => {
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
<TextInput
|
||||
instanceId="sso-oidc-redirection-uri"
|
||||
readOnly={true}
|
||||
label={t`Redirection URI`}
|
||||
value={redirectionUrl}
|
||||
@ -118,6 +120,7 @@ export const SettingsSSOOIDCForm = () => {
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId="sso-oidc-client-id"
|
||||
autoComplete="off"
|
||||
label={t`Client ID`}
|
||||
value={value}
|
||||
@ -132,6 +135,7 @@ export const SettingsSSOOIDCForm = () => {
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId="sso-oidc-client-secret"
|
||||
autoComplete="off"
|
||||
type="password"
|
||||
label={t`Client Secret`}
|
||||
@ -147,6 +151,7 @@ export const SettingsSSOOIDCForm = () => {
|
||||
control={control}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<TextInput
|
||||
instanceId="sso-oidc-issuer"
|
||||
autoComplete="off"
|
||||
label={t`Issuer URI`}
|
||||
value={value}
|
||||
|
||||
@ -169,6 +169,7 @@ export const SettingsSSOSAMLForm = () => {
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
<TextInput
|
||||
instanceId="sso-saml-acs-url"
|
||||
disabled={true}
|
||||
label="ACS Url"
|
||||
value={acsUrl}
|
||||
@ -196,6 +197,7 @@ export const SettingsSSOSAMLForm = () => {
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
<TextInput
|
||||
instanceId="sso-saml-entity-id"
|
||||
disabled={true}
|
||||
label="Entity ID"
|
||||
value={entityID}
|
||||
|
||||
@ -18,11 +18,15 @@ export const SettingsServerlessFunctionNewForm = ({
|
||||
formValues: ServerlessFunctionNewFormValues;
|
||||
onChange: (key: string) => (value: string) => void;
|
||||
}) => {
|
||||
const descriptionTextAreaId = `${formValues.name}-description`;
|
||||
const nameTextInputId = `${formValues.name}-name`;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title title="About" description="Name and set your function" />
|
||||
<StyledInputsContainer>
|
||||
<TextInput
|
||||
instanceId={nameTextInputId}
|
||||
placeholder="Name"
|
||||
fullWidth
|
||||
autoFocusOnMount
|
||||
@ -30,6 +34,7 @@ export const SettingsServerlessFunctionNewForm = ({
|
||||
onChange={onChange('name')}
|
||||
/>
|
||||
<TextArea
|
||||
textAreaId={descriptionTextAreaId}
|
||||
placeholder="Description"
|
||||
minRows={4}
|
||||
value={formValues.description}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user