import type { ChangeEvent, JSX } from 'react'; import { memo, useCallback, useState } from 'react'; import type { IPagePopulatedToShowRevision } from '@growi/core'; import { DevidedPagePath } from '@growi/core/dist/models'; import { normalizePath } from '@growi/core/dist/utils/path-utils'; import { useTranslation } from 'next-i18next'; import nodePath from 'path'; import { debounce } from 'throttle-debounce'; import type { InputValidationResult } from '~/client/util/use-input-validator'; import { useInputValidator, ValidationTarget, } from '~/client/util/use-input-validator'; import type { IPageForItem } from '~/interfaces/page'; import LinkedPagePath from '~/models/linked-page-path'; import { usePageSelectModalActions } from '~/states/ui/modal/page-select'; import { PagePathHierarchicalLink } from '../../../components/Common/PagePathHierarchicalLink'; import { AutosizeSubmittableInput, getAdjustedMaxWidthForAutosizeInput, } from '../Common/SubmittableInput'; import { usePagePathRenameHandler } from '../PageEditor/page-path-rename-utils'; import styles from './PagePathHeader.module.scss'; const moduleClass = styles['page-path-header']; type Props = { currentPage: IPagePopulatedToShowRevision; className?: string; maxWidth?: number; onRenameTerminated?: () => void; }; export const PagePathHeader = memo((props: Props): JSX.Element => { const { t } = useTranslation(); const { currentPage, className, maxWidth, onRenameTerminated } = props; const dPagePath = new DevidedPagePath(currentPage.path, true); const parentPagePath = dPagePath.former; const linkedPagePath = new LinkedPagePath(parentPagePath); const [isRenameInputShown, setRenameInputShown] = useState(false); const [isHover, setHover] = useState(false); const { open: openPageSelectModal } = usePageSelectModalActions(); const [validationResult, setValidationResult] = useState(); const inputValidator = useInputValidator(ValidationTarget.PAGE); const changeHandler = useCallback( async (e: ChangeEvent) => { const validationResult = inputValidator(e.target.value); setValidationResult(validationResult ?? undefined); }, [inputValidator], ); const changeHandlerDebounced = debounce(300, changeHandler); const pagePathRenameHandler = usePagePathRenameHandler(currentPage); const onClickOpenPageSelectModalButton = useCallback(() => { const onSelected = (page: IPageForItem): void => { if (page == null || page.path == null) { return; } const currentPageTitle = nodePath.basename(currentPage?.path ?? '') || '/'; const newPagePath = nodePath.resolve(page.path, currentPageTitle); pagePathRenameHandler(newPagePath); }; openPageSelectModal({ onSelected }); }, [currentPage?.path, openPageSelectModal, pagePathRenameHandler]); const rename = useCallback( (inputText) => { const pathToRename = normalizePath(`${inputText}/${dPagePath.latter}`); pagePathRenameHandler( pathToRename, () => { setRenameInputShown(false); setValidationResult(undefined); onRenameTerminated?.(); }, () => { setRenameInputShown(true); }, ); }, [dPagePath.latter, pagePathRenameHandler, onRenameTerminated], ); const cancel = useCallback(() => { // reset setValidationResult(undefined); setRenameInputShown(false); }, []); const onClickEditButton = useCallback(() => { // reset setRenameInputShown(true); }, []); if (dPagePath.isRoot) { return <>; } const isInvalid = validationResult != null; const fixedMaxWidth = maxWidth != null ? maxWidth - 60 // 60px is the width of the buttons : undefined; const inputMaxWidth = maxWidth != null ? getAdjustedMaxWidthForAutosizeInput( maxWidth, 'sm', validationResult != null ? false : undefined, ) - 16 : undefined; return (
setHover(true)} onMouseLeave={() => setHover(false)} onFocus={() => setHover(true)} onBlur={() => setHover(false)} >
{isRenameInputShown && (
)}
); });