|
|
@@ -1,84 +1,95 @@
|
|
|
import {
|
|
|
- FC, useEffect, useMemo, useState,
|
|
|
+ useMemo, useState, useEffect, useCallback,
|
|
|
} from 'react';
|
|
|
+import type { FC } from 'react';
|
|
|
|
|
|
import type { IPagePopulatedToShowRevision } from '@growi/core';
|
|
|
+import { useTranslation } from 'next-i18next';
|
|
|
|
|
|
+import { ValidationTarget } from '~/client/util/input-validator';
|
|
|
import { usePageSelectModal } from '~/stores/modal';
|
|
|
import { EditorMode, useEditorMode } from '~/stores/ui';
|
|
|
|
|
|
+import ClosableTextInput from '../Common/ClosableTextInput';
|
|
|
import { PagePathNav } from '../Common/PagePathNav';
|
|
|
import { PageSelectModal } from '../PageSelectModal/PageSelectModal';
|
|
|
|
|
|
-import { TextInputForPageTitleAndPath } from './TextInputForPageTitleAndPath';
|
|
|
import { usePagePathRenameHandler } from './page-header-utils';
|
|
|
|
|
|
-type Props = {
|
|
|
- currentPagePath: string
|
|
|
+
|
|
|
+export type Props = {
|
|
|
currentPage: IPagePopulatedToShowRevision
|
|
|
}
|
|
|
|
|
|
export const PagePathHeader: FC<Props> = (props) => {
|
|
|
- const { currentPagePath, currentPage } = props;
|
|
|
+ const { currentPage } = props;
|
|
|
+
|
|
|
+ const currentPagePath = currentPage.path;
|
|
|
|
|
|
const [isRenameInputShown, setRenameInputShown] = useState(false);
|
|
|
const [isButtonsShown, setButtonShown] = useState(false);
|
|
|
- const [inputText, setInputText] = useState('');
|
|
|
+ const [editedPagePath, setEditedPagePath] = useState(currentPagePath);
|
|
|
|
|
|
const { data: editorMode } = useEditorMode();
|
|
|
const { data: PageSelectModalData, open: openPageSelectModal } = usePageSelectModal();
|
|
|
|
|
|
- const onRenameFinish = () => {
|
|
|
+ const pagePathRenameHandler = usePagePathRenameHandler(currentPage);
|
|
|
+
|
|
|
+ const { t } = useTranslation();
|
|
|
+
|
|
|
+ const onRenameFinish = useCallback(() => {
|
|
|
setRenameInputShown(false);
|
|
|
- };
|
|
|
+ }, []);
|
|
|
|
|
|
- const onRenameFailure = () => {
|
|
|
+ const onRenameFailure = useCallback(() => {
|
|
|
setRenameInputShown(true);
|
|
|
- };
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ const onInputChange = useCallback((inputText: string) => {
|
|
|
+ setEditedPagePath(inputText);
|
|
|
+ }, []);
|
|
|
|
|
|
- const pagePathRenameHandler = usePagePathRenameHandler(currentPage, onRenameFinish, onRenameFailure);
|
|
|
+ const onPressEnter = useCallback(() => {
|
|
|
+ pagePathRenameHandler(editedPagePath, onRenameFinish, onRenameFailure);
|
|
|
+ }, [editedPagePath, onRenameFailure, onRenameFinish, pagePathRenameHandler]);
|
|
|
|
|
|
- const stateHandler = { isRenameInputShown, setRenameInputShown };
|
|
|
+ const onPressEscape = useCallback(() => {
|
|
|
+ setEditedPagePath(currentPagePath);
|
|
|
+ setRenameInputShown(false);
|
|
|
+ }, [currentPagePath]);
|
|
|
|
|
|
- const isOpened = PageSelectModalData?.isOpened ?? false;
|
|
|
+ const onClickEditButton = useCallback(() => {
|
|
|
+ if (isRenameInputShown) {
|
|
|
+ pagePathRenameHandler(editedPagePath, onRenameFinish, onRenameFailure);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ setEditedPagePath(currentPagePath);
|
|
|
+ setRenameInputShown(true);
|
|
|
+ }
|
|
|
+ }, [currentPagePath, editedPagePath, isRenameInputShown, pagePathRenameHandler]);
|
|
|
|
|
|
+ const isOpened = PageSelectModalData?.isOpened ?? false;
|
|
|
const isViewMode = editorMode === EditorMode.View;
|
|
|
const isEditorMode = !isViewMode;
|
|
|
|
|
|
const PagePath = useMemo(() => (
|
|
|
- <>
|
|
|
- {currentPagePath != null && (
|
|
|
- <PagePathNav
|
|
|
- pageId={currentPage._id}
|
|
|
- pagePath={currentPagePath}
|
|
|
- isSingleLineMode={isEditorMode}
|
|
|
- />
|
|
|
- )}
|
|
|
- </>
|
|
|
+ <PagePathNav
|
|
|
+ pageId={currentPage._id}
|
|
|
+ pagePath={currentPagePath}
|
|
|
+ isSingleLineMode={isEditorMode}
|
|
|
+ />
|
|
|
), [currentPage._id, currentPagePath, isEditorMode]);
|
|
|
|
|
|
- const handleInputChange = (inputText: string) => {
|
|
|
- setInputText(inputText);
|
|
|
- };
|
|
|
-
|
|
|
- const handleEditButtonClick = () => {
|
|
|
- if (isRenameInputShown) {
|
|
|
- pagePathRenameHandler(inputText);
|
|
|
- }
|
|
|
- else {
|
|
|
- setRenameInputShown(true);
|
|
|
- }
|
|
|
- };
|
|
|
|
|
|
const buttonStyle = isButtonsShown ? '' : 'd-none';
|
|
|
|
|
|
- const clickOutSideHandler = (e) => {
|
|
|
+ const clickOutSideHandler = useCallback((e) => {
|
|
|
const container = document.getElementById('page-path-header');
|
|
|
|
|
|
if (container && !container.contains(e.target)) {
|
|
|
setRenameInputShown(false);
|
|
|
}
|
|
|
- };
|
|
|
+ }, []);
|
|
|
|
|
|
useEffect(() => {
|
|
|
document.addEventListener('click', clickOutSideHandler);
|
|
|
@@ -88,43 +99,49 @@ export const PagePathHeader: FC<Props> = (props) => {
|
|
|
};
|
|
|
}, []);
|
|
|
|
|
|
+
|
|
|
return (
|
|
|
- <>
|
|
|
- <div
|
|
|
- id="page-path-header"
|
|
|
- onMouseLeave={() => setButtonShown(false)}
|
|
|
- >
|
|
|
- <div className="row">
|
|
|
- <div
|
|
|
- className="col-4"
|
|
|
- onMouseEnter={() => setButtonShown(true)}
|
|
|
- >
|
|
|
- <TextInputForPageTitleAndPath
|
|
|
- currentPage={currentPage}
|
|
|
- stateHandler={stateHandler}
|
|
|
- inputValue={currentPagePath}
|
|
|
- CustomComponent={PagePath}
|
|
|
- handleInputChange={handleInputChange}
|
|
|
- />
|
|
|
- </div>
|
|
|
- <div className={`${buttonStyle} col-4 row`}>
|
|
|
- <div className="col-4">
|
|
|
- <button type="button" onClick={handleEditButtonClick}>
|
|
|
- {isRenameInputShown ? <span className="material-symbols-outlined">check_circle</span> : <span className="material-symbols-outlined">edit</span>}
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- <div className="col-4">
|
|
|
- <button type="button" onClick={openPageSelectModal}>
|
|
|
- <span className="material-symbols-outlined">account_tree</span>
|
|
|
- </button>
|
|
|
+ <div
|
|
|
+ id="page-path-header"
|
|
|
+ onMouseLeave={() => setButtonShown(false)}
|
|
|
+ >
|
|
|
+ <div className="row">
|
|
|
+ <div
|
|
|
+ className="col-4"
|
|
|
+ onMouseEnter={() => setButtonShown(true)}
|
|
|
+ >
|
|
|
+ {isRenameInputShown ? (
|
|
|
+ <div className="flex-fill">
|
|
|
+ <ClosableTextInput
|
|
|
+ value={editedPagePath}
|
|
|
+ placeholder={t('Input page name')}
|
|
|
+ onPressEnter={onPressEnter}
|
|
|
+ onPressEscape={onPressEscape}
|
|
|
+ validationTarget={ValidationTarget.PAGE}
|
|
|
+ handleInputChange={onInputChange}
|
|
|
+ />
|
|
|
</div>
|
|
|
+ ) : (
|
|
|
+ <>{ PagePath }</>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div className={`${buttonStyle} col-4 row`}>
|
|
|
+ <div className="col-4">
|
|
|
+ <button type="button" onClick={onClickEditButton}>
|
|
|
+ {isRenameInputShown ? <span className="material-symbols-outlined">check_circle</span> : <span className="material-symbols-outlined">edit</span>}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div className="col-4">
|
|
|
+ <button type="button" onClick={openPageSelectModal}>
|
|
|
+ <span className="material-symbols-outlined">account_tree</span>
|
|
|
+ </button>
|
|
|
</div>
|
|
|
- {isOpened
|
|
|
- && (
|
|
|
- <PageSelectModal />
|
|
|
- )}
|
|
|
</div>
|
|
|
+ {isOpened
|
|
|
+ && (
|
|
|
+ <PageSelectModal />
|
|
|
+ )}
|
|
|
</div>
|
|
|
- </>
|
|
|
+ </div>
|
|
|
);
|
|
|
};
|