diff --git a/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx b/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx
index 62662323becd..b4154a27d2d5 100644
--- a/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx
+++ b/app/client/packages/design-system/ads/src/Icon/Icon.provider.tsx
@@ -1068,6 +1068,10 @@ const ExternalLinkIcon = importRemixIcon(
async () => import("remixicon-react/ExternalLinkLineIcon"),
);
+const InputCursorMoveIcon = importSvg(
+ async () => import("../__assets__/icons/ads/input-cursor-move.svg"),
+);
+
import PlayIconPNG from "../__assets__/icons/control/play-icon.png";
function PlayIconPNGWrapper() {
@@ -1363,6 +1367,7 @@ const ICON_LOOKUP = {
"minimize-v3": MinimizeV3Icon,
"maximize-v3": MaximizeV3Icon,
"workflows-mono": WorkflowsMonochromeIcon,
+ "input-cursor-move": InputCursorMoveIcon,
billing: BillingIcon,
binding: Binding,
book: BookIcon,
diff --git a/app/client/packages/design-system/ads/src/__assets__/icons/ads/input-cursor-move.svg b/app/client/packages/design-system/ads/src/__assets__/icons/ads/input-cursor-move.svg
new file mode 100644
index 000000000000..e05a3c4b656a
--- /dev/null
+++ b/app/client/packages/design-system/ads/src/__assets__/icons/ads/input-cursor-move.svg
@@ -0,0 +1,7 @@
+
diff --git a/app/client/src/IDE/Components/EditableName/EditableName.test.tsx b/app/client/src/IDE/Components/EditableName/EditableName.test.tsx
index 77a4f756555b..6dfadcebe2f6 100644
--- a/app/client/src/IDE/Components/EditableName/EditableName.test.tsx
+++ b/app/client/src/IDE/Components/EditableName/EditableName.test.tsx
@@ -145,15 +145,13 @@ describe("EditableName", () => {
target: { value: invalidTitle },
});
- fireEvent.keyUp(inputElement, KEY_CONFIG.ENTER);
-
expect(getByRole("tooltip")).toBeInTheDocument();
expect(getByRole("tooltip").textContent).toEqual(validationError);
await userEvent.click(document.body);
- expect(getByRole("tooltip").textContent).toEqual("");
+ expect(getByRole("tooltip").textContent).toEqual(validationError);
expect(exitEditing).toHaveBeenCalled();
expect(onNameSave).not.toHaveBeenCalledWith(invalidTitle);
@@ -169,7 +167,6 @@ describe("EditableName", () => {
target: { value: invalidTitle },
});
- fireEvent.keyUp(inputElement, KEY_CONFIG.ENTER);
fireEvent.keyUp(inputElement, KEY_CONFIG.ESC);
expect(getByRole("tooltip")).toBeInTheDocument();
@@ -189,9 +186,8 @@ describe("EditableName", () => {
target: { value: invalidTitle },
});
- fireEvent.keyUp(inputElement, KEY_CONFIG.ENTER);
fireEvent.focusOut(inputElement);
- expect(getByRole("tooltip").textContent).toEqual("");
+ expect(getByRole("tooltip").textContent).toEqual(validationError);
expect(exitEditing).toHaveBeenCalled();
expect(onNameSave).not.toHaveBeenCalledWith(invalidTitle);
});
@@ -201,12 +197,12 @@ describe("EditableName", () => {
const input = getByRole("textbox");
fireEvent.change(input, { target: { value: "" } });
- fireEvent.keyUp(input, KEY_CONFIG.ENTER);
-
- expect(onNameSave).not.toHaveBeenCalledWith("");
expect(getByRole("tooltip")).toHaveTextContent(
"Please enter a valid name",
);
+ fireEvent.keyUp(input, KEY_CONFIG.ENTER);
+
+ expect(onNameSave).not.toHaveBeenCalledWith("");
});
});
});
diff --git a/app/client/src/IDE/Components/EditableName/EditableName.tsx b/app/client/src/IDE/Components/EditableName/EditableName.tsx
index 074c7ca6a829..1f91d827b801 100644
--- a/app/client/src/IDE/Components/EditableName/EditableName.tsx
+++ b/app/client/src/IDE/Components/EditableName/EditableName.tsx
@@ -1,4 +1,10 @@
-import React, { useEffect, useMemo, useRef, useState } from "react";
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
import { Spinner, Text, Tooltip } from "@appsmith/ads";
import { useEventCallback, useEventListener } from "usehooks-ts";
import { usePrevious } from "@mantine/hooks";
@@ -6,10 +12,19 @@ import { useNameEditor } from "./useNameEditor";
interface EditableTextProps {
name: string;
+ /** isLoading true will show a spinner **/
isLoading?: boolean;
+ /** if a valid name is entered, the onNameSave
+ * will be called with the new name */
onNameSave: (name: string) => void;
+ /** Used in conjunction with exit editing to control
+ * this component input editable state */
isEditing: boolean;
+ /** Used in conjunction with exit editing to control this component
+ * input editable state This function will be called when the
+ * user is trying to exit the editing mode **/
exitEditing: () => void;
+ /** Icon is replaced by spinner when isLoading is shown */
icon: React.ReactNode;
inputTestId?: string;
}
@@ -32,30 +47,61 @@ export const EditableName = ({
entityName: name,
});
+ const exitWithoutSaving = useCallback(() => {
+ exitEditing();
+ setEditableName(name);
+ setValidationError(null);
+ }, [exitEditing, name]);
+
+ const validate = useCallback(
+ (name: string) => {
+ const nameError = validateName(name);
+
+ if (nameError === null) {
+ setValidationError(null);
+ } else {
+ setValidationError(nameError);
+ }
+
+ return nameError;
+ },
+ [validateName],
+ );
+
+ const attemptSave = useCallback(() => {
+ const nameError = validate(editableName);
+
+ if (editableName === name) {
+ exitWithoutSaving();
+ } else if (nameError === null) {
+ exitEditing();
+ onNameSave(editableName);
+ }
+ }, [
+ editableName,
+ exitEditing,
+ exitWithoutSaving,
+ name,
+ onNameSave,
+ validate,
+ ]);
+
const handleKeyUp = useEventCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Enter") {
- const nameError = validateName(editableName);
-
- if (nameError === null) {
- exitEditing();
- onNameSave(editableName);
- } else {
- setValidationError(nameError);
- }
+ attemptSave();
} else if (e.key === "Escape") {
- exitEditing();
- setEditableName(name);
- setValidationError(null);
- } else {
- setValidationError(null);
+ exitWithoutSaving();
}
},
);
const handleTitleChange = useEventCallback(
(e: React.ChangeEvent) => {
- setEditableName(normalizeName(e.target.value));
+ const value = normalizeName(e.target.value);
+
+ setEditableName(value);
+ validate(value);
},
);
@@ -67,23 +113,14 @@ export const EditableName = ({
autoFocus: true,
style: { paddingTop: 0, paddingBottom: 0, left: -1, top: -1 },
}),
- [handleKeyUp, handleTitleChange],
+ [handleKeyUp, handleTitleChange, inputTestId],
);
useEventListener(
"focusout",
function handleFocusOut() {
if (isEditing) {
- const nameError = validateName(editableName);
-
- exitEditing();
-
- if (nameError === null) {
- onNameSave(editableName);
- } else {
- setEditableName(name);
- setValidationError(null);
- }
+ attemptSave();
}
},
inputRef,
@@ -120,9 +157,9 @@ export const EditableName = ({
{editableName}
diff --git a/app/client/src/IDE/Components/EditableName/RenameMenuItem.tsx b/app/client/src/IDE/Components/EditableName/RenameMenuItem.tsx
new file mode 100644
index 000000000000..3c7d6049841a
--- /dev/null
+++ b/app/client/src/IDE/Components/EditableName/RenameMenuItem.tsx
@@ -0,0 +1,30 @@
+import React, { useCallback } from "react";
+import { MenuItem } from "@appsmith/ads";
+import { useDispatch } from "react-redux";
+import { setRenameEntity } from "actions/ideActions";
+
+interface Props {
+ disabled?: boolean;
+ entityId: string;
+}
+
+export const RenameMenuItem = ({ disabled, entityId }: Props) => {
+ const dispatch = useDispatch();
+
+ const setRename = useCallback(() => {
+ // We add a delay to avoid having the focus stuck in the menu trigger
+ setTimeout(() => {
+ dispatch(setRenameEntity(entityId));
+ }, 100);
+ }, [dispatch, entityId]);
+
+ return (
+
+ );
+};
diff --git a/app/client/src/IDE/Components/EditableName/index.ts b/app/client/src/IDE/Components/EditableName/index.ts
index 875359b04b0d..f714786de369 100644
--- a/app/client/src/IDE/Components/EditableName/index.ts
+++ b/app/client/src/IDE/Components/EditableName/index.ts
@@ -1 +1,3 @@
export { EditableName } from "./EditableName";
+export { RenameMenuItem } from "./RenameMenuItem";
+export { useIsRenaming } from "./useIsRenaming";
diff --git a/app/client/src/IDE/Components/EditableName/useIsRenaming.ts b/app/client/src/IDE/Components/EditableName/useIsRenaming.ts
new file mode 100644
index 000000000000..85bac3e91193
--- /dev/null
+++ b/app/client/src/IDE/Components/EditableName/useIsRenaming.ts
@@ -0,0 +1,35 @@
+import { useDispatch, useSelector } from "react-redux";
+import { getIsRenaming } from "selectors/ideSelectors";
+import { useCallback, useEffect, useState } from "react";
+import { setRenameEntity } from "actions/ideActions";
+
+export const useIsRenaming = (id: string) => {
+ const dispatch = useDispatch();
+ const [isEditing, setIsEditing] = useState(false);
+
+ const isEditingViaExternal = useSelector(getIsRenaming(id));
+
+ useEffect(
+ function onExternalEditEvent() {
+ if (isEditingViaExternal) {
+ setIsEditing(true);
+ }
+
+ return () => {
+ setIsEditing(false);
+ };
+ },
+ [isEditingViaExternal],
+ );
+
+ const enterEditMode = useCallback(() => {
+ setIsEditing(true);
+ }, []);
+
+ const exitEditMode = useCallback(() => {
+ dispatch(setRenameEntity(""));
+ setIsEditing(false);
+ }, [dispatch]);
+
+ return { isEditing, enterEditMode, exitEditMode };
+};
diff --git a/app/client/src/IDE/Components/EditableName/useNameEditor.ts b/app/client/src/IDE/Components/EditableName/useNameEditor.ts
index 40e7bf1d18d0..cae166cab481 100644
--- a/app/client/src/IDE/Components/EditableName/useNameEditor.ts
+++ b/app/client/src/IDE/Components/EditableName/useNameEditor.ts
@@ -6,8 +6,8 @@ import {
import { shallowEqual, useSelector } from "react-redux";
import type { AppState } from "ee/reducers";
import { getUsedActionNames } from "selectors/actionSelectors";
-import { useEventCallback } from "usehooks-ts";
import { isNameValid, removeSpecialChars } from "utils/helpers";
+import { useCallback } from "react";
interface UseNameEditorProps {
entityName: string;
@@ -25,15 +25,18 @@ export function useNameEditor(props: UseNameEditorProps) {
shallowEqual,
);
- const validateName = useEventCallback((name: string): string | null => {
- if (!name || name.trim().length === 0) {
- return createMessage(ACTION_INVALID_NAME_ERROR);
- } else if (name !== entityName && !isNameValid(name, usedEntityNames)) {
- return createMessage(nameErrorMessage, name);
- }
+ const validateName = useCallback(
+ (name: string): string | null => {
+ if (!name || name.trim().length === 0) {
+ return createMessage(ACTION_INVALID_NAME_ERROR);
+ } else if (name !== entityName && !isNameValid(name, usedEntityNames)) {
+ return createMessage(nameErrorMessage, name);
+ }
- return null;
- });
+ return null;
+ },
+ [entityName, nameErrorMessage, usedEntityNames],
+ );
return {
validateName,
diff --git a/app/client/src/IDE/index.ts b/app/client/src/IDE/index.ts
index c3e0efa7be72..cc69383fab62 100644
--- a/app/client/src/IDE/index.ts
+++ b/app/client/src/IDE/index.ts
@@ -69,7 +69,11 @@ export { ToolbarSettingsPopover } from "./Components/ToolbarSettingsPopover";
* EditableName is a component that allows the user to edit the name of an entity
* It is used in the IDE for renaming pages, actions, queries, etc.
*/
-export { EditableName } from "./Components/EditableName";
+export {
+ EditableName,
+ RenameMenuItem,
+ useIsRenaming,
+} from "./Components/EditableName";
/* ====================================================
**** Interfaces ****
diff --git a/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx b/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx
index 576ab8106fe2..4fa5020b8da1 100644
--- a/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx
+++ b/app/client/src/PluginActionEditor/components/PluginActionNameEditor.tsx
@@ -1,5 +1,5 @@
import React, { useCallback } from "react";
-import { useSelector } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { usePluginActionContext } from "../PluginActionContext";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers";
@@ -8,11 +8,10 @@ import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { ActionUrlIcon } from "pages/Editor/Explorer/ExplorerIcons";
-import { Text as ADSText, Flex } from "@appsmith/ads";
+import { Flex } from "@appsmith/ads";
import styled from "styled-components";
-import { useBoolean } from "usehooks-ts";
import { noop } from "lodash";
-import { EditableName } from "IDE";
+import { EditableName, useIsRenaming } from "IDE";
export interface SaveActionNameParams {
id: string;
@@ -49,26 +48,16 @@ export const IconContainer = styled.div`
}
`;
-export const Text = styled(ADSText)`
- min-width: 3ch;
- padding: 0 var(--ads-v2-spaces-1);
- font-weight: 500;
-`;
-
const PluginActionNameEditor = ({
saveActionName,
}: PluginActionNameEditorProps) => {
const { action, plugin } = usePluginActionContext();
const isLoading = useSelector(
- (state) => getSavingStatusForActionName(state, action?.id || "").isSaving,
+ (state) => getSavingStatusForActionName(state, action.id).isSaving,
);
- const {
- setFalse: exitEditMode,
- setTrue: enterEditMode,
- value: isEditing,
- } = useBoolean(false);
+ const { enterEditMode, exitEditMode, isEditing } = useIsRenaming(action.id);
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const isChangePermitted = getHasManageActionPermission(
@@ -80,9 +69,11 @@ const PluginActionNameEditor = ({
const handleDoubleClick = isChangePermitted ? enterEditMode : noop;
+ const dispatch = useDispatch();
+
const handleNameSave = useCallback(
(name: string) => {
- saveActionName({ id: action.id, name });
+ dispatch(saveActionName({ id: action.id, name }));
},
[action.id, saveActionName],
);
diff --git a/app/client/src/actions/ideActions.ts b/app/client/src/actions/ideActions.ts
index a2d1809b5abc..fbdf13b00c37 100644
--- a/app/client/src/actions/ideActions.ts
+++ b/app/client/src/actions/ideActions.ts
@@ -61,3 +61,10 @@ export const setListViewActiveState = (payload: boolean) => {
payload,
};
};
+
+export const setRenameEntity = (id: string) => {
+ return {
+ type: ReduxActionTypes.SET_RENAME_ENTITY,
+ payload: id,
+ };
+};
diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx
index c225e29be85d..7e7375ede276 100644
--- a/app/client/src/ce/constants/ReduxActionConstants.tsx
+++ b/app/client/src/ce/constants/ReduxActionConstants.tsx
@@ -500,6 +500,7 @@ const IDEActionTypes = {
CLOSE_QUERY_ACTION_TAB_SUCCESS: "CLOSE_QUERY_ACTION_TAB_SUCCESS",
SET_IS_LIST_VIEW_ACTIVE: "SET_IS_LIST_VIEW_ACTIVE",
OPEN_PLUGIN_ACTION_SETTINGS: "OPEN_PLUGIN_ACTION_SETTINGS",
+ SET_RENAME_ENTITY: "SET_RENAME_ENTITY",
};
const IDEActionErrorTypes = {
diff --git a/app/client/src/ce/pages/Editor/AppPluginActionEditor/components/ToolbarMenu/ToolbarMenu.tsx b/app/client/src/ce/pages/Editor/AppPluginActionEditor/components/ToolbarMenu/ToolbarMenu.tsx
index e321611af6a5..27121fbacdd3 100644
--- a/app/client/src/ce/pages/Editor/AppPluginActionEditor/components/ToolbarMenu/ToolbarMenu.tsx
+++ b/app/client/src/ce/pages/Editor/AppPluginActionEditor/components/ToolbarMenu/ToolbarMenu.tsx
@@ -14,7 +14,7 @@ import { ConvertToModuleCTA } from "../ConvertToModule";
import { Move } from "./Move";
import { Copy } from "./Copy";
import { Delete } from "./Delete";
-import { Rename } from "./Rename";
+import { RenameMenuItem } from "IDE";
export const ToolbarMenu = () => {
const { action } = usePluginActionContext();
@@ -31,11 +31,10 @@ export const ToolbarMenu = () => {
return (
<>
-
+
-
diff --git a/app/client/src/components/editorComponents/EditableText.tsx b/app/client/src/components/editorComponents/EditableText.tsx
index 29b4439871af..a4516918b0c5 100644
--- a/app/client/src/components/editorComponents/EditableText.tsx
+++ b/app/client/src/components/editorComponents/EditableText.tsx
@@ -61,6 +61,7 @@ const EditableTextWrapper = styled.div<{
justify-content: flex-start;
align-items: flex-start;
width: 100%;
+
& .${Classes.EDITABLE_TEXT} {
background: ${(props) =>
props.isEditing && !props.minimal
@@ -73,11 +74,13 @@ const EditableTextWrapper = styled.div<{
max-width: 100%;
overflow: hidden;
display: flex;
+
&:before,
&:after {
display: none;
}
}
+
& div.${Classes.EDITABLE_TEXT_INPUT} {
text-transform: none;
width: 100%;
@@ -100,6 +103,7 @@ const TextContainer = styled.div<{
color: var(--ads-v2-color-fg-emphasis-plus);
display: flex;
align-items: center;
+
&&&& .${Classes.EDITABLE_TEXT} {
& .${Classes.EDITABLE_TEXT_CONTENT} {
&:hover {
@@ -108,6 +112,7 @@ const TextContainer = styled.div<{
}
}
}
+
&&& .${Classes.EDITABLE_TEXT_CONTENT}:hover {
${(props) =>
props.underline
diff --git a/app/client/src/pages/Editor/IDE/EditorTabs/EditableTab.tsx b/app/client/src/pages/Editor/IDE/EditorTabs/EditableTab.tsx
index f44f5bbfb117..672c1bf366c5 100644
--- a/app/client/src/pages/Editor/IDE/EditorTabs/EditableTab.tsx
+++ b/app/client/src/pages/Editor/IDE/EditorTabs/EditableTab.tsx
@@ -4,8 +4,8 @@ import { FileTab } from "IDE/Components/FileTab";
import { type EntityItem } from "ee/entities/IDE/constants";
import { useCurrentEditorState } from "../hooks";
-import { useSelector } from "react-redux";
-import { useBoolean, useEventCallback } from "usehooks-ts";
+import { useDispatch, useSelector } from "react-redux";
+import { useEventCallback } from "usehooks-ts";
import { getIsSavingEntityName } from "ee/selectors/entitiesSelector";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
@@ -14,7 +14,7 @@ import {
saveEntityName,
} from "ee/entities/IDE/utils";
import { noop } from "lodash";
-import { EditableName } from "IDE";
+import { EditableName, useIsRenaming } from "IDE";
import { IconContainer } from "IDE/Components/FileTab/styles";
interface EditableTabProps {
@@ -37,11 +37,7 @@ export function EditableTab(props: EditableTabProps) {
entity,
});
- const {
- setFalse: exitEditMode,
- setTrue: enterEditMode,
- value: isEditing,
- } = useBoolean(false);
+ const { enterEditMode, exitEditMode, isEditing } = useIsRenaming(id);
const isLoading = useSelector((state) =>
getIsSavingEntityName(state, { id, segment, entity }),
@@ -54,9 +50,11 @@ export function EditableTab(props: EditableTabProps) {
const handleDoubleClick = isChangePermitted ? enterEditMode : noop;
+ const dispatch = useDispatch();
+
const handleNameSave = useCallback(
(name: string) => {
- saveEntityName({ params: { id, name }, segment, entity });
+ dispatch(saveEntityName({ params: { id, name }, segment, entity }));
exitEditMode();
},
[entity, exitEditMode, id, segment],
diff --git a/app/client/src/pages/Editor/JSEditor/AppJSEditorContextMenu.tsx b/app/client/src/pages/Editor/JSEditor/AppJSEditorContextMenu.tsx
index 9ec5c9811001..6538da16c979 100644
--- a/app/client/src/pages/Editor/JSEditor/AppJSEditorContextMenu.tsx
+++ b/app/client/src/pages/Editor/JSEditor/AppJSEditorContextMenu.tsx
@@ -12,6 +12,7 @@ import {
CONFIRM_CONTEXT_DELETE,
CONTEXT_MOVE,
createMessage,
+ CONTEXT_RENAME,
} from "ee/constants/messages";
import { getPageListAsOptions } from "ee/selectors/entitiesSelector";
import {
@@ -32,6 +33,7 @@ import {
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import type { JSCollection } from "entities/JSCollection";
+import { setRenameEntity } from "actions/ideActions";
interface AppJSEditorContextMenuProps {
pageId: string;
@@ -56,6 +58,13 @@ export function AppJSEditorContextMenu({
jsCollection?.userPermissions || [],
);
+ const renameJS = useCallback(() => {
+ // We add a delay to avoid having the focus stuck in the menu trigger
+ setTimeout(() => {
+ dispatch(setRenameEntity(jsCollection.id));
+ }, 100);
+ }, []);
+
const copyJSCollectionToPage = useCallback(
(actionId: string, actionName: string, pageId: string) => {
dispatch(
@@ -96,6 +105,14 @@ export function AppJSEditorContextMenu({
const menuPages = useSelector(getPageListAsOptions, equal);
+ const renameOption = {
+ icon: "input-cursor-move" as IconName,
+ value: "rename",
+ onSelect: renameJS,
+ label: createMessage(CONTEXT_RENAME),
+ disabled: !isChangePermitted,
+ };
+
const copyOption = {
icon: "duplicate" as IconName,
value: "copy",
@@ -169,7 +186,7 @@ export function AppJSEditorContextMenu({
className: "t--apiFormDeleteBtn error-menuitem",
};
- const options: ContextMenuOption[] = [];
+ const options: ContextMenuOption[] = [renameOption];
if (isChangePermitted) {
options.push(copyOption);
diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx
index 35771dfa77bd..224f77384916 100644
--- a/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx
+++ b/app/client/src/pages/Editor/JSEditor/JSEditorToolbar/JSObjectNameEditor/JSObjectNameEditor.tsx
@@ -7,7 +7,6 @@ import { getSavingStatusForJSObjectName } from "selectors/actionSelectors";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { Text as ADSText, Flex } from "@appsmith/ads";
import styled from "styled-components";
-import { useBoolean } from "usehooks-ts";
import { noop } from "lodash";
import { useParams } from "react-router";
import type { AppState } from "ee/reducers";
@@ -16,7 +15,7 @@ import {
getPlugin,
} from "ee/selectors/entitiesSelector";
import { JSObjectNameEditor as OldJSObjectNameEditor } from "./old/JSObjectNameEditor";
-import { EditableName } from "IDE";
+import { EditableName, useIsRenaming } from "IDE";
export interface SaveActionNameParams {
id: string;
@@ -86,11 +85,9 @@ export const JSObjectNameEditor = ({
const name = currentJSObjectConfig?.name || "";
- const {
- setFalse: exitEditMode,
- setTrue: enterEditMode,
- value: isEditing,
- } = useBoolean(false);
+ const { enterEditMode, exitEditMode, isEditing } = useIsRenaming(
+ currentJSObjectConfig?.id || "",
+ );
const handleDoubleClick = disabled ? noop : enterEditMode;
diff --git a/app/client/src/reducers/uiReducers/ideReducer.ts b/app/client/src/reducers/uiReducers/ideReducer.ts
index 86384fe393d5..5b6d03c3d1f6 100644
--- a/app/client/src/reducers/uiReducers/ideReducer.ts
+++ b/app/client/src/reducers/uiReducers/ideReducer.ts
@@ -15,6 +15,7 @@ const initialState: IDEState = {
tabs: {},
isListViewActive: false,
showCreateModal: false,
+ renameEntity: "",
ideCanvasSideBySideHover: {
navigated: false,
widgetTypes: [],
@@ -110,6 +111,14 @@ const ideReducer = createImmerReducer(initialState, {
) => {
state.isListViewActive = action.payload;
},
+ [ReduxActionTypes.SET_RENAME_ENTITY]: (
+ state: IDEState,
+ action: {
+ payload: string;
+ },
+ ) => {
+ state.renameEntity = action.payload;
+ },
});
export interface IDEState {
@@ -117,6 +126,7 @@ export interface IDEState {
isListViewActive: boolean;
tabs: ParentEntityIDETabs;
showCreateModal: boolean;
+ renameEntity: string;
ideCanvasSideBySideHover: IDECanvasSideBySideHover;
}
diff --git a/app/client/src/selectors/ideSelectors.tsx b/app/client/src/selectors/ideSelectors.tsx
index 65fb4e68a90d..6abbd7255a54 100644
--- a/app/client/src/selectors/ideSelectors.tsx
+++ b/app/client/src/selectors/ideSelectors.tsx
@@ -64,3 +64,10 @@ export const getIdeCanvasSideBySideHoverState = (state: AppState) =>
export const getListViewActiveState = (state: AppState) =>
state.ui.ide.isListViewActive;
+
+export const getRenameEntity = (state: AppState) => state.ui.ide.renameEntity;
+
+export const getIsRenaming = (id: string) =>
+ createSelector(getRenameEntity, (entityId) => {
+ return entityId === id;
+ });