Various fixes (#11108)
Fixes many bug regarding TableCell and InlineCells
This commit is contained in:
@ -17,6 +17,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 { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
||||
|
||||
@ -102,7 +103,6 @@ export const CalendarEventDetails = ({
|
||||
value={{
|
||||
recordId: calendarEvent.id,
|
||||
hotkeyScope: 'calendar-event-details',
|
||||
recoilScopeId: `${calendarEvent.id}-${fieldName}`,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
|
||||
field: fieldsByName[fieldName],
|
||||
@ -116,7 +116,7 @@ export const CalendarEventDetails = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `${calendarEvent.id}-${fieldName}`,
|
||||
instanceId: getRecordFieldInputId(calendarEvent.id, fieldName),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell readonly />
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import {
|
||||
IconCalendar,
|
||||
@ -94,11 +94,6 @@ export const AttachmentRow = ({
|
||||
const [attachmentFileName, setAttachmentFileName] =
|
||||
useState(originalFileName);
|
||||
|
||||
const fieldContext = useMemo(
|
||||
() => ({ recoilScopeId: attachment?.id ?? '' }),
|
||||
[attachment?.id],
|
||||
);
|
||||
|
||||
const { destroyOneRecord: destroyOneAttachment } = useDestroyOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.Attachment,
|
||||
});
|
||||
@ -161,7 +156,13 @@ export const AttachmentRow = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldContext.Provider value={fieldContext as GenericFieldContextType}>
|
||||
<FieldContext.Provider
|
||||
value={
|
||||
{
|
||||
recordId: attachment.id,
|
||||
} as GenericFieldContextType
|
||||
}
|
||||
>
|
||||
<ActivityRow disabled>
|
||||
<StyledLeftContent>
|
||||
<AttachmentIcon attachmentType={attachment.type} />
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
import { useContext } from 'react';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { IconArrowUpRight, IconPencil } from 'twenty-ui';
|
||||
|
||||
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { useOpenActivityTargetInlineCellEditMode } from '@/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode';
|
||||
import { useUpdateActivityTargetFromInlineCell } from '@/activities/inline-cell/hooks/useUpdateActivityTargetFromInlineCell';
|
||||
import { ActivityEditorHotkeyScope } from '@/activities/types/ActivityEditorHotkeyScope';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFieldContext } from '@/object-record/hooks/useFieldContext';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider';
|
||||
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
|
||||
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RecordInlineCellContainer } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer';
|
||||
import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
|
||||
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
|
||||
import { MultipleRecordPicker } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
||||
|
||||
type ActivityTargetsInlineCellProps = {
|
||||
activityRecordId: string;
|
||||
@ -26,6 +24,7 @@ type ActivityTargetsInlineCellProps = {
|
||||
activityObjectNameSingular:
|
||||
| CoreObjectNameSingular.Note
|
||||
| CoreObjectNameSingular.Task;
|
||||
componentInstanceId: string;
|
||||
};
|
||||
|
||||
export const ActivityTargetsInlineCell = ({
|
||||
@ -33,26 +32,17 @@ export const ActivityTargetsInlineCell = ({
|
||||
showLabel = true,
|
||||
maxWidth,
|
||||
activityObjectNameSingular,
|
||||
componentInstanceId,
|
||||
}: ActivityTargetsInlineCellProps) => {
|
||||
const { activityTargetObjectRecords } =
|
||||
useActivityTargetObjectRecords(activityRecordId);
|
||||
|
||||
const multipleRecordPickerInstanceId = `multiple-record-picker-target-${activityRecordId}`;
|
||||
|
||||
const { closeInlineCell } = useInlineCell();
|
||||
const { closeInlineCell } = useInlineCell(componentInstanceId);
|
||||
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const isFieldReadOnly = useIsFieldValueReadOnly();
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
closeInlineCell();
|
||||
},
|
||||
ActivityEditorHotkeyScope.ActivityTargets,
|
||||
);
|
||||
|
||||
const { FieldContextProvider: ActivityTargetsContextProvider } =
|
||||
useFieldContext({
|
||||
objectNameSingular: activityObjectNameSingular,
|
||||
@ -72,7 +62,11 @@ export const ActivityTargetsInlineCell = ({
|
||||
});
|
||||
|
||||
return (
|
||||
<RecordFieldInputScope recordFieldInputScopeId={activityRecordId}>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: componentInstanceId,
|
||||
}}
|
||||
>
|
||||
<FieldFocusContextProvider>
|
||||
{ActivityTargetsContextProvider && (
|
||||
<ActivityTargetsContextProvider>
|
||||
@ -80,20 +74,20 @@ export const ActivityTargetsInlineCell = ({
|
||||
value={{
|
||||
buttonIcon: IconPencil,
|
||||
customEditHotkeyScope:
|
||||
ActivityEditorHotkeyScope.ActivityTargets,
|
||||
MultipleRecordPickerHotkeyScope.MultipleRecordPicker,
|
||||
IconLabel: showLabel ? IconArrowUpRight : undefined,
|
||||
showLabel: showLabel,
|
||||
readonly: isFieldReadOnly,
|
||||
labelWidth: fieldDefinition?.labelWidth,
|
||||
editModeContent: (
|
||||
<MultipleRecordPicker
|
||||
componentInstanceId={multipleRecordPickerInstanceId}
|
||||
componentInstanceId={componentInstanceId}
|
||||
onClickOutside={() => {
|
||||
closeInlineCell();
|
||||
}}
|
||||
onChange={(morphItem) => {
|
||||
updateActivityTargetFromInlineCell({
|
||||
recordPickerInstanceId: multipleRecordPickerInstanceId,
|
||||
recordPickerInstanceId: componentInstanceId,
|
||||
morphItem,
|
||||
activityTargetWithTargetRecords:
|
||||
activityTargetObjectRecords,
|
||||
@ -113,7 +107,7 @@ export const ActivityTargetsInlineCell = ({
|
||||
),
|
||||
onOpenEditMode: () => {
|
||||
openActivityTargetInlineCellEditMode({
|
||||
recordPickerInstanceId: multipleRecordPickerInstanceId,
|
||||
recordPickerInstanceId: componentInstanceId,
|
||||
activityTargetObjectRecords,
|
||||
});
|
||||
},
|
||||
@ -124,6 +118,6 @@ export const ActivityTargetsInlineCell = ({
|
||||
</ActivityTargetsContextProvider>
|
||||
)}
|
||||
</FieldFocusContextProvider>
|
||||
</RecordFieldInputScope>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -96,6 +96,7 @@ export const NoteCard = ({
|
||||
{NoteTargetsContextProvider && (
|
||||
<NoteTargetsContextProvider>
|
||||
<ActivityTargetsInlineCell
|
||||
componentInstanceId={`note-card-${note.id}-targets`}
|
||||
activityRecordId={note.id}
|
||||
activityObjectNameSingular={CoreObjectNameSingular.Note}
|
||||
/>
|
||||
|
||||
@ -135,6 +135,7 @@ export const TaskRow = ({ task }: { task: Task }) => {
|
||||
activityRecordId={task.id}
|
||||
showLabel={false}
|
||||
maxWidth={200}
|
||||
componentInstanceId={`task-row-targets-${task.id}`}
|
||||
/>
|
||||
</TaskTargetsContextProvider>
|
||||
)}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
export enum ActivityEditorHotkeyScope {
|
||||
ActivityTitle = 'activity-title',
|
||||
ActivityBody = 'activity-body',
|
||||
ActivityTargets = 'activity-targets',
|
||||
}
|
||||
|
||||
@ -22,6 +22,8 @@ import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/reco
|
||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
|
||||
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
@ -29,7 +31,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useIsMobile } from 'twenty-ui';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
@ -65,9 +67,23 @@ export const CommandMenuContainer = ({
|
||||
|
||||
useCommandMenuHotKeys();
|
||||
|
||||
const handleClickOutside = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
const hotkeyScope = snapshot
|
||||
.getLoadable(currentHotkeyScopeState)
|
||||
.getValue();
|
||||
|
||||
if (hotkeyScope.scope === AppHotkeyScope.CommandMenuOpen) {
|
||||
closeCommandMenu();
|
||||
}
|
||||
},
|
||||
[closeCommandMenu],
|
||||
);
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [commandMenuRef],
|
||||
callback: closeCommandMenu,
|
||||
callback: handleClickOutside,
|
||||
listenerId: 'COMMAND_MENU_LISTENER_ID',
|
||||
excludeClassNames: ['page-header-command-menu-button'],
|
||||
});
|
||||
|
||||
@ -77,7 +77,12 @@ export const RecordChip = ({
|
||||
avatarType={recordChipData.avatarType}
|
||||
avatarUrl={recordChipData.avatarUrl ?? ''}
|
||||
className={className}
|
||||
variant={variant}
|
||||
variant={
|
||||
variant ??
|
||||
(!forceDisableClick
|
||||
? AvatarChipVariant.Regular
|
||||
: AvatarChipVariant.Transparent)
|
||||
}
|
||||
to={to ?? getLinkToShowPage(objectNameSingular, record)}
|
||||
onClick={(clickEvent) => {
|
||||
// TODO refactor wrapper event listener to avoid colliding events
|
||||
|
||||
@ -59,7 +59,6 @@ export const useFieldContext = ({
|
||||
key={objectRecordId + fieldMetadataItem.id}
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
recoilScopeId: objectRecordId + fieldMetadataItem.id,
|
||||
isLabelIdentifier,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
|
||||
@ -13,6 +13,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const RecordBoardCardBody = ({
|
||||
@ -43,7 +44,6 @@ export const RecordBoardCardBody = ({
|
||||
value={{
|
||||
recordId,
|
||||
maxWidth: 156,
|
||||
recoilScopeId: `board-card-${recordId}-${fieldDefinition.fieldMetadataId}`,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: {
|
||||
disableTooltip: false,
|
||||
@ -64,7 +64,11 @@ export const RecordBoardCardBody = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `board-card-${recordId}-${fieldDefinition.fieldMetadataId}`,
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition.metadata.fieldName,
|
||||
'record-board-card',
|
||||
),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
|
||||
@ -10,11 +10,8 @@ import { PhonesFieldInput } from '@/object-record/record-field/meta-types/input/
|
||||
import { RawJsonFieldInput } from '@/object-record/record-field/meta-types/input/components/RawJsonFieldInput';
|
||||
import { RelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput';
|
||||
import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/components/SelectFieldInput';
|
||||
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
|
||||
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
|
||||
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
|
||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
|
||||
import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
@ -30,6 +27,7 @@ import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/is
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
|
||||
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
|
||||
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { FieldContext } from '../contexts/FieldContext';
|
||||
import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput';
|
||||
@ -58,7 +56,6 @@ type FieldInputProps = {
|
||||
};
|
||||
|
||||
export const FieldInput = ({
|
||||
recordFieldInputdId,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
onEnter,
|
||||
@ -71,9 +68,7 @@ export const FieldInput = ({
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
return (
|
||||
<RecordFieldInputScope
|
||||
recordFieldInputScopeId={getScopeIdFromComponentId(recordFieldInputdId)}
|
||||
>
|
||||
<>
|
||||
{isFieldRelationToOneObject(fieldDefinition) ? (
|
||||
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldRelationFromManyObjects(fieldDefinition) ? (
|
||||
@ -173,6 +168,6 @@ export const FieldInput = ({
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RecordFieldInputScope>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -23,7 +23,6 @@ export type GenericFieldContextType = {
|
||||
fieldDefinition: FieldDefinition<FieldMetadata>;
|
||||
useUpdateRecord?: RecordUpdateHook;
|
||||
recordId: string;
|
||||
recoilScopeId?: string;
|
||||
hotkeyScope: string;
|
||||
isLabelIdentifier: boolean;
|
||||
labelIdentifierLink?: string;
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
import { RecordFieldInputScopeInternalContext } from '@/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext';
|
||||
import { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector';
|
||||
import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId';
|
||||
import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
|
||||
|
||||
export const useRecordFieldInputStates = <FieldValue>(
|
||||
recordFieldInputId?: string,
|
||||
) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordFieldInputScopeInternalContext,
|
||||
getScopeIdOrUndefinedFromComponentId(recordFieldInputId),
|
||||
);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
getDraftValueSelector: extractComponentSelector<
|
||||
FieldInputDraftValue<FieldValue> | undefined
|
||||
>(recordFieldInputDraftValueComponentSelector, scopeId),
|
||||
};
|
||||
};
|
||||
@ -9,7 +9,6 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { computeDraftValueFromFieldValue } from '@/object-record/record-field/utils/computeDraftValueFromFieldValue';
|
||||
import { computeDraftValueFromString } from '@/object-record/record-field/utils/computeDraftValueFromString';
|
||||
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
|
||||
|
||||
export const useInitDraftValueV2 = <FieldValue>() => {
|
||||
@ -19,19 +18,19 @@ export const useInitDraftValueV2 = <FieldValue>() => {
|
||||
value,
|
||||
recordId,
|
||||
fieldDefinition,
|
||||
fieldComponentInstanceId,
|
||||
}: {
|
||||
value?: string;
|
||||
recordId: string;
|
||||
fieldDefinition: FieldDefinition<FieldMetadata>;
|
||||
fieldComponentInstanceId: string;
|
||||
}) => {
|
||||
const recordFieldInputScopeId = `${getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
)}`;
|
||||
|
||||
const getDraftValueSelector = extractComponentSelector<
|
||||
FieldInputDraftValue<FieldValue> | undefined
|
||||
>(recordFieldInputDraftValueComponentSelector, recordFieldInputScopeId);
|
||||
>(
|
||||
recordFieldInputDraftValueComponentSelector,
|
||||
fieldComponentInstanceId,
|
||||
);
|
||||
|
||||
const recordFieldValue = snapshot
|
||||
.getLoadable(
|
||||
|
||||
@ -1,13 +1,22 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useRecordFieldInputStates } from '@/object-record/record-field/hooks/internal/useRecordFieldInputStates';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector';
|
||||
import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
|
||||
|
||||
export const useRecordFieldInput = <FieldValue>(
|
||||
recordFieldInputId?: string,
|
||||
) => {
|
||||
const { scopeId, getDraftValueSelector } =
|
||||
useRecordFieldInputStates<FieldValue>(recordFieldInputId);
|
||||
export const useRecordFieldInput = <FieldValue>() => {
|
||||
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const getDraftValueSelector = extractComponentSelector<
|
||||
FieldInputDraftValue<FieldValue> | undefined
|
||||
>(
|
||||
recordFieldInputDraftValueComponentSelector,
|
||||
recordFieldComponentInstanceId,
|
||||
);
|
||||
|
||||
const setDraftValue = useSetRecoilState(getDraftValueSelector());
|
||||
|
||||
@ -26,7 +35,6 @@ export const useRecordFieldInput = <FieldValue>(
|
||||
};
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
setDraftValue,
|
||||
getDraftValueSelector,
|
||||
isDraftValueEmpty,
|
||||
|
||||
@ -19,7 +19,6 @@ export const FieldContextProvider = ({
|
||||
value={{
|
||||
recordId: recordId ?? '1',
|
||||
isLabelIdentifier: false,
|
||||
recoilScopeId: '1',
|
||||
hotkeyScope: 'hotkey-scope',
|
||||
fieldDefinition,
|
||||
useUpdateRecord: () => [() => undefined, {}],
|
||||
|
||||
@ -41,7 +41,7 @@ export const useAddressField = () => {
|
||||
};
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldAddressValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldAddressValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ export const useCurrencyField = () => {
|
||||
};
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldCurrencyValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldCurrencyValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -25,9 +25,7 @@ export const useDateField = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const { setDraftValue } = useRecordFieldInput<FieldDateValue>(
|
||||
`${recordId}-${fieldName}`,
|
||||
);
|
||||
const { setDraftValue } = useRecordFieldInput<FieldDateValue>();
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
|
||||
@ -29,9 +29,7 @@ export const useDateTimeField = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const { setDraftValue } = useRecordFieldInput<FieldDateTimeValue>(
|
||||
`${recordId}-${fieldName}`,
|
||||
);
|
||||
const { setDraftValue } = useRecordFieldInput<FieldDateTimeValue>();
|
||||
|
||||
return {
|
||||
fieldDefinition,
|
||||
|
||||
@ -27,7 +27,7 @@ export const useEmailsField = () => {
|
||||
);
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldEmailsValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldEmailsValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ export const useFullNameField = () => {
|
||||
};
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldFullNameValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldFullNameValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ export const useJsonField = () => {
|
||||
};
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldJsonValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldJsonValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ export const useLinksField = () => {
|
||||
);
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldLinksValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldLinksValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ export const useMultiSelectField = () => {
|
||||
const persistField = usePersistField();
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldMultiSelectValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldMultiSelectValue>();
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
return {
|
||||
|
||||
@ -55,7 +55,7 @@ export const useNumberField = () => {
|
||||
};
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldNumberValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldNumberValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ export const usePhonesField = () => {
|
||||
);
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldPhonesValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldPhonesValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -28,9 +28,8 @@ export const useRelationField = <T extends ObjectRecord | ObjectRecord[]>() => {
|
||||
recordStoreFamilySelector({ recordId, fieldName }),
|
||||
);
|
||||
|
||||
const { getDraftValueSelector } = useRecordFieldInput<FieldRelationValue<T>>(
|
||||
`${recordId}-${fieldName}`,
|
||||
);
|
||||
const { getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldRelationValue<T>>();
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
const initialSearchValue = draftValue;
|
||||
|
||||
@ -35,7 +35,7 @@ export const useRichTextField = () => {
|
||||
const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : '';
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldRichTextValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ export const useRichTextV2Field = () => {
|
||||
: ({ blocknote: null, markdown: null } as FieldRichTextV2Value);
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldRichTextValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ export const useSelectField = () => {
|
||||
const persistField = usePersistField();
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldSelectValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldSelectValue>();
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
return {
|
||||
|
||||
@ -28,7 +28,7 @@ export const useTextField = () => {
|
||||
const fieldTextValue = isFieldTextValue(fieldValue) ? fieldValue : '';
|
||||
|
||||
const { setDraftValue, getDraftValueSelector } =
|
||||
useRecordFieldInput<FieldTextValue>(`${recordId}-${fieldName}`);
|
||||
useRecordFieldInput<FieldTextValue>();
|
||||
|
||||
const draftValue = useRecoilValue(getDraftValueSelector());
|
||||
|
||||
|
||||
@ -12,6 +12,8 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
|
||||
const AddressValueSetterEffect = ({
|
||||
value,
|
||||
@ -49,32 +51,42 @@ const AddressInputWithContext = ({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'text',
|
||||
label: 'Address',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
iconName: 'IconTag',
|
||||
metadata: {
|
||||
fieldName: 'Address',
|
||||
placeHolder: 'Enter text',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Address',
|
||||
'record-table-cell',
|
||||
),
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<AddressValueSetterEffect value={value} />
|
||||
<AddressInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
value={value}
|
||||
hotkeyScope="hotkey-scope"
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'text',
|
||||
label: 'Address',
|
||||
type: FieldMetadataType.ADDRESS,
|
||||
iconName: 'IconTag',
|
||||
metadata: {
|
||||
fieldName: 'Address',
|
||||
placeHolder: 'Enter text',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<AddressValueSetterEffect value={value} />
|
||||
<AddressInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
value={value}
|
||||
hotkeyScope="hotkey-scope"
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -7,6 +7,8 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
|
||||
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import {
|
||||
BooleanFieldInput,
|
||||
BooleanFieldInputProps,
|
||||
@ -39,23 +41,36 @@ const BooleanFieldInputWithContext = ({
|
||||
onSubmit,
|
||||
}: BooleanFieldInputWithContextProps) => {
|
||||
return (
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
defaultValue: false,
|
||||
fieldMetadataId: 'boolean',
|
||||
label: 'Boolean',
|
||||
iconName: 'Icon123',
|
||||
type: FieldMetadataType.BOOLEAN,
|
||||
metadata: {
|
||||
fieldName: 'Boolean',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Boolean',
|
||||
'record-table-cell',
|
||||
),
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<BooleanFieldValueSetterEffect value={value} recordId={recordId ?? ''} />
|
||||
<BooleanFieldInput onSubmit={onSubmit} testId="boolean-field-input" />
|
||||
</FieldContextProvider>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
defaultValue: false,
|
||||
fieldMetadataId: 'boolean',
|
||||
label: 'Boolean',
|
||||
iconName: 'Icon123',
|
||||
type: FieldMetadataType.BOOLEAN,
|
||||
metadata: {
|
||||
fieldName: 'Boolean',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<BooleanFieldValueSetterEffect
|
||||
value={value}
|
||||
recordId={recordId ?? ''}
|
||||
/>
|
||||
<BooleanFieldInput onSubmit={onSubmit} testId="boolean-field-input" />
|
||||
</FieldContextProvider>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -6,6 +6,8 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
|
||||
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
|
||||
import { useDateTimeField } from '../../../hooks/useDateTimeField';
|
||||
import {
|
||||
@ -66,7 +68,15 @@ const DateFieldInputWithContext = ({
|
||||
}, [setHotkeyScope]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Date',
|
||||
'record-table-cell',
|
||||
),
|
||||
}}
|
||||
>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'date',
|
||||
@ -90,7 +100,7 @@ const DateFieldInputWithContext = ({
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div"></div>
|
||||
</div>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -7,6 +7,8 @@ import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { useNumberField } from '../../../hooks/useNumberField';
|
||||
@ -43,7 +45,15 @@ const NumberFieldInputWithContext = ({
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Number',
|
||||
'record-table-cell',
|
||||
),
|
||||
}}
|
||||
>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'number',
|
||||
@ -69,7 +79,7 @@ const NumberFieldInputWithContext = ({
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
</div>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -7,6 +7,8 @@ import { isDefined } from 'twenty-shared';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { FieldRatingValue } from '../../../../types/FieldMetadata';
|
||||
import { useRatingField } from '../../../hooks/useRatingField';
|
||||
import { RatingFieldInput, RatingFieldInputProps } from '../RatingFieldInput';
|
||||
@ -42,22 +44,32 @@ const RatingFieldInputWithContext = ({
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'rating',
|
||||
label: 'Rating',
|
||||
type: FieldMetadataType.RATING,
|
||||
iconName: 'Icon123',
|
||||
metadata: {
|
||||
fieldName: 'Rating',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId ?? '',
|
||||
'Rating',
|
||||
'record-table-cell',
|
||||
),
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<RatingFieldValueSetterEffect value={value} />
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
</FieldContextProvider>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'rating',
|
||||
label: 'Rating',
|
||||
type: FieldMetadataType.RATING,
|
||||
iconName: 'Icon123',
|
||||
metadata: {
|
||||
fieldName: 'Rating',
|
||||
objectMetadataNameSingular: 'person',
|
||||
},
|
||||
}}
|
||||
recordId={recordId}
|
||||
>
|
||||
<RatingFieldValueSetterEffect value={value} />
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
</FieldContextProvider>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,13 +1,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 { FieldMetadataType } from '~/generated/graphql';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { Decorator, Meta, StoryObj } from '@storybook/react';
|
||||
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { FieldContextProvider } from '../../../components/FieldContextProvider';
|
||||
import { useTextField } from '../../../hooks/useTextField';
|
||||
import { TextFieldInput, TextFieldInputProps } from '../TextFieldInput';
|
||||
@ -43,7 +44,11 @@ const TextFieldInputWithContext = ({
|
||||
}, [setHotKeyScope]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'record-field-component-instance-id',
|
||||
}}
|
||||
>
|
||||
<FieldContextProvider
|
||||
fieldDefinition={{
|
||||
fieldMetadataId: 'text',
|
||||
@ -69,7 +74,7 @@ const TextFieldInputWithContext = ({
|
||||
/>
|
||||
</FieldContextProvider>
|
||||
<div data-testid="data-field-input-click-outside-div" />
|
||||
</div>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { RecordFieldInputScopeInternalContext } from '@/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext';
|
||||
|
||||
type RecordFieldInputScopeProps = {
|
||||
children: ReactNode;
|
||||
recordFieldInputScopeId: string;
|
||||
};
|
||||
|
||||
export const RecordFieldInputScope = ({
|
||||
children,
|
||||
recordFieldInputScopeId,
|
||||
}: RecordFieldInputScopeProps) => {
|
||||
return (
|
||||
<RecordFieldInputScopeInternalContext.Provider
|
||||
value={{ scopeId: recordFieldInputScopeId }}
|
||||
>
|
||||
{children}
|
||||
</RecordFieldInputScopeInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey';
|
||||
|
||||
type RecordFieldInputScopeInternalContextProps = RecoilComponentStateKey;
|
||||
|
||||
export const RecordFieldInputScopeInternalContext =
|
||||
createScopeInternalContext<RecordFieldInputScopeInternalContextProps>();
|
||||
@ -12,15 +12,19 @@ import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFie
|
||||
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
|
||||
import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
|
||||
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
|
||||
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
|
||||
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||
import { RecordInlineCellContainer } from './RecordInlineCellContainer';
|
||||
import {
|
||||
@ -42,6 +46,10 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
|
||||
onCloseEditMode,
|
||||
} = useContext(FieldContext);
|
||||
|
||||
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const buttonIcon = useGetButtonIcon();
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
@ -78,15 +86,22 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleClickOutside: FieldInputClickOutsideEvent = (
|
||||
persistField,
|
||||
event,
|
||||
) => {
|
||||
event.stopImmediatePropagation();
|
||||
const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(persistField, event) => {
|
||||
const hotkeyScope = snapshot
|
||||
.getLoadable(currentHotkeyScopeState)
|
||||
.getValue();
|
||||
if (hotkeyScope.scope !== InlineCellHotkeyScope.InlineCell) {
|
||||
return;
|
||||
}
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
},
|
||||
[closeInlineCell],
|
||||
);
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
const { openFieldInput, closeFieldInput } = useOpenFieldInputEditMode();
|
||||
@ -132,10 +147,7 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
|
||||
isCentered,
|
||||
editModeContent: (
|
||||
<FieldInput
|
||||
recordFieldInputdId={getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
)}
|
||||
recordFieldInputdId={recordFieldComponentInstanceId}
|
||||
onEnter={handleEnter}
|
||||
onCancel={handleCancel}
|
||||
onEscape={handleEscape}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
|
||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
||||
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
||||
import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
||||
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 styled from '@emotion/styled';
|
||||
@ -37,21 +37,19 @@ export const RecordInlineCellEditMode = ({
|
||||
children,
|
||||
}: RecordInlineCellEditModeProps) => {
|
||||
const { isCentered } = useContext(RecordInlineCellContext);
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const instanceId = getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const setFieldInputLayoutDirection = useSetRecoilComponentStateV2(
|
||||
recordFieldInputLayoutDirectionComponentState,
|
||||
instanceId,
|
||||
recordFieldComponentInstanceId,
|
||||
);
|
||||
|
||||
const setFieldInputLayoutDirectionLoading = useSetRecoilComponentStateV2(
|
||||
recordFieldInputLayoutDirectionLoadingComponentState,
|
||||
instanceId,
|
||||
recordFieldComponentInstanceId,
|
||||
);
|
||||
|
||||
const setFieldInputLayoutDirectionMiddleware = {
|
||||
|
||||
@ -6,22 +6,28 @@ import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousH
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
import { useInitDraftValueV2 } from '@/object-record/record-field/hooks/useInitDraftValueV2';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { isInlineCellInEditModeScopedState } from '../states/isInlineCellInEditModeScopedState';
|
||||
import { InlineCellHotkeyScope } from '../types/InlineCellHotkeyScope';
|
||||
|
||||
export const useInlineCell = () => {
|
||||
const {
|
||||
recoilScopeId = '',
|
||||
recordId,
|
||||
fieldDefinition,
|
||||
} = useContext(FieldContext);
|
||||
export const useInlineCell = (
|
||||
recordFieldComponentInstanceIdFromProps?: string,
|
||||
) => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
recordFieldComponentInstanceIdFromProps,
|
||||
);
|
||||
|
||||
const [isInlineCellInEditMode, setIsInlineCellInEditMode] = useRecoilState(
|
||||
isInlineCellInEditModeScopedState(recoilScopeId),
|
||||
isInlineCellInEditModeScopedState(recordFieldComponentInstanceId),
|
||||
);
|
||||
|
||||
const { onOpenEditMode, onCloseEditMode } = useRecordInlineCellContext();
|
||||
@ -50,7 +56,15 @@ export const useInlineCell = () => {
|
||||
const openInlineCell = (customEditHotkeyScopeForField?: string) => {
|
||||
onOpenEditMode?.();
|
||||
setIsInlineCellInEditMode(true);
|
||||
initFieldInputDraftValue({ recordId, fieldDefinition });
|
||||
initFieldInputDraftValue({
|
||||
recordId,
|
||||
fieldDefinition,
|
||||
fieldComponentInstanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition.metadata.fieldName,
|
||||
'inline-cell',
|
||||
),
|
||||
});
|
||||
|
||||
if (isDefined(customEditHotkeyScopeForField)) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(customEditHotkeyScopeForField);
|
||||
|
||||
@ -15,6 +15,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 { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
|
||||
@ -97,7 +98,6 @@ export const FieldsCard = ({
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
maxWidth: 200,
|
||||
recoilScopeId: objectRecordId + fieldMetadataItem.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
@ -110,22 +110,21 @@ export const FieldsCard = ({
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
}}
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: objectRecordId + fieldMetadataItem.id,
|
||||
}}
|
||||
>
|
||||
<ActivityTargetsInlineCell
|
||||
activityObjectNameSingular={
|
||||
objectNameSingular as
|
||||
| CoreObjectNameSingular.Note
|
||||
| CoreObjectNameSingular.Task
|
||||
}
|
||||
activityRecordId={objectRecordId}
|
||||
showLabel={true}
|
||||
maxWidth={200}
|
||||
/>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
<ActivityTargetsInlineCell
|
||||
componentInstanceId={getRecordFieldInputId(
|
||||
objectRecordId,
|
||||
fieldMetadataItem.name,
|
||||
'fields-card',
|
||||
)}
|
||||
activityObjectNameSingular={
|
||||
objectNameSingular as
|
||||
| CoreObjectNameSingular.Note
|
||||
| CoreObjectNameSingular.Task
|
||||
}
|
||||
activityRecordId={objectRecordId}
|
||||
showLabel={true}
|
||||
maxWidth={200}
|
||||
/>
|
||||
</FieldContext.Provider>
|
||||
),
|
||||
)}
|
||||
@ -135,7 +134,6 @@ export const FieldsCard = ({
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
maxWidth: 200,
|
||||
recoilScopeId: objectRecordId + fieldMetadataItem.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
@ -150,7 +148,11 @@ export const FieldsCard = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `${objectRecordId}-${fieldMetadataItem.id}`,
|
||||
instanceId: getRecordFieldInputId(
|
||||
objectRecordId,
|
||||
fieldMetadataItem.name,
|
||||
'fields-card',
|
||||
),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell loading={recordLoading} />
|
||||
@ -169,7 +171,6 @@ export const FieldsCard = ({
|
||||
key={objectRecordId + fieldMetadataItem.id}
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
recoilScopeId: objectRecordId + fieldMetadataItem.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
|
||||
@ -68,8 +68,6 @@ export const ObjectRecordShowPageBreadcrumb = ({
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
recoilScopeId:
|
||||
objectRecordId + labelIdentifierFieldMetadataItem?.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: {
|
||||
type:
|
||||
|
||||
@ -70,8 +70,6 @@ export const SummaryCard = ({
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
recoilScopeId:
|
||||
objectRecordId + labelIdentifierFieldMetadataItem?.id,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: {
|
||||
type:
|
||||
|
||||
@ -37,6 +37,7 @@ import { RecordDetailRecordsListItem } from '@/object-record/record-show/record-
|
||||
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -268,7 +269,6 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
value={{
|
||||
recordId: relationRecord.id,
|
||||
maxWidth: 200,
|
||||
recoilScopeId: `${relationRecord.id}-${fieldMetadataItem.id}`,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
@ -283,7 +283,11 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `${relationRecord.id}-${fieldMetadataItem.id}`,
|
||||
instanceId: getRecordFieldInputId(
|
||||
relationRecord.id,
|
||||
fieldMetadataItem.name,
|
||||
'record-detail',
|
||||
),
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
|
||||
@ -13,7 +13,10 @@ export const useUpsertRecordsInStore = () => {
|
||||
.getValue();
|
||||
|
||||
if (JSON.stringify(currentRecord) !== JSON.stringify(record)) {
|
||||
set(recordStoreFamilyState(record.id), record);
|
||||
set(recordStoreFamilyState(record.id), {
|
||||
...currentRecord,
|
||||
...record,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -62,7 +62,10 @@ export const useSetRecordTableData = ({
|
||||
.getValue();
|
||||
|
||||
if (JSON.stringify(currentRecord) !== JSON.stringify(record)) {
|
||||
set(recordStoreFamilyState(record.id), record);
|
||||
set(recordStoreFamilyState(record.id), {
|
||||
...currentRecord,
|
||||
...record,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
|
||||
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
|
||||
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
||||
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 styled from '@emotion/styled';
|
||||
@ -14,7 +14,7 @@ import {
|
||||
offset,
|
||||
useFloating,
|
||||
} from '@floating-ui/react';
|
||||
import { ReactElement, useContext } from 'react';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
const StyledEditableCellEditModeContainer = styled.div<RecordTableCellEditModeProps>`
|
||||
align-items: center;
|
||||
@ -35,25 +35,21 @@ export type RecordTableCellEditModeProps = {
|
||||
export const RecordTableCellEditMode = ({
|
||||
children,
|
||||
}: RecordTableCellEditModeProps) => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const isFieldInError = useRecoilComponentValueV2(
|
||||
recordFieldInputIsFieldInErrorComponentState,
|
||||
);
|
||||
|
||||
const instanceId = getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
RecordFieldComponentInstanceContext,
|
||||
);
|
||||
|
||||
const setFieldInputLayoutDirection = useSetRecoilComponentStateV2(
|
||||
recordFieldInputLayoutDirectionComponentState,
|
||||
instanceId,
|
||||
recordFieldComponentInstanceId,
|
||||
);
|
||||
|
||||
const setFieldInputLayoutDirectionLoading = useSetRecoilComponentStateV2(
|
||||
recordFieldInputLayoutDirectionLoadingComponentState,
|
||||
instanceId,
|
||||
recordFieldComponentInstanceId,
|
||||
);
|
||||
|
||||
const setFieldInputLayoutDirectionMiddleware = {
|
||||
|
||||
@ -16,6 +16,7 @@ import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/co
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
@ -72,7 +73,6 @@ export const RecordTableCellFieldContextWrapper = ({
|
||||
return (
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recoilScopeId: recordId + columnDefinition.label,
|
||||
recordId,
|
||||
fieldDefinition: columnDefinition,
|
||||
useUpdateRecord: () => [updateRecord, {}],
|
||||
@ -89,7 +89,13 @@ export const RecordTableCellFieldContextWrapper = ({
|
||||
}}
|
||||
>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordId + columnDefinition.label }}
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
columnDefinition.metadata.fieldName,
|
||||
'record-table-cell',
|
||||
),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
|
||||
@ -3,9 +3,13 @@ import { useContext } from 'react';
|
||||
import { FieldInput } from '@/object-record/record-field/components/FieldInput';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext';
|
||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const RecordTableCellFieldInput = () => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
@ -31,16 +35,22 @@ export const RecordTableCellFieldInput = () => {
|
||||
onCloseTableCell();
|
||||
};
|
||||
|
||||
const handleClickOutside = (
|
||||
persistField: () => void,
|
||||
event: MouseEvent | TouchEvent,
|
||||
) => {
|
||||
event.stopImmediatePropagation();
|
||||
const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(persistField, event) => {
|
||||
const hotkeyScope = snapshot
|
||||
.getLoadable(currentHotkeyScopeState)
|
||||
.getValue();
|
||||
if (hotkeyScope.scope !== TableHotkeyScope.CellEditMode) {
|
||||
return;
|
||||
}
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
persistField();
|
||||
|
||||
onCloseTableCell();
|
||||
};
|
||||
persistField();
|
||||
onCloseTableCell();
|
||||
},
|
||||
[onCloseTableCell],
|
||||
);
|
||||
|
||||
const handleEscape: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
|
||||
@ -24,6 +24,7 @@ import { recordIndexOpenRecordInState } from '@/object-record/record-index/state
|
||||
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
||||
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates';
|
||||
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
|
||||
@ -158,6 +159,11 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
|
||||
value: initialValue,
|
||||
recordId,
|
||||
fieldDefinition,
|
||||
fieldComponentInstanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition.metadata.fieldName,
|
||||
'record-table-cell',
|
||||
),
|
||||
});
|
||||
|
||||
toggleClickOutsideListener(false);
|
||||
|
||||
@ -8,6 +8,7 @@ import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEv
|
||||
import { useInlineCell } from '../../record-inline-cell/hooks/useInlineCell';
|
||||
|
||||
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { RecordTitleCellContainer } from '@/object-record/record-title-cell/components/RecordTitleCellContainer';
|
||||
import {
|
||||
RecordTitleCellContext,
|
||||
@ -30,7 +31,13 @@ export const RecordTitleCell = ({
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
|
||||
const { closeInlineCell } = useInlineCell();
|
||||
const { closeInlineCell } = useInlineCell(
|
||||
getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
'title',
|
||||
),
|
||||
);
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
@ -64,10 +71,6 @@ export const RecordTitleCell = ({
|
||||
const recordTitleCellContextValue: RecordTitleCellContextProps = {
|
||||
editModeContent: (
|
||||
<RecordTitleCellFieldInput
|
||||
recordFieldInputId={getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
)}
|
||||
onEnter={handleEnter}
|
||||
onEscape={handleEscape}
|
||||
onTab={handleTab}
|
||||
@ -82,10 +85,20 @@ export const RecordTitleCell = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<FieldFocusContextProvider>
|
||||
<RecordTitleCellContext.Provider value={recordTitleCellContextValue}>
|
||||
<RecordTitleCellContainer />
|
||||
</RecordTitleCellContext.Provider>
|
||||
</FieldFocusContextProvider>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
'title',
|
||||
),
|
||||
}}
|
||||
>
|
||||
<FieldFocusContextProvider>
|
||||
<RecordTitleCellContext.Provider value={recordTitleCellContextValue}>
|
||||
<RecordTitleCellContainer />
|
||||
</RecordTitleCellContext.Provider>
|
||||
</FieldFocusContextProvider>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
@ -11,7 +8,6 @@ import { RecordTitleCellTextFieldInput } from '@/object-record/record-title-cell
|
||||
import { RecordTitleFullNameFieldInput } from '@/object-record/record-title-cell/components/RecordTitleFullNameFieldInput';
|
||||
|
||||
type RecordTitleCellFieldInputProps = {
|
||||
recordFieldInputId: string;
|
||||
onClickOutside?: (
|
||||
persist: () => void,
|
||||
event: MouseEvent | TouchEvent,
|
||||
@ -25,7 +21,6 @@ type RecordTitleCellFieldInputProps = {
|
||||
|
||||
export const RecordTitleCellFieldInput = ({
|
||||
sizeVariant,
|
||||
recordFieldInputId,
|
||||
onEnter,
|
||||
onEscape,
|
||||
onShiftTab,
|
||||
@ -39,9 +34,7 @@ export const RecordTitleCellFieldInput = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<RecordFieldInputScope
|
||||
recordFieldInputScopeId={getScopeIdFromComponentId(recordFieldInputId)}
|
||||
>
|
||||
<>
|
||||
{isFieldText(fieldDefinition) ? (
|
||||
<RecordTitleCellTextFieldInput
|
||||
onEnter={onEnter}
|
||||
@ -61,6 +54,6 @@ export const RecordTitleCellFieldInput = ({
|
||||
sizeVariant={sizeVariant}
|
||||
/>
|
||||
) : null}
|
||||
</RecordFieldInputScope>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const getRecordFieldInputId = (
|
||||
recordId: string,
|
||||
fieldName?: string,
|
||||
prefix?: string,
|
||||
): string => {
|
||||
if (isDefined(prefix)) {
|
||||
return `${prefix}-${recordId}-${fieldName}`;
|
||||
}
|
||||
|
||||
return `${recordId}-${fieldName}`;
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { FieldDisplay } from '@/object-record/record-field/components/FieldDispl
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { BooleanFieldInput } from '@/object-record/record-field/meta-types/input/components/BooleanFieldInput';
|
||||
import { RatingFieldInput } from '@/object-record/record-field/meta-types/input/components/RatingFieldInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { SettingsDataModelSetFieldValueEffect } from '@/settings/data-model/fields/preview/components/SettingsDataModelSetFieldValueEffect';
|
||||
import { SettingsDataModelSetPreviewRecordEffect } from '@/settings/data-model/fields/preview/components/SettingsDataModelSetRecordEffect';
|
||||
import { useFieldPreviewValue } from '@/settings/data-model/fields/preview/hooks/useFieldPreviewValue';
|
||||
@ -98,59 +99,65 @@ export const SettingsDataModelFieldPreview = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{previewRecord ? (
|
||||
<SettingsDataModelSetPreviewRecordEffect
|
||||
fieldName={fieldName}
|
||||
record={previewRecord}
|
||||
/>
|
||||
) : (
|
||||
<SettingsDataModelSetFieldValueEffect
|
||||
recordId={recordId}
|
||||
fieldName={fieldName}
|
||||
value={fieldPreviewValue}
|
||||
/>
|
||||
)}
|
||||
<StyledFieldPreview shrink={shrink}>
|
||||
{!!withFieldLabel && (
|
||||
<StyledFieldLabel>
|
||||
<FieldIcon
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
{fieldMetadataItem.label}:
|
||||
</StyledFieldLabel>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'record-field-component-instance-id',
|
||||
}}
|
||||
>
|
||||
{previewRecord ? (
|
||||
<SettingsDataModelSetPreviewRecordEffect
|
||||
fieldName={fieldName}
|
||||
record={previewRecord}
|
||||
/>
|
||||
) : (
|
||||
<SettingsDataModelSetFieldValueEffect
|
||||
recordId={recordId}
|
||||
fieldName={fieldName}
|
||||
value={fieldPreviewValue}
|
||||
/>
|
||||
)}
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId,
|
||||
isLabelIdentifier,
|
||||
fieldDefinition: {
|
||||
type: fieldMetadataItem.type,
|
||||
iconName: 'FieldIcon',
|
||||
fieldMetadataId: fieldMetadataItem.id || '',
|
||||
label: fieldMetadataItem.label,
|
||||
metadata: {
|
||||
fieldName,
|
||||
objectMetadataNameSingular: objectMetadataItem.nameSingular,
|
||||
relationObjectMetadataNameSingular:
|
||||
relationObjectMetadataItem?.nameSingular,
|
||||
options: fieldMetadataItem.options ?? [],
|
||||
settings: fieldMetadataItem.settings,
|
||||
},
|
||||
defaultValue: fieldMetadataItem.defaultValue,
|
||||
},
|
||||
hotkeyScope: 'field-preview',
|
||||
}}
|
||||
>
|
||||
{fieldMetadataItem.type === FieldMetadataType.BOOLEAN ? (
|
||||
<BooleanFieldInput readonly />
|
||||
) : fieldMetadataItem.type === FieldMetadataType.RATING ? (
|
||||
<RatingFieldInput readonly />
|
||||
) : (
|
||||
<FieldDisplay />
|
||||
<StyledFieldPreview shrink={shrink}>
|
||||
{!!withFieldLabel && (
|
||||
<StyledFieldLabel>
|
||||
<FieldIcon
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
{fieldMetadataItem.label}:
|
||||
</StyledFieldLabel>
|
||||
)}
|
||||
</FieldContext.Provider>
|
||||
</StyledFieldPreview>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId,
|
||||
isLabelIdentifier,
|
||||
fieldDefinition: {
|
||||
type: fieldMetadataItem.type,
|
||||
iconName: 'FieldIcon',
|
||||
fieldMetadataId: fieldMetadataItem.id || '',
|
||||
label: fieldMetadataItem.label,
|
||||
metadata: {
|
||||
fieldName,
|
||||
objectMetadataNameSingular: objectMetadataItem.nameSingular,
|
||||
relationObjectMetadataNameSingular:
|
||||
relationObjectMetadataItem?.nameSingular,
|
||||
options: fieldMetadataItem.options ?? [],
|
||||
settings: fieldMetadataItem.settings,
|
||||
},
|
||||
defaultValue: fieldMetadataItem.defaultValue,
|
||||
},
|
||||
hotkeyScope: 'field-preview',
|
||||
}}
|
||||
>
|
||||
{fieldMetadataItem.type === FieldMetadataType.BOOLEAN ? (
|
||||
<BooleanFieldInput readonly />
|
||||
) : fieldMetadataItem.type === FieldMetadataType.RATING ? (
|
||||
<RatingFieldInput readonly />
|
||||
) : (
|
||||
<FieldDisplay />
|
||||
)}
|
||||
</FieldContext.Provider>
|
||||
</StyledFieldPreview>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -8,8 +8,6 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
|
||||
import { CurrencyPickerHotkeyScope } from '../types/CurrencyPickerHotkeyScope';
|
||||
|
||||
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
|
||||
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
||||
import { CurrencyPickerDropdownSelect } from './CurrencyPickerDropdownSelect';
|
||||
|
||||
const StyledDropdownButtonContainer = styled.div`
|
||||
@ -69,10 +67,6 @@ export const CurrencyPickerDropdownButton = ({
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
const { toggleClickOutsideListener } = useClickOutsideListener(
|
||||
TableHotkeyScope.CellEditMode,
|
||||
);
|
||||
|
||||
const currency = currencies.find(({ value }) => value === valueCode);
|
||||
|
||||
const currencyCode = currency?.value ?? CurrencyCode.USD;
|
||||
@ -98,8 +92,6 @@ export const CurrencyPickerDropdownButton = ({
|
||||
}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownOffset={{ x: 0, y: 4 }}
|
||||
onOpen={() => toggleClickOutsideListener(false)}
|
||||
onClose={() => toggleClickOutsideListener(true)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,6 +5,7 @@ import { useRecoilCallback } from 'recoil';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import {
|
||||
RecordFieldValueSelectorContextProvider,
|
||||
useSetRecordValue,
|
||||
@ -124,26 +125,32 @@ export const getFieldDecorator =
|
||||
});
|
||||
|
||||
return (
|
||||
<RecordFieldValueSelectorContextProvider>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: record.id,
|
||||
isLabelIdentifier,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
position: 0,
|
||||
objectMetadataItem,
|
||||
}),
|
||||
hotkeyScope: 'hotkey-scope',
|
||||
}}
|
||||
>
|
||||
<RecordMockSetterEffect
|
||||
companies={companies}
|
||||
people={people}
|
||||
tasks={tasks}
|
||||
/>
|
||||
<Story />
|
||||
</FieldContext.Provider>
|
||||
</RecordFieldValueSelectorContextProvider>
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'record-field-component-instance-id',
|
||||
}}
|
||||
>
|
||||
<RecordFieldValueSelectorContextProvider>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId: record.id,
|
||||
isLabelIdentifier,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
position: 0,
|
||||
objectMetadataItem,
|
||||
}),
|
||||
hotkeyScope: 'hotkey-scope',
|
||||
}}
|
||||
>
|
||||
<RecordMockSetterEffect
|
||||
companies={companies}
|
||||
people={people}
|
||||
tasks={tasks}
|
||||
/>
|
||||
<Story />
|
||||
</FieldContext.Provider>
|
||||
</RecordFieldValueSelectorContextProvider>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -46,7 +46,6 @@ export const useMockFieldContext = ({
|
||||
key={objectRecordId + fieldMetadataItem.id}
|
||||
value={{
|
||||
recordId: objectRecordId,
|
||||
recoilScopeId: objectRecordId + fieldMetadataItem.id,
|
||||
isLabelIdentifier,
|
||||
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
|
||||
field: fieldMetadataItem,
|
||||
|
||||
@ -361,7 +361,7 @@ const StyledButton = styled('button', {
|
||||
const StyledButtonWrapper = styled.div<
|
||||
Pick<
|
||||
ButtonProps,
|
||||
'isLoading' | 'variant' | 'accent' | 'inverted' | 'disabled'
|
||||
'isLoading' | 'variant' | 'accent' | 'inverted' | 'disabled' | 'fullWidth'
|
||||
>
|
||||
>`
|
||||
${({ theme, variant, accent, inverted, disabled }) => css`
|
||||
@ -409,7 +409,9 @@ const StyledButtonWrapper = styled.div<
|
||||
|
||||
max-width: ${({ isLoading, theme }) =>
|
||||
isLoading ? `calc(100% - ${theme.spacing(8)})` : 'none'};
|
||||
|
||||
position: relative;
|
||||
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
|
||||
`;
|
||||
|
||||
export const Button = ({
|
||||
@ -445,6 +447,7 @@ export const Button = ({
|
||||
accent={accent}
|
||||
inverted={inverted}
|
||||
disabled={soon || disabled}
|
||||
fullWidth={fullWidth}
|
||||
>
|
||||
{(isLoading || Icon) && (
|
||||
<ButtonIcon Icon={Icon} isLoading={!!isLoading} />
|
||||
|
||||
Reference in New Issue
Block a user