Mudana-Grune 3 лет назад
Родитель
Сommit
aa0740feb8

+ 5 - 1
packages/app/public/static/locales/en_US/translation.json

@@ -155,8 +155,12 @@
     "error_message": "Some values ​​are incorrect",
     "required": "%s is required",
     "invalid_syntax": "The syntax of %s is invalid.",
-    "title_required": "Title is required."
+    "title_required": "Title is required.",
+    "field_required": "{{target}} is required"
   },
+  "page_name": "Page name",
+  "folder_name": "Folder name",
+  "field": "field",
   "not_creatable_page": {
     "could_not_creata_path": "Couldn't create path."
   },

+ 5 - 1
packages/app/public/static/locales/ja_JP/translation.json

@@ -157,8 +157,12 @@
     "error_message": "いくつかの値が設定されていません",
     "required": "%sに値を入力してください",
     "invalid_syntax": "%sの構文が不正です",
-    "title_required": "タイトルを入力してください"
+    "title_required": "タイトルを入力してください",
+    "field_required": "{{target}}に値を入力してください"
   },
+  "page_name": "ページ名",
+  "folder_name": "フォルダ名",
+  "field": "フィールド",
   "not_creatable_page": {
     "could_not_creata_path": "パスを作成できませんでした。"
   },

+ 5 - 1
packages/app/public/static/locales/zh_CN/translation.json

@@ -163,8 +163,12 @@
 		"error_message": "有些值不正确",
 		"required": "%s 是必需的",
 		"invalid_syntax": "%s的语法无效。",
-    "title_required": "标题是必需的。"
+    "title_required": "标题是必需的。",
+    "field_required": "{{target}} 是必需的"
   },
+  "page_name": "页面名称",
+  "folder_name": "文件夹名称",
+  "field": "字段",
   "not_creatable_page": {
     "could_not_creata_path": "无法创建路径"
   },

+ 32 - 0
packages/app/src/client/util/input-validator.ts

@@ -0,0 +1,32 @@
+export const AlertType = {
+  WARNING: 'warning',
+  ERROR: 'error',
+} as const;
+
+export type AlertType = typeof AlertType[keyof typeof AlertType];
+
+export const ValidationTarget = {
+  FOLDER: 'folder_name',
+  PAGE: 'page_name',
+  DEFAULT: 'field',
+};
+
+export type ValidationTarget = typeof ValidationTarget[keyof typeof ValidationTarget];
+
+export type AlertInfo = {
+  type?: AlertType
+  message?: string,
+  target?: string
+}
+
+export const inputValidator = async(title: string | null, target?: string): Promise<AlertInfo | null> => {
+  const validationTarget = target || ValidationTarget.DEFAULT;
+  if (title == null || title === '' || title.trim() === '') {
+    return {
+      type: AlertType.WARNING,
+      message: 'form_validation.field_required',
+      target: validationTarget,
+    };
+  }
+  return null;
+};

+ 3 - 2
packages/app/src/components/Bookmarks/BookmarkFolderNameInput.tsx

@@ -1,6 +1,7 @@
 import { useTranslation } from 'next-i18next';
 
