mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: special behavior of UserSelectAutoComplete when multi (#286)
This commit is contained in:
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,
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user