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