-import ClosableTextInput, { inputValidator } from '~/components/Common/ClosableTextInput';
+import { inputValidator, ValidationTarget } from '~/client/util/input-validator';
+import ClosableTextInput from '~/components/Common/ClosableTextInput';
 
 
 type Props = {
@@ -22,7 +23,7 @@ export const BookmarkFolderNameInput = (props: Props): JSX.Element => {
         placeholder={t('bookmark_folder.input_placeholder')}
         onClickOutside={onClickOutside}
         onPressEnter={onPressEnter}
-        inputValidator={inputValidator}
+        validationTarget={ValidationTarget.FOLDER}
       />
     </div>
   );

+ 3 - 2
packages/app/src/components/Bookmarks/BookmarkItem.tsx

@@ -9,13 +9,14 @@ import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 
 import { unbookmark } from '~/client/services/page-operation';
 import { renamePage } from '~/client/util/bookmark-utils';
+import { ValidationTarget } from '~/client/util/input-validator';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { BookmarkFolderItems, DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info';
 import { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '~/interfaces/page';
 import { useSWRxBookamrkFolderAndChild } from '~/stores/bookmark-folder';
 import { useSWRxPageInfo } from '~/stores/page';
 
-import ClosableTextInput, { inputValidator } from '../Common/ClosableTextInput';
+import ClosableTextInput from '../Common/ClosableTextInput';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
 import { PageListItemS } from '../PageList/PageListItemS';
 
@@ -117,7 +118,7 @@ export const BookmarkItem = (props: Props): JSX.Element => {
           placeholder={t('Input page name')}
           onClickOutside={() => { setRenameInputShown(false) }}
           onPressEnter={pressEnterForRenameHandler}
-          inputValidator={inputValidator}
+          validationTarget={ValidationTarget.PAGE}
         />
       ) : <PageListItemS page={bookmarkedPage} pageTitle={pageTitle}/>}
       <div className='grw-foldertree-control'>

+ 9 - 29
packages/app/src/components/Common/ClosableTextInput.tsx

@@ -4,43 +4,33 @@ import React, {
 
 import { useTranslation } from 'next-i18next';
 
-export const AlertType = {
-  WARNING: 'warning',
-  ERROR: 'error',
-} as const;
-
-export type AlertType = typeof AlertType[keyof typeof AlertType];
-
-export type AlertInfo = {
-  type?: AlertType
-  message?: string
-}
+import { AlertInfo, AlertType, inputValidator } from '~/client/util/input-validator';
 
 type ClosableTextInputProps = {
   value?: string
   placeholder?: string
-  inputValidator?(text: string): AlertInfo | Promise<AlertInfo> | null
+  validationTarget?: string,
   onPressEnter?(inputText: string | null): void
   onClickOutside?(): void
 }
 
 const ClosableTextInput: FC<ClosableTextInputProps> = memo((props: ClosableTextInputProps) => {
   const { t } = useTranslation();
-  const inputRef = useRef<HTMLInputElement>(null);
+  const { validationTarget } = props;
 
+  const inputRef = useRef<HTMLInputElement>(null);
   const [inputText, setInputText] = useState(props.value);
   const [currentAlertInfo, setAlertInfo] = useState<AlertInfo | null>(null);
   const [isAbleToShowAlert, setIsAbleToShowAlert] = useState(false);
   const [isComposing, setComposing] = useState(false);
 
+
   const createValidation = async(inputText: string) => {
-    if (props.inputValidator != null) {
-      const alertInfo = await props.inputValidator(inputText);
-      if (alertInfo && alertInfo.message != null) {
-        alertInfo.message = t(alertInfo?.message);
-      }
-      setAlertInfo(alertInfo);
+    const alertInfo = await inputValidator(inputText, validationTarget);
+    if (alertInfo && alertInfo.message != null && alertInfo.target != null) {
+      alertInfo.message = t(alertInfo.message, { target: t(alertInfo.target) });
     }
+    setAlertInfo(alertInfo);
   };
 
   const onChangeHandler = async(e: React.ChangeEvent<HTMLInputElement>) => {
@@ -137,16 +127,6 @@ const ClosableTextInput: FC<ClosableTextInputProps> = memo((props: ClosableTextI
   );
 });
 
-export const inputValidator = (title: string | null): AlertInfo | null => {
-  if (title == null || title === '' || title.trim() === '') {
-    return {
-      type: AlertType.WARNING,
-      message: 'form_validation.title_required',
-    };
-  }
-  return null;
-};
-
 ClosableTextInput.displayName = 'ClosableTextInput';
 
 export default ClosableTextInput;

+ 3 - 2
packages/app/src/components/PageList/BookmarkList.tsx

@@ -11,10 +11,11 @@ import { DropdownToggle } from 'reactstrap';
 import { unbookmark } from '~/client/services/page-operation';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import { apiv3Put } from '~/client/util/apiv3-client';
+import { ValidationTarget } from '~/client/util/input-validator';
 import { IPageHasId } from '~/interfaces/page';
 import loggerFactory from '~/utils/logger';
 
-import ClosableTextInput, { inputValidator } from '../Common/ClosableTextInput';
+import ClosableTextInput from '../Common/ClosableTextInput';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
 
 import { PageListItemS } from './PageListItemS';
@@ -88,7 +89,7 @@ export const BookmarkList = (props:Props): JSX.Element => {
           placeholder={t('Input page name')}
           onClickOutside={() => { setIsRenameInputShown(false) }}
           onPressEnter={pressEnterForRenameHandler}
-          inputValidator={inputValidator}
+          validationTarget={ValidationTarget.PAGE}
         />
       ) : (
         <PageListItemS page={page} />

+ 4 - 3
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -13,6 +13,7 @@ import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 import { bookmark, unbookmark, resumeRenameOperation } from '~/client/services/page-operation';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
+import { ValidationTarget } from '~/client/util/input-validator';
 import { TriangleIcon } from '~/components/Icons/TriangleIcon';
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
 import {
@@ -25,7 +26,7 @@ import { usePageTreeDescCountMap } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 import { shouldRecoverPagePaths } from '~/utils/page-operation';
 
-import ClosableTextInput, { inputValidator } from '../../Common/ClosableTextInput';
+import ClosableTextInput from '../../Common/ClosableTextInput';
 import CountBadge from '../../Common/CountBadge';
 import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
 
@@ -450,7 +451,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
                   placeholder={t('Input page name')}
                   onClickOutside={() => { setRenameInputShown(false) }}
                   onPressEnter={onPressEnterForRenameHandler}
-                  inputValidator={inputValidator}
+                  validationTarget={ValidationTarget.PAGE}
                 />
               </NotDraggableForClosableTextInput>
             </div>
@@ -522,7 +523,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
               placeholder={t('Input page name')}
               onClickOutside={() => { setNewPageInputShown(false) }}
               onPressEnter={onPressEnterForCreateHandler}
-              inputValidator={inputValidator}
+              validationTarget={ValidationTarget.PAGE}
             />
           </NotDraggableForClosableTextInput>
         </div>