mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
core&ui: assignment auto complete (#1061)
This commit is contained in:
1 parent
f32cee2200
commit
564ff1b02d
6 files changed
+184
-8
No files matched your search
@@ -0,0 +1,24 @@
|
||||
import AutoComplete, { AutoCompleteOptions } from '.';
|
||||
import AssignSelectAutoCompleteFC from './components/AssignSelectAutoComplete';
|
||||
|
||||
export default class AssignSelectAutoComplete<Multi extends boolean> extends AutoComplete {
|
||||
static DOMAttachKey = 'ucwAssignSelectAutoCompleteInstance';
|
||||
|
||||
constructor($dom, options: AutoCompleteOptions<Multi> = {}) {
|
||||
super($dom, {
|
||||
classes: 'assign-select',
|
||||
component: AssignSelectAutoCompleteFC,
|
||||
props: {
|
||||
multi: true,
|
||||
height: 'auto',
|
||||
},
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
value(): string {
|
||||
return this.ref?.getSelectedItemKeys().join(',') ?? this.$dom.val();
|
||||
}
|
||||
}
|
||||
|
||||
window.Hydro.components.AssignSelectAutoComplete = AssignSelectAutoComplete;
|
||||
@@ -0,0 +1,118 @@
|
||||
import { AutoComplete, AutoCompleteHandle, AutoCompleteProps } from '@hydrooj/components';
|
||||
import type { GDoc, Udoc } from 'hydrooj/src/interface';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { forwardRef } from 'react';
|
||||
import { api } from 'vj/utils';
|
||||
|
||||
interface AssignItem {
|
||||
type: 'user' | 'group';
|
||||
key: string;
|
||||
name: string;
|
||||
displayName?: string;
|
||||
avatarUrl?: string;
|
||||
uids?: number[];
|
||||
invalid?: boolean;
|
||||
}
|
||||
|
||||
const toUserItem = (user: Udoc): AssignItem => ({
|
||||
type: 'user',
|
||||
key: user._id.toString(),
|
||||
name: user.uname,
|
||||
displayName: user.displayName,
|
||||
avatarUrl: user.avatarUrl,
|
||||
});
|
||||
|
||||
const toGroupItem = (group: GDoc): AssignItem => ({
|
||||
type: 'group',
|
||||
key: group.name,
|
||||
name: group.name,
|
||||
uids: group.uids,
|
||||
});
|
||||
|
||||
const AssignSelectAutoComplete = forwardRef<AutoCompleteHandle<AssignItem>, AutoCompleteProps<AssignItem>>((props, ref) => (
|
||||
<AutoComplete<AssignItem>
|
||||
ref={ref as any}
|
||||
cacheKey="assign"
|
||||
queryItems={async (query) => {
|
||||
const [users, groups] = await Promise.all([
|
||||
api('users', { search: query }, ['_id', 'uname', 'displayName', 'avatarUrl']),
|
||||
api('groups', { search: query }, ['name', 'uids']),
|
||||
]);
|
||||
const userItems: AssignItem[] = users.map((user: Udoc) => toUserItem(user));
|
||||
const groupItems: AssignItem[] = groups.map((group: GDoc) => toGroupItem(group));
|
||||
return [...groupItems, ...userItems];
|
||||
}}
|
||||
fetchItems={async (keys) => {
|
||||
const isUserId = (k: string) => /^-?[0-9]+$/.test(k);
|
||||
const userIds = keys.filter((k) => isUserId(k));
|
||||
const groupNames = keys.filter((k) => !isUserId(k));
|
||||
|
||||
const [users, groups]: [Udoc[], GDoc[]] = await Promise.all([
|
||||
userIds.length > 0 ? api('users', { auto: userIds }, ['_id', 'uname', 'displayName']) : [],
|
||||
groupNames.length > 0 ? api('groups', { names: groupNames }, ['name', 'uids']) : [],
|
||||
]);
|
||||
|
||||
const userItems: AssignItem[] = users.map((user: Udoc) => toUserItem(user));
|
||||
const groupItems: AssignItem[] = keys
|
||||
.filter((key) => !isUserId(key))
|
||||
.map((key) => {
|
||||
const group = groups.find((g) => g.name === key);
|
||||
return group ? toGroupItem(group) : { type: 'group', key, name: key, invalid: true };
|
||||
});
|
||||
|
||||
return [...groupItems, ...userItems];
|
||||
}}
|
||||
itemText={(item) => {
|
||||
if (item.type === 'group') {
|
||||
if (item.invalid) return `${item.name} (invalid)`;
|
||||
return `${item.name} (${item.uids?.length || 0} users)`;
|
||||
}
|
||||
return item.name + (item.displayName ? ` (${item.displayName})` : '');
|
||||
}}
|
||||
itemKey={(item) => item.key}
|
||||
renderItem={(item) => (
|
||||
<div className="media">
|
||||
{item.type === 'user' && (
|
||||
<div className="media__left medium">
|
||||
<img className="small user-profile-avatar" alt="" src={item.avatarUrl} width="30" height="30" />
|
||||
</div>
|
||||
)}
|
||||
<div className="media__body medium">
|
||||
<div className="assign-select__name">
|
||||
{item.name}{item.type === 'user' && item.displayName && ` (${item.displayName})`}
|
||||
</div>
|
||||
<div className="assign-select__desc">
|
||||
{item.type === 'group' ? `Group • ${item.uids?.length || 0} users` : `User • UID = ${item.key}`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{...{
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
listStyle: {},
|
||||
multi: true,
|
||||
selectedKeys: [],
|
||||
allowEmptyQuery: false,
|
||||
freeSolo: false,
|
||||
freeSoloConverter: (input) => input,
|
||||
...props,
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
AssignSelectAutoComplete.propTypes = {
|
||||
width: PropTypes.string,
|
||||
height: PropTypes.string,
|
||||
listStyle: PropTypes.object,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
multi: PropTypes.bool,
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.string),
|
||||
allowEmptyQuery: PropTypes.bool,
|
||||
freeSolo: PropTypes.bool,
|
||||
freeSoloConverter: PropTypes.func,
|
||||
};
|
||||
|
||||
AssignSelectAutoComplete.displayName = 'AssignSelectAutoComplete';
|
||||
|
||||
export default AssignSelectAutoComplete;
|
||||
Reference in new issue
Block a user