Open showpage on workflow creation (#9714)
- Created an new component state `isRecordEditableNameRenamingComponentState` - Updated `useCreateNewTableRecord` to open the ShowPage on workflow creation - Refactored `RecordEditableName` and its components to remove the useEffect (This was causing the recordName state to be updated after the focus on `NavigationDrawerInput`, but we want the text so be selected after the update). - Introduced a new component `EditableBreadcrumbItem` - Created an autosizing text input: This is done by a hack using a span inside a div and the input position is set to absolute and takes the size of the div. There are two problems that I didn't manage to fix: If the text is too long, the title overflows, and the letter spacing is different between the span and the input creating a small offset. https://github.com/user-attachments/assets/4aa1e177-7458-4691-b0c8-96567b482206 New text input component: https://github.com/user-attachments/assets/94565546-fe2b-457d-a1d8-907007e0e2ce
This commit is contained in:
@ -1,50 +1,49 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { EditableBreadcrumbItem } from '@/ui/navigation/bread-crumb/components/EditableBreadcrumbItem';
|
||||
import styled from '@emotion/styled';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
const StyledEditableTitleContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: hidden;
|
||||
`;
|
||||
|
||||
const StyledEditableTitlePrefix = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
line-height: 24px;
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
flex-direction: row;
|
||||
padding: ${({ theme }) => theme.spacing(0.75)};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(0.75)};
|
||||
`;
|
||||
|
||||
export const RecordEditableName = ({
|
||||
export const ObjectRecordShowPageBreadcrumb = ({
|
||||
objectNameSingular,
|
||||
objectRecordId,
|
||||
objectLabelPlural,
|
||||
labelIdentifierFieldMetadataItem,
|
||||
}: {
|
||||
objectNameSingular: string;
|
||||
objectRecordId: string;
|
||||
objectLabelPlural: string;
|
||||
labelIdentifierFieldMetadataItem?: FieldMetadataItem;
|
||||
}) => {
|
||||
const [isRenaming, setIsRenaming] = useState(false);
|
||||
const { record, loading } = useFindOneRecord({
|
||||
objectNameSingular,
|
||||
objectRecordId,
|
||||
recordGqlFields: {
|
||||
name: true,
|
||||
[labelIdentifierFieldMetadataItem?.name ?? 'name']: true,
|
||||
},
|
||||
});
|
||||
|
||||
const [recordName, setRecordName] = useState(record?.name);
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular,
|
||||
recordGqlFields: {
|
||||
name: true,
|
||||
[labelIdentifierFieldMetadataItem?.name ?? 'name']: true,
|
||||
},
|
||||
});
|
||||
|
||||
@ -55,18 +54,8 @@ export const RecordEditableName = ({
|
||||
name: value,
|
||||
},
|
||||
});
|
||||
setIsRenaming(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setRecordName(record?.name);
|
||||
setIsRenaming(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setRecordName(record?.name);
|
||||
}, [record?.name]);
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
@ -77,24 +66,13 @@ export const RecordEditableName = ({
|
||||
{capitalize(objectLabelPlural)}
|
||||
<span>{' / '}</span>
|
||||
</StyledEditableTitlePrefix>
|
||||
{isRenaming ? (
|
||||
<NavigationDrawerInput
|
||||
value={recordName}
|
||||
onChange={setRecordName}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
onClickOutside={handleCancel}
|
||||
hotkeyScope="favorites-folder-input"
|
||||
/>
|
||||
) : (
|
||||
<NavigationDrawerItem
|
||||
label={recordName}
|
||||
onClick={() => setIsRenaming(true)}
|
||||
rightOptions={undefined}
|
||||
className="navigation-drawer-item"
|
||||
active
|
||||
/>
|
||||
)}
|
||||
<EditableBreadcrumbItem
|
||||
defaultValue={record?.name ?? ''}
|
||||
noValuePlaceholder={labelIdentifierFieldMetadataItem?.label ?? 'Name'}
|
||||
placeholder={labelIdentifierFieldMetadataItem?.label ?? 'Name'}
|
||||
onSubmit={handleSubmit}
|
||||
hotkeyScope="editable-breadcrumb-item"
|
||||
/>
|
||||
</StyledEditableTitleContainer>
|
||||
);
|
||||
};
|
||||
@ -5,7 +5,10 @@ import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-ce
|
||||
import { useSelectedTableCellEditMode } from '@/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode';
|
||||
import { recordTablePendingRecordIdByGroupComponentFamilyState } from '@/object-record/record-table/states/recordTablePendingRecordIdByGroupComponentFamilyState';
|
||||
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
|
||||
import { isUpdatingRecordEditableNameState } from '@/object-record/states/isUpdatingRecordEditableName';
|
||||
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
|
||||
import { shouldRedirectToShowPageOnCreation } from '@/object-record/utils/shouldRedirectToShowPageOnCreation';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
@ -14,6 +17,7 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { useNavigateApp } from '~/hooks/useNavigateApp';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useCreateNewTableRecord = ({
|
||||
@ -54,30 +58,69 @@ export const useCreateNewTableRecord = ({
|
||||
shouldMatchRootQueryFilter: true,
|
||||
});
|
||||
|
||||
const createNewTableRecord = async () => {
|
||||
const recordId = v4();
|
||||
const navigate = useNavigateApp();
|
||||
|
||||
if (isCommandMenuV2Enabled) {
|
||||
await createOneRecord({ id: recordId });
|
||||
const createNewTableRecord = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async () => {
|
||||
const recordId = v4();
|
||||
|
||||
openRecordInCommandMenu(recordId, objectMetadataItem.nameSingular);
|
||||
return;
|
||||
}
|
||||
if (isCommandMenuV2Enabled) {
|
||||
// TODO: Generalize this behaviour, there will be a view setting to specify
|
||||
// if the new record should be displayed in the side panel or on the record page
|
||||
if (
|
||||
shouldRedirectToShowPageOnCreation(objectMetadataItem.nameSingular)
|
||||
) {
|
||||
await createOneRecord({
|
||||
id: recordId,
|
||||
name: 'Untitled',
|
||||
});
|
||||
|
||||
setPendingRecordId(recordId);
|
||||
setSelectedTableCellEditMode(-1, 0);
|
||||
setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes);
|
||||
navigate(AppPath.RecordShowPage, {
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
objectRecordId: recordId,
|
||||
});
|
||||
|
||||
if (isDefined(objectMetadataItem.labelIdentifierFieldMetadataId)) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(
|
||||
getDropdownFocusIdForRecordField(
|
||||
recordId,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
'table-cell',
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
set(isUpdatingRecordEditableNameState, true);
|
||||
return;
|
||||
}
|
||||
|
||||
await createOneRecord({ id: recordId });
|
||||
openRecordInCommandMenu(recordId, objectMetadataItem.nameSingular);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setPendingRecordId(recordId);
|
||||
setSelectedTableCellEditMode(-1, 0);
|
||||
setHotkeyScope(
|
||||
DEFAULT_CELL_SCOPE.scope,
|
||||
DEFAULT_CELL_SCOPE.customScopes,
|
||||
);
|
||||
|
||||
if (isDefined(objectMetadataItem.labelIdentifierFieldMetadataId)) {
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(
|
||||
getDropdownFocusIdForRecordField(
|
||||
recordId,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
'table-cell',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
createOneRecord,
|
||||
isCommandMenuV2Enabled,
|
||||
navigate,
|
||||
objectMetadataItem.labelIdentifierFieldMetadataId,
|
||||
objectMetadataItem.nameSingular,
|
||||
openRecordInCommandMenu,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
setHotkeyScope,
|
||||
setPendingRecordId,
|
||||
setSelectedTableCellEditMode,
|
||||
],
|
||||
);
|
||||
|
||||
const createNewTableRecordInGroup = useRecoilCallback(
|
||||
({ set }) =>
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import { createState } from 'twenty-ui';
|
||||
|
||||
export const isUpdatingRecordEditableNameState = createState<boolean>({
|
||||
key: 'isUpdatingRecordEditableNameState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
||||
export const shouldRedirectToShowPageOnCreation = (
|
||||
objectNameSingular: string,
|
||||
) => {
|
||||
if (objectNameSingular === CoreObjectNameSingular.Workflow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
Reference in New Issue
Block a user