[DASHBOARDS] Tab duplication (#15906)

Closes https://github.com/twentyhq/core-team-issues/issues/1878

- Allow tab duplication
- Add autofocus on the tiltle input upon the creation and duplication of
tabs and the creation of widgets
- Fix a bug where cancelling the edition while on a new tab, by
resetting the active tab id if it's not in the persisted tabs


https://github.com/user-attachments/assets/a4dc2f0b-1a56-406a-bde7-cca17f9cdbc1
This commit is contained in:
Raphaël Bosi
2025-11-19 10:57:37 +01:00
committed by GitHub
parent d4e85376ea
commit 5b52ac992e
14 changed files with 368 additions and 36 deletions
@@ -3,6 +3,7 @@ import { useRef, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { useRegisterInputEvents } from '@/object-record/record-field/ui/meta-types/input/hooks/useRegisterInputEvents';
import { TitleInputAutoOpenEffect } from '@/ui/input/components/TitleInputAutoOpenEffect';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks/useRemoveFocusItemFromFocusStackById';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
@@ -24,6 +25,8 @@ type InputProps = {
export type TitleInputProps = {
disabled?: boolean;
shouldOpen?: boolean;
onOpen?: () => void;
} & InputProps;
const StyledDiv = styled.div<{
@@ -142,6 +145,8 @@ export const TitleInput = ({
onClickOutside,
onTab,
onShiftTab,
shouldOpen,
onOpen,
}: TitleInputProps) => {
const [isOpened, setIsOpened] = useState(false);
@@ -149,6 +154,14 @@ export const TitleInput = ({
return (
<>
<TitleInputAutoOpenEffect
shouldOpen={shouldOpen}
isOpened={isOpened}
disabled={disabled}
instanceId={instanceId}
onOpen={onOpen}
setIsOpened={setIsOpened}
/>
{isOpened ? (
<Input
instanceId={instanceId}
@@ -0,0 +1,52 @@
import { useEffect } from 'react';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { isDefined } from 'twenty-shared/utils';
type TitleInputAutoOpenEffectProps = {
shouldOpen?: boolean;
isOpened: boolean;
disabled?: boolean;
instanceId: string;
onOpen?: () => void;
setIsOpened: (isOpened: boolean) => void;
};
export const TitleInputAutoOpenEffect = ({
shouldOpen,
isOpened,
disabled,
instanceId,
onOpen,
setIsOpened,
}: TitleInputAutoOpenEffectProps) => {
const { pushFocusItemToFocusStack } = usePushFocusItemToFocusStack();
useEffect(() => {
if (isDefined(shouldOpen) && shouldOpen && !isOpened && !disabled) {
setIsOpened(true);
pushFocusItemToFocusStack({
focusId: instanceId,
component: {
type: FocusComponentType.TEXT_INPUT,
instanceId: instanceId,
},
globalHotkeysConfig: {
enableGlobalHotkeysConflictingWithKeyboard: false,
},
});
onOpen?.();
}
}, [
shouldOpen,
isOpened,
disabled,
instanceId,
pushFocusItemToFocusStack,
onOpen,
setIsOpened,
]);
return null;
};