|
@@ -25,37 +25,45 @@ const DescendantsPageList = dynamic<DescendantsPageListProps>(() => import('./De
|
|
|
|
|
|
|
|
const PageTimeline = dynamic(() => import('./PageTimeline').then(mod => mod.PageTimeline), { ssr: false });
|
|
const PageTimeline = dynamic(() => import('./PageTimeline').then(mod => mod.PageTimeline), { ssr: false });
|
|
|
|
|
|
|
|
-export const DescendantsPageListModal = (): React.JSX.Element => {
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * DescendantsPageListModalSubstance - Presentation component (all logic here)
|
|
|
|
|
+ */
|
|
|
|
|
+type DescendantsPageListModalSubstanceProps = {
|
|
|
|
|
+ path: string | undefined;
|
|
|
|
|
+ closeModal: () => void;
|
|
|
|
|
+ onExpandedChange?: (isExpanded: boolean) => void;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const DescendantsPageListModalSubstance = ({
|
|
|
|
|
+ path,
|
|
|
|
|
+ closeModal,
|
|
|
|
|
+ onExpandedChange,
|
|
|
|
|
+}: DescendantsPageListModalSubstanceProps): React.JSX.Element => {
|
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
|
|
|
|
|
|
const [activeTab, setActiveTab] = useState('pagelist');
|
|
const [activeTab, setActiveTab] = useState('pagelist');
|
|
|
const [isWindowExpanded, setIsWindowExpanded] = useState(false);
|
|
const [isWindowExpanded, setIsWindowExpanded] = useState(false);
|
|
|
|
|
|
|
|
const isSharedUser = useIsSharedUser();
|
|
const isSharedUser = useIsSharedUser();
|
|
|
-
|
|
|
|
|
- const status = useDescendantsPageListModalStatus();
|
|
|
|
|
- const { close } = useDescendantsPageListModalActions();
|
|
|
|
|
-
|
|
|
|
|
const { events } = useRouter();
|
|
const { events } = useRouter();
|
|
|
-
|
|
|
|
|
const [isDeviceLargerThanLg] = useDeviceLargerThanLg();
|
|
const [isDeviceLargerThanLg] = useDeviceLargerThanLg();
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
- events.on('routeChangeStart', close);
|
|
|
|
|
|
|
+ events.on('routeChangeStart', closeModal);
|
|
|
return () => {
|
|
return () => {
|
|
|
- events.off('routeChangeStart', close);
|
|
|
|
|
|
|
+ events.off('routeChangeStart', closeModal);
|
|
|
};
|
|
};
|
|
|
- }, [close, events]);
|
|
|
|
|
|
|
+ }, [closeModal, events]);
|
|
|
|
|
|
|
|
const navTabMapping = useMemo(() => {
|
|
const navTabMapping = useMemo(() => {
|
|
|
return {
|
|
return {
|
|
|
pagelist: {
|
|
pagelist: {
|
|
|
Icon: () => <span className="material-symbols-outlined">subject</span>,
|
|
Icon: () => <span className="material-symbols-outlined">subject</span>,
|
|
|
Content: () => {
|
|
Content: () => {
|
|
|
- if (status == null || status.path == null || !status.isOpened) {
|
|
|
|
|
|
|
+ if (path == null) {
|
|
|
return <></>;
|
|
return <></>;
|
|
|
}
|
|
}
|
|
|
- return <DescendantsPageList path={status.path} />;
|
|
|
|
|
|
|
+ return <DescendantsPageList path={path} />;
|
|
|
},
|
|
},
|
|
|
i18n: t('page_list'),
|
|
i18n: t('page_list'),
|
|
|
isLinkEnabled: () => !isSharedUser,
|
|
isLinkEnabled: () => !isSharedUser,
|
|
@@ -63,20 +71,23 @@ export const DescendantsPageListModal = (): React.JSX.Element => {
|
|
|
timeline: {
|
|
timeline: {
|
|
|
Icon: () => <span data-testid="timeline-tab-button" className="material-symbols-outlined">timeline</span>,
|
|
Icon: () => <span data-testid="timeline-tab-button" className="material-symbols-outlined">timeline</span>,
|
|
|
Content: () => {
|
|
Content: () => {
|
|
|
- if (status == null || !status.isOpened) {
|
|
|
|
|
- return <></>;
|
|
|
|
|
- }
|
|
|
|
|
return <PageTimeline />;
|
|
return <PageTimeline />;
|
|
|
},
|
|
},
|
|
|
i18n: t('Timeline View'),
|
|
i18n: t('Timeline View'),
|
|
|
isLinkEnabled: () => !isSharedUser,
|
|
isLinkEnabled: () => !isSharedUser,
|
|
|
},
|
|
},
|
|
|
};
|
|
};
|
|
|
- }, [isSharedUser, status, t]);
|
|
|
|
|
|
|
+ }, [isSharedUser, path, t]);
|
|
|
|
|
|
|
|
// Memoize event handlers
|
|
// Memoize event handlers
|
|
|
- const expandWindow = useCallback(() => setIsWindowExpanded(true), []);
|
|
|
|
|
- const contractWindow = useCallback(() => setIsWindowExpanded(false), []);
|
|
|
|
|
|
|
+ const expandWindow = useCallback(() => {
|
|
|
|
|
+ setIsWindowExpanded(true);
|
|
|
|
|
+ onExpandedChange?.(true);
|
|
|
|
|
+ }, [onExpandedChange]);
|
|
|
|
|
+ const contractWindow = useCallback(() => {
|
|
|
|
|
+ setIsWindowExpanded(false);
|
|
|
|
|
+ onExpandedChange?.(false);
|
|
|
|
|
+ }, [onExpandedChange]);
|
|
|
const onNavSelected = useCallback((v: string) => setActiveTab(v), []);
|
|
const onNavSelected = useCallback((v: string) => setActiveTab(v), []);
|
|
|
|
|
|
|
|
const buttons = useMemo(() => (
|
|
const buttons = useMemo(() => (
|
|
@@ -86,26 +97,13 @@ export const DescendantsPageListModal = (): React.JSX.Element => {
|
|
|
expandWindow={expandWindow}
|
|
expandWindow={expandWindow}
|
|
|
contractWindow={contractWindow}
|
|
contractWindow={contractWindow}
|
|
|
/>
|
|
/>
|
|
|
- <button type="button" className="btn btn-close ms-2" onClick={close} aria-label="Close"></button>
|
|
|
|
|
|
|
+ <button type="button" className="btn btn-close ms-2" onClick={closeModal} aria-label="Close"></button>
|
|
|
</span>
|
|
</span>
|
|
|
- ), [close, isWindowExpanded, expandWindow, contractWindow]);
|
|
|
|
|
-
|
|
|
|
|
- // Early return after all hooks
|
|
|
|
|
- if (status == null || !status.isOpened) {
|
|
|
|
|
- return <></>;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const { isOpened } = status;
|
|
|
|
|
|
|
+ ), [closeModal, isWindowExpanded, expandWindow, contractWindow]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <Modal
|
|
|
|
|
- size="xl"
|
|
|
|
|
- isOpen={isOpened}
|
|
|
|
|
- toggle={close}
|
|
|
|
|
- data-testid="descendants-page-list-modal"
|
|
|
|
|
- className={`grw-descendants-page-list-modal ${styles['grw-descendants-page-list-modal']} ${isWindowExpanded ? 'grw-modal-expanded' : ''} `}
|
|
|
|
|
- >
|
|
|
|
|
- <ModalHeader className={isDeviceLargerThanLg ? 'p-0' : ''} toggle={close} close={buttons}>
|
|
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <ModalHeader className={isDeviceLargerThanLg ? 'p-0' : ''} toggle={closeModal} close={buttons}>
|
|
|
{isDeviceLargerThanLg && (
|
|
{isDeviceLargerThanLg && (
|
|
|
<CustomNavTab
|
|
<CustomNavTab
|
|
|
activeTab={activeTab}
|
|
activeTab={activeTab}
|
|
@@ -130,7 +128,38 @@ export const DescendantsPageListModal = (): React.JSX.Element => {
|
|
|
additionalClassNames={!isDeviceLargerThanLg ? ['grw-tab-content-style-md-down'] : undefined}
|
|
additionalClassNames={!isDeviceLargerThanLg ? ['grw-tab-content-style-md-down'] : undefined}
|
|
|
/>
|
|
/>
|
|
|
</ModalBody>
|
|
</ModalBody>
|
|
|
- </Modal>
|
|
|
|
|
|
|
+ </div>
|
|
|
);
|
|
);
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * DescendantsPageListModal - Container component (lightweight, always rendered)
|
|
|
|
|
+ */
|
|
|
|
|
+export const DescendantsPageListModal = (): React.JSX.Element => {
|
|
|
|
|
+ const [isWindowExpanded, setIsWindowExpanded] = useState(false);
|
|
|
|
|
+ const status = useDescendantsPageListModalStatus();
|
|
|
|
|
+ const { close } = useDescendantsPageListModalActions();
|
|
|
|
|
+ const isOpened = status?.isOpened ?? false;
|
|
|
|
|
+
|
|
|
|
|
+ const handleExpandedChange = useCallback((isExpanded: boolean) => {
|
|
|
|
|
+ setIsWindowExpanded(isExpanded);
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Modal
|
|
|
|
|
+ size="xl"
|
|
|
|
|
+ isOpen={isOpened}
|
|
|
|
|
+ toggle={close}
|
|
|
|
|
+ data-testid="descendants-page-list-modal"
|
|
|
|
|
+ className={`grw-descendants-page-list-modal ${styles['grw-descendants-page-list-modal']} ${isWindowExpanded ? 'grw-modal-expanded' : ''}`}
|
|
|
|
|
+ >
|
|
|
|
|
+ {isOpened && (
|
|
|
|
|
+ <DescendantsPageListModalSubstance
|
|
|
|
|
+ path={status?.path}
|
|
|
|
|
+ closeModal={close}
|
|
|
|
|
+ onExpandedChange={handleExpandedChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ )}
|
|
|
|
|
+ </Modal>
|
|
|
|
|
+ );
|
|
|
};
|
|
};
|