Error invalid link (#10288)
Don't have access to push on https://github.com/twentyhq/twenty/pull/9942, so close it and open new PR here <img width="244" alt="Screenshot 2025-02-18 at 11 09 39" src="https://github.com/user-attachments/assets/4bc1b436-147a-4d17-88c8-2aff0fffd06a" /> <img width="246" alt="Screenshot 2025-02-18 at 11 09 51" src="https://github.com/user-attachments/assets/3d7b2972-ab7e-4e3b-a177-658325a3bb70" /> Ok for RecordInlineCell / RecordTableCell and EmailsFieldInput / LinksFieldInput. I think it's too complex for a so small issue (agree with you khuddite) closes https://github.com/twentyhq/twenty/issues/9778 on top of https://github.com/twentyhq/twenty/pull/9942 from @khuddite --------- Co-authored-by: khuddite <khuddite@gmail.com> Co-authored-by: khuddite <62555977+khuddite@users.noreply.github.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -14,6 +14,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
|
||||
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 { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
@ -113,7 +114,13 @@ export const CalendarEventDetails = ({
|
||||
maxWidth: 300,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell readonly />
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `${calendarEvent.id}-${fieldName}`,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell readonly />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StyledPropertyBox>
|
||||
));
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
RecordUpdateHook,
|
||||
RecordUpdateHookParams,
|
||||
} from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
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';
|
||||
@ -61,7 +62,13 @@ export const RecordBoardCardBody = ({
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `board-card-${recordId}-${fieldDefinition.fieldMetadataId}`,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
</StopPropagationContainer>
|
||||
))}
|
||||
|
||||
@ -17,7 +17,6 @@ import { isFieldRelationToOneObject } from '@/object-record/record-field/types/g
|
||||
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
|
||||
|
||||
import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray';
|
||||
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
|
||||
@ -72,114 +71,108 @@ export const FieldInput = ({
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
return (
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: recordFieldInputdId,
|
||||
}}
|
||||
<RecordFieldInputScope
|
||||
recordFieldInputScopeId={getScopeIdFromComponentId(recordFieldInputdId)}
|
||||
>
|
||||
<RecordFieldInputScope
|
||||
recordFieldInputScopeId={getScopeIdFromComponentId(recordFieldInputdId)}
|
||||
>
|
||||
{isFieldRelationToOneObject(fieldDefinition) ? (
|
||||
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldRelationFromManyObjects(fieldDefinition) ? (
|
||||
<RelationFromManyFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldPhones(fieldDefinition) ? (
|
||||
<PhonesFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldText(fieldDefinition) ? (
|
||||
<TextFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldEmails(fieldDefinition) ? (
|
||||
<EmailsFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldFullName(fieldDefinition) ? (
|
||||
<FullNameFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldDateTime(fieldDefinition) ? (
|
||||
<DateTimeFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onClear={onSubmit}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
) : isFieldDate(fieldDefinition) ? (
|
||||
<DateFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onClear={onSubmit}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
) : isFieldNumber(fieldDefinition) ? (
|
||||
<NumberFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldLinks(fieldDefinition) ? (
|
||||
<LinksFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldCurrency(fieldDefinition) ? (
|
||||
<CurrencyFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldBoolean(fieldDefinition) ? (
|
||||
<BooleanFieldInput onSubmit={onSubmit} readonly={isReadOnly} />
|
||||
) : isFieldRating(fieldDefinition) ? (
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldSelect(fieldDefinition) ? (
|
||||
<SelectFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldMultiSelect(fieldDefinition) ? (
|
||||
<MultiSelectFieldInput onCancel={onCancel} />
|
||||
) : isFieldAddress(fieldDefinition) ? (
|
||||
<AddressFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldRawJson(fieldDefinition) ? (
|
||||
<RawJsonFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldArray(fieldDefinition) ? (
|
||||
<ArrayFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RecordFieldInputScope>
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
{isFieldRelationToOneObject(fieldDefinition) ? (
|
||||
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldRelationFromManyObjects(fieldDefinition) ? (
|
||||
<RelationFromManyFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldPhones(fieldDefinition) ? (
|
||||
<PhonesFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldText(fieldDefinition) ? (
|
||||
<TextFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldEmails(fieldDefinition) ? (
|
||||
<EmailsFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldFullName(fieldDefinition) ? (
|
||||
<FullNameFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldDateTime(fieldDefinition) ? (
|
||||
<DateTimeFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onClear={onSubmit}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
) : isFieldDate(fieldDefinition) ? (
|
||||
<DateFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onClear={onSubmit}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
) : isFieldNumber(fieldDefinition) ? (
|
||||
<NumberFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldLinks(fieldDefinition) ? (
|
||||
<LinksFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : isFieldCurrency(fieldDefinition) ? (
|
||||
<CurrencyFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldBoolean(fieldDefinition) ? (
|
||||
<BooleanFieldInput onSubmit={onSubmit} readonly={isReadOnly} />
|
||||
) : isFieldRating(fieldDefinition) ? (
|
||||
<RatingFieldInput onSubmit={onSubmit} />
|
||||
) : isFieldSelect(fieldDefinition) ? (
|
||||
<SelectFieldInput onSubmit={onSubmit} onCancel={onCancel} />
|
||||
) : isFieldMultiSelect(fieldDefinition) ? (
|
||||
<MultiSelectFieldInput onCancel={onCancel} />
|
||||
) : isFieldAddress(fieldDefinition) ? (
|
||||
<AddressFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldRawJson(fieldDefinition) ? (
|
||||
<RawJsonFieldInput
|
||||
onEnter={onEnter}
|
||||
onEscape={onEscape}
|
||||
onClickOutside={onClickOutside}
|
||||
onTab={onTab}
|
||||
onShiftTab={onShiftTab}
|
||||
/>
|
||||
) : isFieldArray(fieldDefinition) ? (
|
||||
<ArrayFieldInput
|
||||
onCancel={onCancel}
|
||||
onClickOutside={(event) => onClickOutside?.(() => {}, event)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</RecordFieldInputScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { useEmailsField } from '@/object-record/record-field/meta-types/hooks/useEmailsField';
|
||||
import { EmailsFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/EmailsFieldMenuItem';
|
||||
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
|
||||
import { emailSchema } from '@/object-record/record-field/validation-schemas/emailSchema';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -44,6 +46,14 @@ export const EmailsFieldInput = ({
|
||||
|
||||
const isPrimaryEmail = (index: number) => index === 0 && emails?.length > 1;
|
||||
|
||||
const setIsFieldInError = useSetRecoilComponentStateV2(
|
||||
recordFieldInputIsFieldInErrorComponentState,
|
||||
);
|
||||
|
||||
const handleError = (hasError: boolean, values: any[]) => {
|
||||
setIsFieldInError(hasError && values.length === 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<MultiItemFieldInput
|
||||
items={emails}
|
||||
@ -70,6 +80,7 @@ export const EmailsFieldInput = ({
|
||||
onDelete={handleDelete}
|
||||
/>
|
||||
)}
|
||||
onError={handleError}
|
||||
hotkeyScope={hotkeyScope}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { useLinksField } from '@/object-record/record-field/meta-types/hooks/useLinksField';
|
||||
import { LinksFieldMenuItem } from '@/object-record/record-field/meta-types/input/components/LinksFieldMenuItem';
|
||||
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { useMemo } from 'react';
|
||||
import { absoluteUrlSchema, isDefined } from 'twenty-shared';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@ -47,6 +49,14 @@ export const LinksFieldInput = ({
|
||||
|
||||
const isPrimaryLink = (index: number) => index === 0 && links?.length > 1;
|
||||
|
||||
const setIsFieldInError = useSetRecoilComponentStateV2(
|
||||
recordFieldInputIsFieldInErrorComponentState,
|
||||
);
|
||||
|
||||
const handleError = (hasError: boolean, values: any[]) => {
|
||||
setIsFieldInError(hasError && values.length === 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<MultiItemFieldInput
|
||||
items={links}
|
||||
@ -59,6 +69,7 @@ export const LinksFieldInput = ({
|
||||
isValid: absoluteUrlSchema.safeParse(input).success,
|
||||
errorMessage: '',
|
||||
})}
|
||||
onError={handleError}
|
||||
formatInput={(input) => ({ url: input, label: '' })}
|
||||
renderItem={({
|
||||
value: link,
|
||||
|
||||
@ -20,6 +20,13 @@ const StyledInput = styled.input<{
|
||||
border-radius: 4px;
|
||||
border: 1px solid ${theme.border.color.medium};
|
||||
`}
|
||||
|
||||
${({ hasError, hasItem, theme }) =>
|
||||
hasError &&
|
||||
hasItem &&
|
||||
css`
|
||||
border: 1px solid ${theme.border.color.danger};
|
||||
`}
|
||||
|
||||
box-sizing: border-box;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
|
||||
@ -36,6 +36,7 @@ type MultiItemFieldInputProps<T> = {
|
||||
fieldMetadataType: FieldMetadataType;
|
||||
renderInput?: MultiItemBaseInputProps['renderInput'];
|
||||
onClickOutside?: (event: MouseEvent | TouchEvent) => void;
|
||||
onError?: (hasError: boolean, values: any[]) => void;
|
||||
};
|
||||
|
||||
// Todo: the API of this component does not look healthy: we have renderInput, renderItem, formatInput, ...
|
||||
@ -53,6 +54,7 @@ export const MultiItemFieldInput = <T,>({
|
||||
fieldMetadataType,
|
||||
renderInput,
|
||||
onClickOutside,
|
||||
onError,
|
||||
}: MultiItemFieldInputProps<T>) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const handleDropdownClose = () => {
|
||||
@ -85,6 +87,7 @@ export const MultiItemFieldInput = <T,>({
|
||||
setErrorData(
|
||||
errorData.isValid ? errorData : { isValid: true, errorMessage: '' },
|
||||
);
|
||||
onError?.(false, items);
|
||||
};
|
||||
|
||||
const handleAddButtonClick = () => {
|
||||
@ -123,6 +126,7 @@ export const MultiItemFieldInput = <T,>({
|
||||
if (validateInput !== undefined) {
|
||||
const validationData = validateInput(inputValue) ?? { isValid: true };
|
||||
if (!validationData.isValid) {
|
||||
onError?.(true, items);
|
||||
setErrorData(validationData);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
|
||||
|
||||
export const recordFieldInputIsFieldInErrorComponentState =
|
||||
createComponentStateV2<boolean>({
|
||||
key: 'recordFieldInputIsFieldInErrorComponentState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: RecordFieldComponentInstanceContext,
|
||||
});
|
||||
@ -1,9 +1,11 @@
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
@ -63,6 +65,10 @@ export const RecordInlineCellEditMode = ({
|
||||
},
|
||||
};
|
||||
|
||||
const isFieldInError = useRecoilComponentValueV2(
|
||||
recordFieldInputIsFieldInErrorComponentState,
|
||||
);
|
||||
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
placement: isCentered ? 'bottom' : 'bottom-start',
|
||||
middleware: [
|
||||
@ -93,6 +99,7 @@ export const RecordInlineCellEditMode = ({
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
borderRadius="sm"
|
||||
hasDangerBorder={isFieldInError}
|
||||
>
|
||||
{children}
|
||||
</OverlayContainer>,
|
||||
|
||||
@ -6,6 +6,7 @@ import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadat
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
|
||||
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 { PropertyBoxSkeletonLoader } from '@/object-record/record-inline-cell/property-box/components/PropertyBoxSkeletonLoader';
|
||||
@ -141,7 +142,13 @@ export const FieldsCard = ({
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell loading={recordLoading} />
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `${objectRecordId}-${fieldMetadataItem.id}`,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell loading={recordLoading} />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
))}
|
||||
</>
|
||||
|
||||
@ -28,6 +28,7 @@ import {
|
||||
} from '@/object-record/record-field/contexts/FieldContext';
|
||||
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
|
||||
import { usePersistField } from '@/object-record/record-field/hooks/usePersistField';
|
||||
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
|
||||
import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
|
||||
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
|
||||
@ -280,7 +281,13 @@ export const RecordDetailRelationRecordsListItem = ({
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: `${relationRecord.id}-${fieldMetadataItem.id}`,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
),
|
||||
)}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
@ -35,6 +37,10 @@ export const RecordTableCellEditMode = ({
|
||||
}: RecordTableCellEditModeProps) => {
|
||||
const { recordId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const isFieldInError = useRecoilComponentValueV2(
|
||||
recordFieldInputIsFieldInErrorComponentState,
|
||||
);
|
||||
|
||||
const instanceId = getRecordFieldInputId(
|
||||
recordId,
|
||||
fieldDefinition?.metadata?.fieldName,
|
||||
@ -84,6 +90,7 @@ export const RecordTableCellEditMode = ({
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
borderRadius="sm"
|
||||
hasDangerBorder={isFieldInError}
|
||||
>
|
||||
{children}
|
||||
</OverlayContainer>
|
||||
|
||||
@ -2,6 +2,7 @@ import { ReactNode, useContext } from 'react';
|
||||
|
||||
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 { 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';
|
||||
@ -87,7 +88,11 @@ export const RecordTableCellFieldContextWrapper = ({
|
||||
displayedMaxRows: 1,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
<RecordFieldComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordId + columnDefinition.label }}
|
||||
>
|
||||
{children}
|
||||
</RecordFieldComponentInstanceContext.Provider>
|
||||
</FieldContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@ import styled from '@emotion/styled';
|
||||
// eslint-disable-next-line @nx/workspace-styled-components-prefixed-with-styled
|
||||
export const OverlayContainer = styled.div<{
|
||||
borderRadius?: 'sm' | 'md';
|
||||
hasDangerBorder?: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@ -14,7 +15,9 @@ export const OverlayContainer = styled.div<{
|
||||
theme.border.radius[borderRadius ?? 'md']};
|
||||
|
||||
background: ${({ theme }) => theme.background.transparent.primary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border: 1px solid
|
||||
${({ theme, hasDangerBorder }) =>
|
||||
theme.border.color[hasDangerBorder ? 'danger' : 'medium']};
|
||||
box-shadow: ${({ theme }) => theme.boxShadow.strong};
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
Reference in New Issue
Block a user