Shun Miyazawa 4 лет назад
Родитель
Сommit
4b870ed5ce

+ 1 - 0
packages/app/src/components/Page/TagEditModal.jsx

@@ -11,6 +11,7 @@ function TagEditModal(props) {
   const [tags, setTags] = useState([]);
 
   function onTagsUpdatedByTagsInput(tags) {
+    console.log('モーダル ok');
     setTags(tags);
   }
 

+ 93 - 0
packages/app/src/components/Page/TagsInput.tsx

@@ -0,0 +1,93 @@
+import React, {
+  FC, useRef, useState,
+} from 'react';
+import { AsyncTypeahead } from 'react-bootstrap-typeahead';
+
+import { apiGet } from '~/client/util/apiv1-client';
+
+type ITagsSearch = {
+  ok: boolean,
+  tags: string[]
+}
+
+type TypeaheadInstance = {
+  focus: () => void,
+  _handleMenuItemSelect: (activeItem: string, e: React.ChangeEvent<HTMLInputElement>) => void,
+  state: {
+    initialItem: string,
+  },
+}
+
+type Props = {
+  tags: string[],
+  onTagsUpdated: (tags: string[]) => void,
+  autoFocus: boolean
+}
+
+const TagsInput: FC<Props> = (props: Props) => {
+
+  const tagsInputRef = useRef<TypeaheadInstance>(null);
+
+  const [resultTags, setResultTags] = useState<string[]>([]);
+  const [isLoading, setLoading] = useState(false);
+  const [selected, setSelected] = useState(props.tags);
+
+  const handleChange = (selected) => {
+    setSelected(selected);
+
+    if (props.onTagsUpdated != null) {
+      props.onTagsUpdated(selected);
+    }
+  };
+
+  const handleSearch = async(query) => {
+    setLoading(true);
+    try {
+      const res = await apiGet('/tags.search', { q: query }) as ITagsSearch;
+      res.tags.unshift(query);
+      setResultTags(Array.from(new Set(res.tags)));
+    }
+    catch (err) {
+      //
+    }
+    finally {
+      setLoading(false);
+    }
+  };
+
+  const handleSelect = (e) => {
+    if (e.keyCode === 32 || e.keyCode === 13) { // '32' means ASCII code of 'space'
+      e.preventDefault();
+
+      const initialItem = tagsInputRef?.current?.state?.initialItem;
+      const handleMenuItemSelect = tagsInputRef?.current?._handleMenuItemSelect;
+
+      if (initialItem != null && handleMenuItemSelect != null) {
+        handleMenuItemSelect(initialItem, e);
+      }
+    }
+  };
+
+  return (
+    <div className="tag-typeahead">
+      <AsyncTypeahead
+        id="tag-typeahead-asynctypeahead"
+        ref={tagsInputRef}
+        caseSensitive={false}
+        defaultSelected={props.tags ?? []}
+        isLoading={isLoading}
+        minLength={1}
+        multiple
+        newSelectionPrefix=""
+        onChange={handleChange}
+        onSearch={handleSearch}
+        onKeyDown={handleSelect}
+        options={resultTags} // Search result (Some tag names)
+        placeholder="tag name"
+        autoFocus={props.autoFocus}
+      />
+    </div>
+  );
+};
+
+export default TagsInput;

+ 7 - 6
packages/app/src/components/Page/TagsInput.jsx → packages/app/src/components/Page/_TagsInput.jsx

@@ -55,16 +55,17 @@ class TagsInput extends React.Component {
   }
 
   handleSelect(e) {
-    if (e.keyCode === 32) { // '32' means ASCII code of 'space'
+    if (e.keyCode === 32 || e.keyCode === 13) { // '32' means ASCII code of 'space'
       e.preventDefault();
-      const { initialItem } = this.tagsInput.state;
-
-      if (initialItem) {
-        this.tagsInput._handleMenuItemSelect(initialItem, e);
+      const { initialItem, selected } = this.tagsInput.current.state;
+      if (initialItem != null) {
+        this.handleChange([...selected, initialItem]);
+        this.forceUpdate();
       }
     }
   }
 
+
   render() {
     return (
       <div className="tag-typeahead">
@@ -82,7 +83,7 @@ class TagsInput extends React.Component {
           onKeyDown={this.handleSelect}
           options={this.state.resultTags} // Search result (Some tag names)
           placeholder="tag name"
-          selectHintOnEnter
+          inputProps={this.inputProps}
           autoFocus={this.props.autoFocus}
         />
       </div>