Browse Source

add BookmarkItemRenameInput

Yuki Takei 1 year ago
parent
commit
b6161e6535

+ 2 - 6
apps/app/src/components/Bookmarks/BookmarkItem.tsx

@@ -19,9 +19,9 @@ import { usePutBackPageModal } from '~/stores/modal';
 import { mutateAllPageInfo, useSWRMUTxCurrentPage, useSWRxPageInfo } from '~/stores/page';
 
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
-import { AutosizeSubmittableInput } from '../Common/SubmittableInput';
 import { PageListItemS } from '../PageList/PageListItemS';
 
+import { BookmarkItemRenameInput } from './BookmarkItemRenameInput';
 import { BookmarkMoveToRootBtn } from './BookmarkMoveToRootBtn';
 import { DragAndDropWrapper } from './DragAndDropWrapper';
 
@@ -162,14 +162,10 @@ export const BookmarkItem = (props: Props): JSX.Element => {
       >
         { isRenameInputShown
           ? (
-            <AutosizeSubmittableInput
+            <BookmarkItemRenameInput
               value={nodePath.basename(bookmarkedPage.path ?? '')}
-              inputClassName="form-control"
-              placeholder={t('Input page name')}
               onSubmit={rename}
               onCancel={() => { setRenameInputShown(false) }}
-              autoFocus
-              // validationTarget={ValidationTarget.PAGE}
             />
           )
           : <PageListItemS page={bookmarkedPage} pageTitle={pageTitle} isNarrowView />}

+ 68 - 0
apps/app/src/components/Bookmarks/BookmarkItemRenameInput.tsx

@@ -0,0 +1,68 @@
+import type { ChangeEvent } from 'react';
+import { useCallback, useRef, useState } from 'react';
+
+import { useRect } from '@growi/ui/dist/utils';
+import { useTranslation } from 'next-i18next';
+import type { AutosizeInputProps } from 'react-input-autosize';
+import { debounce } from 'throttle-debounce';
+
+import type { InputValidationResult } from '~/client/util/use-input-validator';
+import { ValidationTarget, useInputValidator } from '~/client/util/use-input-validator';
+
+import { AutosizeSubmittableInput, getAdjustedMaxWidthForAutosizeInput } from '../Common/SubmittableInput';
+import type { SubmittableInputProps } from '../Common/SubmittableInput/types';
+
+
+type Props = Pick<SubmittableInputProps<AutosizeInputProps>, 'value' | 'onSubmit' | 'onCancel'>;
+
+export const BookmarkItemRenameInput = (props: Props): JSX.Element => {
+  const { t } = useTranslation();
+
+  const { value, onSubmit, onCancel } = props;
+
+  const parentRef = useRef<HTMLDivElement>(null);
+  const [parentRect] = useRect(parentRef);
+
+  const [validationResult, setValidationResult] = useState<InputValidationResult>();
+
+
+  const inputValidator = useInputValidator(ValidationTarget.PAGE);
+
+  const changeHandler = useCallback(async(e: ChangeEvent<HTMLInputElement>) => {
+    const validationResult = inputValidator(e.target.value);
+    setValidationResult(validationResult ?? undefined);
+  }, [inputValidator]);
+  const changeHandlerDebounced = debounce(300, changeHandler);
+
+  const cancelHandler = useCallback(() => {
+    setValidationResult(undefined);
+    onCancel?.();
+  }, [onCancel]);
+
+  const isInvalid = validationResult != null;
+
+  const maxWidth = parentRect != null
+    ? getAdjustedMaxWidthForAutosizeInput(parentRect.width, 'md', validationResult != null ? false : undefined)
+    : undefined;
+
+  return (
+    <div className="flex-fill" ref={parentRef}>
+      <AutosizeSubmittableInput
+        value={value}
+        inputClassName={`form-control ${isInvalid ? 'is-invalid' : ''}`}
+        inputStyle={{ maxWidth }}
+        placeholder={t('Input page name')}
+        aria-describedby={isInvalid ? 'bookmark-item-rename-input-feedback' : undefined}
+        autoFocus
+        onChange={changeHandlerDebounced}
+        onSubmit={onSubmit}
+        onCancel={cancelHandler}
+      />
+      { isInvalid && (
+        <div id="bookmark-item-rename-input-feedback" className="invalid-feedback d-block my-1">
+          {validationResult.message}
+        </div>
+      ) }
+    </div>
+  );
+};