From 538862de07f2de2d12115028a3f4dbd52d7204d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=A0=E9=99=90UCW?= <45730483+wuxianucw@users.noreply.github.com> Date: Sun, 9 Jan 2022 00:46:27 +0800 Subject: [PATCH] ui: special behavior of `UserSelectAutoComplete` when `multi` (#286) --- .../autocomplete/UserSelectAutoComplete.jsx | 35 ++++++++++--- .../autocomplete/components/AutoComplete.jsx | 18 ++++++- .../components/UserSelectAutoComplete.jsx | 50 +++++++++++++++++-- packages/ui-default/package.json | 1 + 4 files changed, 89 insertions(+), 15 deletions(-) diff --git a/packages/ui-default/components/autocomplete/UserSelectAutoComplete.jsx b/packages/ui-default/components/autocomplete/UserSelectAutoComplete.jsx index 62d493e35..4b56e034f 100644 --- a/packages/ui-default/components/autocomplete/UserSelectAutoComplete.jsx +++ b/packages/ui-default/components/autocomplete/UserSelectAutoComplete.jsx @@ -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( - { this.ref = ref; }} - height="34px" - defaultItems={value} - onChange={this.onChange} - multi={this.options.multi} - freeSolo={this.options.multi} - />, + + { this.ref = ref; }} + height="34px" + defaultItems={value} + onChange={this.onChange} + multi={this.options.multi} + freeSolo={this.options.multi} + /> + , this.container, ); } diff --git a/packages/ui-default/components/autocomplete/components/AutoComplete.jsx b/packages/ui-default/components/autocomplete/components/AutoComplete.jsx index 92debe2d8..b5dd3ab1c 100644 --- a/packages/ui-default/components/autocomplete/components/AutoComplete.jsx +++ b/packages/ui-default/components/autocomplete/components/AutoComplete.jsx @@ -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 (
@@ -202,9 +204,17 @@ const AutoComplete = forwardRef(function AutoComplete(props, ref) { toggleItem(item)} />
))} + {disabled && ( + + )} { 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, diff --git a/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.jsx b/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.jsx index bdc532874..8c91ebfe7 100644 --- a/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.jsx +++ b/packages/ui-default/components/autocomplete/components/UserSelectAutoComplete.jsx @@ -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) => (
@@ -29,11 +65,15 @@ const UserSelectAutoComplete = forwardRef(function UserSelectAutoComplete(props, return ( ); }); @@ -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, diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 8adc69646..b91cc38d6 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -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" }