@selfcommunity/react-ui 0.11.0-alpha.102 → 0.11.0-alpha.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/components/Composer/Layer/AudienceLayer/AudienceLayer.js +25 -12
- package/lib/cjs/components/UserAutocomplete/UserAutocomplete.js +30 -15
- package/lib/esm/components/Composer/Layer/AudienceLayer/AudienceLayer.js +25 -12
- package/lib/esm/components/UserAutocomplete/UserAutocomplete.js +31 -16
- package/lib/umd/react-ui.js +1 -1
- package/package.json +3 -3
|
@@ -102,18 +102,31 @@ const AudienceLayer = react_1.default.forwardRef((props, ref) => {
|
|
|
102
102
|
const handleChangeAudience = (0, react_1.useCallback)((_event, data) => setAudience(data), []);
|
|
103
103
|
const handleAutocompleteOpen = (0, react_1.useCallback)(() => setAutocompleteOpen(true), []);
|
|
104
104
|
const handleAutocompleteClose = (0, react_1.useCallback)(() => setAutocompleteOpen(false), []);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
105
|
+
const isAudienceTypeSelected = (type) => {
|
|
106
|
+
// "ALL" tab is never disabled
|
|
107
|
+
if (type === AudienceTypes.AUDIENCE_ALL)
|
|
108
|
+
return false;
|
|
109
|
+
// Empty value → nothing is disabled
|
|
110
|
+
if (!value || (Array.isArray(value) && value.length === 0))
|
|
111
|
+
return false;
|
|
112
|
+
const valueType = (() => {
|
|
113
|
+
if (Array.isArray(value)) {
|
|
114
|
+
if (value.some((v) => 'color' in v))
|
|
115
|
+
return AudienceTypes.AUDIENCE_TAG;
|
|
116
|
+
return AudienceTypes.AUDIENCE_USERS;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
if ('recurring' in value)
|
|
120
|
+
return AudienceTypes.AUDIENCE_EVENT;
|
|
121
|
+
if ('managed_by' in value)
|
|
122
|
+
return AudienceTypes.AUDIENCE_GROUP;
|
|
123
|
+
return AudienceTypes.AUDIENCE_USERS;
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
// Disable all tabs except the currently selected type
|
|
127
|
+
return type !== valueType;
|
|
128
|
+
};
|
|
129
|
+
return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ ref: ref, className: (0, classnames_1.default)(className, classes.root) }, rest, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogTitle, Object.assign({ className: classes.title }, { children: [(0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ onClick: onClose }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "arrow_back" }) })), (0, jsx_runtime_1.jsx)(material_1.Typography, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.title", defaultMessage: "ui.composer.layer.audience.title" }) }), (0, jsx_runtime_1.jsx)(material_1.Button, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSave }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.save", defaultMessage: "ui.composer.layer.save" }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.DialogContent, Object.assign({ className: classes.content }, { children: [(0, jsx_runtime_1.jsxs)(material_1.Tabs, Object.assign({ value: audience, onChange: handleChangeAudience, "aria-label": "audience type" }, { children: [!taggingRequiredEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { value: AudienceTypes.AUDIENCE_ALL, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "public" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all", defaultMessage: "ui.composer.layer.audience.all" }) })), eventsEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_EVENT), value: AudienceTypes.AUDIENCE_EVENT, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "CalendarIcon" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.event", defaultMessage: "ui.composer.layer.audience.event" }) })), groupsEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_GROUP), value: AudienceTypes.AUDIENCE_GROUP, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "groups" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) })), (0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_TAG), value: AudienceTypes.AUDIENCE_TAG, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "label" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) }), usersTaggingEnabled && ((0, jsx_runtime_1.jsx)(material_1.Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_USERS), value: AudienceTypes.AUDIENCE_USERS, icon: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "people_alt" }), label: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.users", defaultMessage: "ui.composer.layer.audience.users" }) }))] })), (0, jsx_runtime_1.jsxs)(material_1.Typography, Object.assign({ className: classes.message }, { children: [audience === AudienceTypes.AUDIENCE_ALL && !taggingRequiredEnabled && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.layer.audience.all.message" })), audience === AudienceTypes.AUDIENCE_EVENT && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.event.message", defaultMessage: "ui.composer.layer.audience.event.message" })), audience === AudienceTypes.AUDIENCE_GROUP && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.layer.audience.group.message" })), audience === AudienceTypes.AUDIENCE_TAG && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tag.message", defaultMessage: "ui.composer.layer.audience.tag.message" })), audience === AudienceTypes.AUDIENCE_USERS && ((0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.users.message", defaultMessage: "ui.composer.layer.audience.users.message" }))] })), audience === AudienceTypes.AUDIENCE_TAG && ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { className: classes.autocomplete, open: autocompleteOpen, onOpen: handleAutocompleteOpen, onClose: handleAutocompleteClose, multiple: true, options: scAddressingTags || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, handleHomeEndKeys: true, clearIcon: null, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.composer.layer.audience.tags.empty", defaultMessage: "ui.composer.layer.audience.tags.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id, renderTags: (value, getTagProps) => {
|
|
117
130
|
return value.map((option, index) => (0, jsx_runtime_1.jsx)(TagChip_1.default, Object.assign({ tag: option }, getTagProps({ index })), option.id));
|
|
118
131
|
}, renderOption: (props, option, { selected, inputValue }) => {
|
|
119
132
|
const matches = (0, match_1.default)(option.name, inputValue);
|
|
@@ -46,16 +46,18 @@ const UserAutocomplete = (inProps) => {
|
|
|
46
46
|
.filter(Boolean)
|
|
47
47
|
.join(','), [value]);
|
|
48
48
|
// Fetch users excluding selected ones
|
|
49
|
-
const { users, isLoading } = (0, react_core_1.useSCFetchUsers)({ search: inputValue, exclude: excludeIds });
|
|
49
|
+
const { users, isLoading } = (0, react_core_1.useSCFetchUsers)({ search: (inputValue === null || inputValue === void 0 ? void 0 : inputValue.length) >= 3 ? inputValue : '', exclude: excludeIds });
|
|
50
50
|
const filteredUsers = users.filter((u) => !excludeIds.includes(u.id));
|
|
51
51
|
(0, react_1.useEffect)(() => {
|
|
52
52
|
onChange === null || onChange === void 0 ? void 0 : onChange(value);
|
|
53
53
|
setTextAreaValue(value.map((u) => (typeof u === 'object' ? u.username : u)).join('\n'));
|
|
54
54
|
}, [value]);
|
|
55
55
|
// Handlers
|
|
56
|
-
const handleOpen = () => {
|
|
57
|
-
filteredUsers.length > 0
|
|
58
|
-
|
|
56
|
+
const handleOpen = (0, react_1.useCallback)(() => {
|
|
57
|
+
if (filteredUsers.length > 0) {
|
|
58
|
+
setOpen(true);
|
|
59
|
+
}
|
|
60
|
+
}, [filteredUsers]);
|
|
59
61
|
const handleClose = () => {
|
|
60
62
|
setOpen(false);
|
|
61
63
|
};
|
|
@@ -67,24 +69,37 @@ const UserAutocomplete = (inProps) => {
|
|
|
67
69
|
.split(/\s|,|\n/)
|
|
68
70
|
.map((s) => s.trim())
|
|
69
71
|
.filter(Boolean)));
|
|
70
|
-
let resolvedUsers = [];
|
|
71
72
|
try {
|
|
72
73
|
if (names.length > 0) {
|
|
73
74
|
const resp = yield api_services_1.UserService.matchUsernames(names);
|
|
74
|
-
const
|
|
75
|
-
resolvedUsers =
|
|
75
|
+
const resolvedUsernames = new Set(resp.map((u) => u.username));
|
|
76
|
+
const resolvedUsers = resp.filter((u) => resolvedUsernames.has(u.username));
|
|
77
|
+
setValue(resolvedUsers);
|
|
78
|
+
setTextAreaValue(resolvedUsers.map((u) => u.username).join('\n'));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
setValue([]);
|
|
82
|
+
setTextAreaValue('');
|
|
76
83
|
}
|
|
77
84
|
}
|
|
78
85
|
catch (err) {
|
|
79
|
-
console.error(`Failed fetching
|
|
80
|
-
|
|
86
|
+
console.error(`Failed fetching matching usernames`, err);
|
|
87
|
+
setValue([]);
|
|
88
|
+
setTextAreaValue('');
|
|
81
89
|
}
|
|
82
|
-
setValue(resolvedUsers);
|
|
83
|
-
setTextAreaValue(e.target.value);
|
|
84
90
|
});
|
|
85
|
-
|
|
91
|
+
const filterOptions = (0, react_1.useCallback)((options, state) => {
|
|
92
|
+
const search = state.inputValue.toLowerCase();
|
|
93
|
+
return options.filter((option) => {
|
|
94
|
+
var _a, _b;
|
|
95
|
+
const usernameMatch = (_a = option.username) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search);
|
|
96
|
+
const nameMatch = (_b = option.real_name) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(search);
|
|
97
|
+
return usernameMatch || nameMatch;
|
|
98
|
+
});
|
|
99
|
+
}, []);
|
|
100
|
+
return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ className: classes.root }, { children: [(0, jsx_runtime_1.jsx)(AutocompleteRoot, Object.assign({ multiple: true, className: classes.autocompleteRoot, open: open, onOpen: handleOpen, onClose: handleClose, options: filteredUsers || [], filterOptions: filterOptions, getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
|
|
86
101
|
setInputValue(newInputValue);
|
|
87
|
-
}, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => option.id ===
|
|
102
|
+
}, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => (option ? val.id === option.id : false), renderTags: (value, getTagProps) => value.map((option, index) => {
|
|
88
103
|
const username = typeof option === 'string' ? option : option.username;
|
|
89
104
|
const avatar = typeof option === 'string' ? '' : option.avatar;
|
|
90
105
|
const id = typeof option === 'string' ? `fallback-${option}` : option.id;
|
|
@@ -92,8 +107,8 @@ const UserAutocomplete = (inProps) => {
|
|
|
92
107
|
}), renderOption: (props, option, { inputValue }) => {
|
|
93
108
|
const matches = (0, match_1.default)(option.username, inputValue);
|
|
94
109
|
const parts = (0, parse_1.default)(option.username, matches);
|
|
95
|
-
return ((0, jsx_runtime_1.jsxs)(material_1.Box, Object.assign({ component: "li" }, props, { children: [(0, jsx_runtime_1.jsx)(material_1.Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: parts.map((part, index) => ((0, jsx_runtime_1.jsx)(material_1.Typography,
|
|
96
|
-
}, renderInput: (params) => ((0, jsx_runtime_1.jsx)(material_1.TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [isLoading
|
|
110
|
+
return ((0, jsx_runtime_1.jsxs)(material_1.Box, Object.assign({ component: "li" }, props, { children: [(0, jsx_runtime_1.jsx)(material_1.Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: parts.map((part, index) => ((0, jsx_runtime_1.jsx)(material_1.Typography, { children: part.text }, index))) })] })));
|
|
111
|
+
}, renderInput: (params) => ((0, jsx_runtime_1.jsx)(material_1.TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: filteredUsers.length > 0 ? ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [isLoading && (0, jsx_runtime_1.jsx)(material_1.CircularProgress, { color: "inherit", size: 20 }), params.InputProps.endAdornment] })) : null }) }))) }, rest)), (0, jsx_runtime_1.jsx)(material_1.Typography, Object.assign({ variant: "body2", color: "primary", className: classes.info }, { children: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.userAutocomplete.textarea.info", defaultMessage: "ui.userAutocomplete.textarea.info", values: {
|
|
97
112
|
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
98
113
|
// @ts-ignore
|
|
99
114
|
icon: (...chunks) => (0, jsx_runtime_1.jsx)(material_1.Icon, { children: chunks })
|
|
@@ -99,18 +99,31 @@ const AudienceLayer = React.forwardRef((props, ref) => {
|
|
|
99
99
|
const handleChangeAudience = useCallback((_event, data) => setAudience(data), []);
|
|
100
100
|
const handleAutocompleteOpen = useCallback(() => setAutocompleteOpen(true), []);
|
|
101
101
|
const handleAutocompleteClose = useCallback(() => setAutocompleteOpen(false), []);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
const isAudienceTypeSelected = (type) => {
|
|
103
|
+
// "ALL" tab is never disabled
|
|
104
|
+
if (type === AudienceTypes.AUDIENCE_ALL)
|
|
105
|
+
return false;
|
|
106
|
+
// Empty value → nothing is disabled
|
|
107
|
+
if (!value || (Array.isArray(value) && value.length === 0))
|
|
108
|
+
return false;
|
|
109
|
+
const valueType = (() => {
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
if (value.some((v) => 'color' in v))
|
|
112
|
+
return AudienceTypes.AUDIENCE_TAG;
|
|
113
|
+
return AudienceTypes.AUDIENCE_USERS;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
if ('recurring' in value)
|
|
117
|
+
return AudienceTypes.AUDIENCE_EVENT;
|
|
118
|
+
if ('managed_by' in value)
|
|
119
|
+
return AudienceTypes.AUDIENCE_GROUP;
|
|
120
|
+
return AudienceTypes.AUDIENCE_USERS;
|
|
121
|
+
}
|
|
122
|
+
})();
|
|
123
|
+
// Disable all tabs except the currently selected type
|
|
124
|
+
return type !== valueType;
|
|
125
|
+
};
|
|
126
|
+
return (_jsxs(Root, Object.assign({ ref: ref, className: classNames(className, classes.root) }, rest, { children: [_jsxs(DialogTitle, Object.assign({ className: classes.title }, { children: [_jsx(IconButton, Object.assign({ onClick: onClose }, { children: _jsx(Icon, { children: "arrow_back" }) })), _jsx(Typography, { children: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.title", defaultMessage: "ui.composer.layer.audience.title" }) }), _jsx(Button, Object.assign({ size: "small", color: "secondary", variant: "contained", onClick: handleSave }, { children: _jsx(FormattedMessage, { id: "ui.composer.layer.save", defaultMessage: "ui.composer.layer.save" }) }))] })), _jsxs(DialogContent, Object.assign({ className: classes.content }, { children: [_jsxs(Tabs, Object.assign({ value: audience, onChange: handleChangeAudience, "aria-label": "audience type" }, { children: [!taggingRequiredEnabled && (_jsx(Tab, { value: AudienceTypes.AUDIENCE_ALL, icon: _jsx(Icon, { children: "public" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.all", defaultMessage: "ui.composer.layer.audience.all" }) })), eventsEnabled && (_jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_EVENT), value: AudienceTypes.AUDIENCE_EVENT, icon: _jsx(Icon, { children: "CalendarIcon" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.event", defaultMessage: "ui.composer.layer.audience.event" }) })), groupsEnabled && (_jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_GROUP), value: AudienceTypes.AUDIENCE_GROUP, icon: _jsx(Icon, { children: "groups" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.group", defaultMessage: "ui.composer.layer.audience.group" }) })), _jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_TAG), value: AudienceTypes.AUDIENCE_TAG, icon: _jsx(Icon, { children: "label" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.tag", defaultMessage: "ui.composer.layer.audience.tag" }) }), usersTaggingEnabled && (_jsx(Tab, { disabled: isAudienceTypeSelected(AudienceTypes.AUDIENCE_USERS), value: AudienceTypes.AUDIENCE_USERS, icon: _jsx(Icon, { children: "people_alt" }), label: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.users", defaultMessage: "ui.composer.layer.audience.users" }) }))] })), _jsxs(Typography, Object.assign({ className: classes.message }, { children: [audience === AudienceTypes.AUDIENCE_ALL && !taggingRequiredEnabled && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.all.message", defaultMessage: "ui.composer.layer.audience.all.message" })), audience === AudienceTypes.AUDIENCE_EVENT && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.event.message", defaultMessage: "ui.composer.layer.audience.event.message" })), audience === AudienceTypes.AUDIENCE_GROUP && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.group.message", defaultMessage: "ui.composer.layer.audience.group.message" })), audience === AudienceTypes.AUDIENCE_TAG && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.tag.message", defaultMessage: "ui.composer.layer.audience.tag.message" })), audience === AudienceTypes.AUDIENCE_USERS && (_jsx(FormattedMessage, { id: "ui.composer.layer.audience.users.message", defaultMessage: "ui.composer.layer.audience.users.message" }))] })), audience === AudienceTypes.AUDIENCE_TAG && (_jsx(Autocomplete, { className: classes.autocomplete, open: autocompleteOpen, onOpen: handleAutocompleteOpen, onClose: handleAutocompleteClose, multiple: true, options: scAddressingTags || [], getOptionLabel: (option) => option.name || '', value: value, selectOnFocus: true, clearOnBlur: true, handleHomeEndKeys: true, clearIcon: null, noOptionsText: _jsx(FormattedMessage, { id: "ui.composer.layer.audience.tags.empty", defaultMessage: "ui.composer.layer.audience.tags.empty" }), onChange: handleChange, isOptionEqualToValue: (option, value) => value.id === option.id, renderTags: (value, getTagProps) => {
|
|
114
127
|
return value.map((option, index) => _jsx(TagChip, Object.assign({ tag: option }, getTagProps({ index })), option.id));
|
|
115
128
|
}, renderOption: (props, option, { selected, inputValue }) => {
|
|
116
129
|
const matches = match(option.name, inputValue);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter, __rest } from "tslib";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React, { Fragment, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
4
|
import { FormattedMessage } from 'react-intl';
|
|
5
5
|
import parse from 'autosuggest-highlight/parse';
|
|
6
6
|
import match from 'autosuggest-highlight/match';
|
|
@@ -44,16 +44,18 @@ const UserAutocomplete = (inProps) => {
|
|
|
44
44
|
.filter(Boolean)
|
|
45
45
|
.join(','), [value]);
|
|
46
46
|
// Fetch users excluding selected ones
|
|
47
|
-
const { users, isLoading } = useSCFetchUsers({ search: inputValue, exclude: excludeIds });
|
|
47
|
+
const { users, isLoading } = useSCFetchUsers({ search: (inputValue === null || inputValue === void 0 ? void 0 : inputValue.length) >= 3 ? inputValue : '', exclude: excludeIds });
|
|
48
48
|
const filteredUsers = users.filter((u) => !excludeIds.includes(u.id));
|
|
49
49
|
useEffect(() => {
|
|
50
50
|
onChange === null || onChange === void 0 ? void 0 : onChange(value);
|
|
51
51
|
setTextAreaValue(value.map((u) => (typeof u === 'object' ? u.username : u)).join('\n'));
|
|
52
52
|
}, [value]);
|
|
53
53
|
// Handlers
|
|
54
|
-
const handleOpen = () => {
|
|
55
|
-
filteredUsers.length > 0
|
|
56
|
-
|
|
54
|
+
const handleOpen = useCallback(() => {
|
|
55
|
+
if (filteredUsers.length > 0) {
|
|
56
|
+
setOpen(true);
|
|
57
|
+
}
|
|
58
|
+
}, [filteredUsers]);
|
|
57
59
|
const handleClose = () => {
|
|
58
60
|
setOpen(false);
|
|
59
61
|
};
|
|
@@ -65,24 +67,37 @@ const UserAutocomplete = (inProps) => {
|
|
|
65
67
|
.split(/\s|,|\n/)
|
|
66
68
|
.map((s) => s.trim())
|
|
67
69
|
.filter(Boolean)));
|
|
68
|
-
let resolvedUsers = [];
|
|
69
70
|
try {
|
|
70
71
|
if (names.length > 0) {
|
|
71
72
|
const resp = yield UserService.matchUsernames(names);
|
|
72
|
-
const
|
|
73
|
-
resolvedUsers =
|
|
73
|
+
const resolvedUsernames = new Set(resp.map((u) => u.username));
|
|
74
|
+
const resolvedUsers = resp.filter((u) => resolvedUsernames.has(u.username));
|
|
75
|
+
setValue(resolvedUsers);
|
|
76
|
+
setTextAreaValue(resolvedUsers.map((u) => u.username).join('\n'));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
setValue([]);
|
|
80
|
+
setTextAreaValue('');
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
catch (err) {
|
|
77
|
-
console.error(`Failed fetching
|
|
78
|
-
|
|
84
|
+
console.error(`Failed fetching matching usernames`, err);
|
|
85
|
+
setValue([]);
|
|
86
|
+
setTextAreaValue('');
|
|
79
87
|
}
|
|
80
|
-
setValue(resolvedUsers);
|
|
81
|
-
setTextAreaValue(e.target.value);
|
|
82
88
|
});
|
|
83
|
-
|
|
89
|
+
const filterOptions = useCallback((options, state) => {
|
|
90
|
+
const search = state.inputValue.toLowerCase();
|
|
91
|
+
return options.filter((option) => {
|
|
92
|
+
var _a, _b;
|
|
93
|
+
const usernameMatch = (_a = option.username) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(search);
|
|
94
|
+
const nameMatch = (_b = option.real_name) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(search);
|
|
95
|
+
return usernameMatch || nameMatch;
|
|
96
|
+
});
|
|
97
|
+
}, []);
|
|
98
|
+
return (_jsxs(Root, Object.assign({ className: classes.root }, { children: [_jsx(AutocompleteRoot, Object.assign({ multiple: true, className: classes.autocompleteRoot, open: open, onOpen: handleOpen, onClose: handleClose, options: filteredUsers || [], filterOptions: filterOptions, getOptionLabel: (option) => option.username || '', value: value, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
|
|
84
99
|
setInputValue(newInputValue);
|
|
85
|
-
}, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: _jsx(FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => option.id ===
|
|
100
|
+
}, selectOnFocus: true, clearOnBlur: true, blurOnSelect: true, handleHomeEndKeys: true, clearIcon: null, disabled: disabled || isLoading, noOptionsText: _jsx(FormattedMessage, { id: "ui.userAutocomplete.empty", defaultMessage: "ui.userAutocomplete.empty" }), onChange: handleChange, isOptionEqualToValue: (option, val) => (option ? val.id === option.id : false), renderTags: (value, getTagProps) => value.map((option, index) => {
|
|
86
101
|
const username = typeof option === 'string' ? option : option.username;
|
|
87
102
|
const avatar = typeof option === 'string' ? '' : option.avatar;
|
|
88
103
|
const id = typeof option === 'string' ? `fallback-${option}` : option.id;
|
|
@@ -90,8 +105,8 @@ const UserAutocomplete = (inProps) => {
|
|
|
90
105
|
}), renderOption: (props, option, { inputValue }) => {
|
|
91
106
|
const matches = match(option.username, inputValue);
|
|
92
107
|
const parts = parse(option.username, matches);
|
|
93
|
-
return (_jsxs(Box, Object.assign({ component: "li" }, props, { children: [_jsx(Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), _jsx(React.Fragment, { children: parts.map((part, index) => (_jsx(Typography,
|
|
94
|
-
}, renderInput: (params) => (_jsx(TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: (_jsxs(Fragment, { children: [isLoading
|
|
108
|
+
return (_jsxs(Box, Object.assign({ component: "li" }, props, { children: [_jsx(Avatar, { alt: option.username, src: option.avatar, sx: { marginRight: 1 } }), _jsx(React.Fragment, { children: parts.map((part, index) => (_jsx(Typography, { children: part.text }, index))) })] })));
|
|
109
|
+
}, renderInput: (params) => (_jsx(TextField, Object.assign({}, params, TextFieldProps, { margin: "dense", InputProps: Object.assign(Object.assign({}, params.InputProps), { autoComplete: 'off', endAdornment: filteredUsers.length > 0 ? (_jsxs(Fragment, { children: [isLoading && _jsx(CircularProgress, { color: "inherit", size: 20 }), params.InputProps.endAdornment] })) : null }) }))) }, rest)), _jsx(Typography, Object.assign({ variant: "body2", color: "primary", className: classes.info }, { children: _jsx(FormattedMessage, { id: "ui.userAutocomplete.textarea.info", defaultMessage: "ui.userAutocomplete.textarea.info", values: {
|
|
95
110
|
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
96
111
|
// @ts-ignore
|
|
97
112
|
icon: (...chunks) => _jsx(Icon, { children: chunks })
|