|
|
@@ -1,5 +1,5 @@
|
|
|
import React, {
|
|
|
- useState, useEffect, useCallback, useMemo, type JSX,
|
|
|
+ useState, useEffect, useCallback, useMemo,
|
|
|
} from 'react';
|
|
|
|
|
|
import { useAtomValue } from 'jotai';
|
|
|
@@ -19,8 +19,10 @@ import DuplicatePathsTable from './DuplicatedPathsTable';
|
|
|
import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
|
|
|
import PagePathAutoComplete from './PagePathAutoComplete';
|
|
|
|
|
|
-
|
|
|
-const PageDuplicateModal = (): JSX.Element => {
|
|
|
+/**
|
|
|
+ * PageDuplicateModalSubstance - Heavy processing component (rendered only when modal is open)
|
|
|
+ */
|
|
|
+const PageDuplicateModalSubstance: React.FC = () => {
|
|
|
const { t } = useTranslation();
|
|
|
|
|
|
const siteUrl = useSiteUrl();
|
|
|
@@ -39,6 +41,12 @@ const PageDuplicateModal = (): JSX.Element => {
|
|
|
const [isDuplicateRecursivelyWithoutExistPath, setIsDuplicateRecursivelyWithoutExistPath] = useState(true);
|
|
|
const [onlyDuplicateUserRelatedResources, setOnlyDuplicateUserRelatedResources] = useState(false);
|
|
|
|
|
|
+ // Memoize computed values
|
|
|
+ const isTargetPageDuplicate = useMemo(() => existingPaths.includes(pageNameInput), [existingPaths, pageNameInput]);
|
|
|
+ const submitButtonEnabled = useMemo(() => (
|
|
|
+ existingPaths.length === 0 || (isDuplicateRecursively && isDuplicateRecursivelyWithoutExistPath)
|
|
|
+ ), [existingPaths.length, isDuplicateRecursively, isDuplicateRecursivelyWithoutExistPath]);
|
|
|
+
|
|
|
const updateSubordinatedList = useCallback(async() => {
|
|
|
if (page == null) {
|
|
|
return;
|
|
|
@@ -90,14 +98,14 @@ const PageDuplicateModal = (): JSX.Element => {
|
|
|
* change pageNameInput
|
|
|
* @param {string} value
|
|
|
*/
|
|
|
- function inputChangeHandler(value) {
|
|
|
+ const inputChangeHandler = useCallback((value) => {
|
|
|
setErrs(null);
|
|
|
setPageNameInput(value);
|
|
|
- }
|
|
|
+ }, []);
|
|
|
|
|
|
- function changeIsDuplicateRecursivelyHandler() {
|
|
|
+ const changeIsDuplicateRecursivelyHandler = useCallback(() => {
|
|
|
setIsDuplicateRecursively(!isDuplicateRecursively);
|
|
|
- }
|
|
|
+ }, [isDuplicateRecursively]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (page != null && isOpened) {
|
|
|
@@ -156,7 +164,6 @@ const PageDuplicateModal = (): JSX.Element => {
|
|
|
}
|
|
|
|
|
|
const { path } = page;
|
|
|
- const isTargetPageDuplicate = existingPaths.includes(pageNameInput);
|
|
|
|
|
|
return (
|
|
|
<>
|
|
|
@@ -257,9 +264,6 @@ const PageDuplicateModal = (): JSX.Element => {
|
|
|
return <></>;
|
|
|
}
|
|
|
|
|
|
- const submitButtonEnabled = existingPaths.length === 0
|
|
|
- || (isDuplicateRecursively && isDuplicateRecursivelyWithoutExistPath);
|
|
|
-
|
|
|
return (
|
|
|
<>
|
|
|
<ApiErrorMessageList errs={errs} targetPath={pageNameInput} />
|
|
|
@@ -278,7 +282,7 @@ const PageDuplicateModal = (): JSX.Element => {
|
|
|
|
|
|
|
|
|
return (
|
|
|
- <Modal size="lg" isOpen={isOpened} toggle={closeDuplicateModal} data-testid="page-duplicate-modal" className="grw-duplicate-page" autoFocus={false}>
|
|
|
+ <>
|
|
|
<ModalHeader tag="h4" toggle={closeDuplicateModal}>
|
|
|
{ t('modal_duplicate.label.Duplicate page') }
|
|
|
</ModalHeader>
|
|
|
@@ -288,9 +292,26 @@ const PageDuplicateModal = (): JSX.Element => {
|
|
|
<ModalFooter>
|
|
|
{renderFooterContent()}
|
|
|
</ModalFooter>
|
|
|
- </Modal>
|
|
|
+ </>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * PageDuplicateModal - Container component (lightweight, always rendered)
|
|
|
+ */
|
|
|
+const PageDuplicateModal = (): React.JSX.Element => {
|
|
|
+ const { isOpened } = usePageDuplicateModalStatus();
|
|
|
+ const { close: closeDuplicateModal } = usePageDuplicateModalActions();
|
|
|
+
|
|
|
+ if (!isOpened) {
|
|
|
+ return <></>;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Modal size="lg" isOpen={isOpened} toggle={closeDuplicateModal} data-testid="page-duplicate-modal" className="grw-duplicate-page" autoFocus={false}>
|
|
|
+ <PageDuplicateModalSubstance />
|
|
|
+ </Modal>
|
|
|
+ );
|
|
|
+};
|
|
|
|
|
|
export default PageDuplicateModal;
|