ui: special behavior of UserSelectAutoComplete when multi (#286)

This commit is contained in:
无限UCW authored and GitHub committed 2022-01-09 00:46:27 +08:00
1 parent f384ae70fb
commit 538862de07
4 files changed
+89 -15

No files matched your search

@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { QueryClient, QueryClientProvider } from 'react-query';
import { assign } from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject';
import AutoComplete from '.';
@@ -13,19 +14,37 @@ export default class UserSelectAutoComplete extends AutoComplete {
classes: 'user-select',
...options,
});
this.client = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
retry: false,
staleTime: Infinity,
},
},
});
}
value() {
if (this.options.multi) return this.ref?.getSelectedItems().map((item) => item._id) ?? this.$dom.val();
return this.ref?.getSelectedItems()[0] ?? null;
}
attach() {
const value = this.$dom.val();
ReactDOM.render(
<UserSelectAutoCompleteFC
ref={(ref) => { this.ref = ref; }}
height="34px"
defaultItems={value}
onChange={this.onChange}
multi={this.options.multi}
freeSolo={this.options.multi}
/>,
<QueryClientProvider client={this.client}>
<UserSelectAutoCompleteFC
ref={(ref) => { this.ref = ref; }}
height="34px"
defaultItems={value}
onChange={this.onChange}
multi={this.options.multi}
freeSolo={this.options.multi}
/>
</QueryClientProvider>,
this.container,
);
}
@@ -13,6 +13,8 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
// if you need fix height, set to at least "30px"
// for Hydro, no less then "34px" can be better
const height = props.height ?? 'auto';
const disabled = props.disabled ?? false;
const disabledHint = props.disabledHint ?? '';
const listStyle = props.listStyle ?? {};
const itemsFn = props.itemsFn ?? (async () => []);
const renderItem = props.renderItem ?? ((item) => item);
@@ -22,7 +24,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
const multi = props.multi ?? false;
const rawDefaultItems = props.defaultItems ?? [];
const defaultItems = typeof rawDefaultItems === 'string'
? props.defaultItems.split(',').map((i) => i.trim()).filter((i) => i.length > 0) : rawDefaultItems;
? rawDefaultItems.split(',').map((i) => i.trim()).filter((i) => i.length > 0) : rawDefaultItems;
const allowEmptyQuery = props.allowEmptyQuery ?? false;
const freeSolo = props.freeSolo ?? false;
const freeSoloConverter = freeSolo ? props.freeSoloConverter ?? ((i) => i) : ((i) => i);
@@ -188,7 +190,7 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
setFocused(true);
inputRef.current?.focus();
},
}));
}), [selected, selectedKeys, inputRef, multi]);
return (
<div style={{ display: 'inline-block', width: '100%' }}>
@@ -202,9 +204,17 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
<Icon name="close" onClick={() => toggleItem(item)} />
</div>
))}
{disabled && (
<input
disabled
autoComplete="off"
value={disabledHint}
/>
)}
<input
ref={inputRef}
autoComplete="off"
hidden={disabled}
onChange={(e) => {
dispatchChange();
handleInputChange(e);
@@ -243,6 +253,8 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) {
AutoComplete.propTypes = {
width: PropTypes.string,
height: PropTypes.string,
disabled: PropTypes.bool,
disabledHint: PropTypes.string,
listStyle: PropTypes.object,
itemsFn: PropTypes.func.isRequired,
itemKey: PropTypes.func,
@@ -259,6 +271,8 @@ AutoComplete.propTypes = {
AutoComplete.defaultProps = {
width: '100%',
height: 'auto',
disabled: false,
disabledHint: '',
listStyle: {},
renderItem: (item) => item,
itemText: (item) => item,
@@ -1,10 +1,44 @@
import React, { forwardRef } from 'react';
import React, {
forwardRef, createRef, useImperativeHandle, useRef, useEffect,
} from 'react';
import { useQuery } from 'react-query';
import PropTypes from 'prop-types';
import api, { gql } from 'vj/utils/api';
import AutoComplete from './AutoComplete';
// eslint-disable-next-line prefer-arrow-callback
const UserSelectAutoComplete = forwardRef(function UserSelectAutoComplete(props, ref) {
const multi = props.multi ?? false;
const rawDefaultItems = props.defaultItems ?? '';
const defaultItems = multi ? rawDefaultItems
.split(',')
.map((i) => i.trim())
.filter((i) => i.length > 0)
.map((i) => +i) : rawDefaultItems;
const comRef = createRef();
const itemsBox = useRef([]);
const { isLoading } = useQuery(['default_user', defaultItems], async () => {
if (!multi || defaultItems.length === 0) return;
const items = await api(gql`
users(ids: ${defaultItems}) {
_id
uname
}
`, ['data', 'users']);
itemsBox.current = items;
});
useEffect(() => {
if (multi && defaultItems.length > 0) {
comRef.current.setSelectedItems(itemsBox.current);
}
}, [itemsBox.current]);
useImperativeHandle(ref, () => ({
...comRef.current,
}));
const itemsFn = (query) => api(gql`
users(search: ${query}) {
_id
@@ -15,6 +49,8 @@ const UserSelectAutoComplete = forwardRef(function UserSelectAutoComplete(props,
const itemText = (user) => user.uname || user;
const itemKey = (user) => user._id;
const renderItem = (user) => (
<div className="media">
<div className="media__left medium">
@@ -29,11 +65,15 @@ const UserSelectAutoComplete = forwardRef(function UserSelectAutoComplete(props,
return (
<AutoComplete
ref={ref}
ref={comRef}
{...props}
disabled={isLoading}
disabledHint="Loading..."
itemsFn={itemsFn}
itemText={itemText}
itemKey={multi ? itemKey : itemText}
renderItem={renderItem}
{...props}
defaultItems={multi ? [] : rawDefaultItems}
/>
);
});
@@ -44,7 +84,7 @@ UserSelectAutoComplete.propTypes = {
listStyle: PropTypes.object,
onChange: PropTypes.func.isRequired,
multi: PropTypes.bool,
defaultItems: PropTypes.oneOfType([(PropTypes.arrayOf(PropTypes.any)), PropTypes.string]),
defaultItems: PropTypes.string,
allowEmptyQuery: PropTypes.bool,
freeSolo: PropTypes.bool,
freeSoloConverter: PropTypes.func,
@@ -55,7 +95,7 @@ UserSelectAutoComplete.defaultProps = {
height: 'auto',
listStyle: {},
multi: false,
defaultItems: [],
defaultItems: '',
allowEmptyQuery: false,
freeSolo: false,
freeSoloConverter: (input) => input,
+1
View File
@@ -115,6 +115,7 @@
"markdown-it-table-of-contents": "^0.6.0",
"mongodb": "^3.7.3",
"nunjucks": "^3.2.3",
"react-query": "^3.34.7",
"streamsaver": "^2.0.5",
"xss": "^1.0.10"
}