pl-fe: allow searching folders in SelectBookmarkFolderModal
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -17,7 +17,7 @@ const messages = defineMessages({
|
||||
/** Possible theme names for an Input. */
|
||||
type InputThemes = 'normal' | 'search' | 'transparent'
|
||||
|
||||
interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxLength' | 'onChange' | 'onBlur' | 'type' | 'autoComplete' | 'autoCorrect' | 'autoCapitalize' | 'required' | 'disabled' | 'onClick' | 'readOnly' | 'min' | 'pattern' | 'onKeyDown' | 'onKeyUp' | 'onFocus' | 'onMouseDown' | 'style' | 'id' | 'lang'> {
|
||||
interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxLength' | 'onChange' | 'onBlur' | 'type' | 'autoComplete' | 'autoCorrect' | 'autoCapitalize' | 'required' | 'disabled' | 'onClick' | 'readOnly' | 'min' | 'pattern' | 'onKeyDown' | 'onKeyUp' | 'onFocus' | 'onMouseDown' | 'style' | 'id' | 'lang' | 'title'> {
|
||||
/** Put the cursor into the input on mount. */
|
||||
autoFocus?: boolean;
|
||||
/** The initial text in the input. */
|
||||
|
||||
@ -143,7 +143,7 @@ const getLanguageDropdown = (composeId: string): React.FC<ILanguageDropdown> =>
|
||||
}, [node.current]);
|
||||
|
||||
const isSearching = searchValue !== '';
|
||||
const results = search();
|
||||
const results = useMemo(search, [searchValue]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -289,6 +289,7 @@
|
||||
"bookmark_folders.edit.success": "Bookmark folder edited successfully",
|
||||
"bookmark_folders.new.create_title": "Add folder",
|
||||
"bookmark_folders.new.title_placeholder": "New folder title",
|
||||
"bookmark_folders.new.title_with_search_placeholder": "Search or create new folder",
|
||||
"bookmarks.delete_folder": "Delete folder",
|
||||
"bookmarks.delete_folder.fail": "Failed to delete folder",
|
||||
"bookmarks.delete_folder.success": "Folder deleted",
|
||||
|
||||
@ -151,16 +151,15 @@ const EditBookmarkFolderModal: React.FC<BaseModalProps & EditBookmarkFolderModal
|
||||
onPickEmoji={handleEmojiPick}
|
||||
/>
|
||||
)}
|
||||
<label className='grow'>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
|
||||
<Input
|
||||
type='text'
|
||||
placeholder={label}
|
||||
disabled={isPending}
|
||||
{...name}
|
||||
/>
|
||||
</label>
|
||||
<Input
|
||||
outerClassName='grow'
|
||||
type='text'
|
||||
placeholder={label}
|
||||
title={label}
|
||||
disabled={isPending}
|
||||
{...name}
|
||||
/>
|
||||
</HStack>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import fuzzysort from 'fuzzysort';
|
||||
import { BookmarkFolder } from 'pl-api';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ListItem } from 'pl-fe/components/list';
|
||||
@ -23,12 +25,23 @@ interface SelectBookmarkFolderModalProps {
|
||||
statusId: string;
|
||||
}
|
||||
|
||||
const search = (bookmarkFolders: Array<BookmarkFolder>, term: string) => {
|
||||
if (!term) return bookmarkFolders;
|
||||
|
||||
return fuzzysort.go(term, bookmarkFolders, { key: 'name' }).map(result => result.obj);
|
||||
};
|
||||
|
||||
const SelectBookmarkFolderModal: React.FC<SelectBookmarkFolderModalProps & BaseModalProps> = ({ statusId, onClose }) => {
|
||||
const getStatus = useCallback(makeGetStatus(), []);
|
||||
const status = useAppSelector(state => getStatus(state, { id: statusId }))!;
|
||||
const features = useFeatures();
|
||||
|
||||
const [selectedFolder, setSelectedFolder] = useState(status.bookmark_folder);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
const handleSearchChange: React.ChangeEventHandler<HTMLInputElement> = e => {
|
||||
setSearchTerm(e.target.value);
|
||||
};
|
||||
|
||||
const { isFetching, data: bookmarkFolders } = useBookmarkFolders(data => data);
|
||||
const { data: selectedBookmarkFolders, isPending: fetchingSelectedBookmarkFolders } = useStatusBookmarkFolders(statusId);
|
||||
@ -57,10 +70,18 @@ const SelectBookmarkFolderModal: React.FC<SelectBookmarkFolderModalProps & BaseM
|
||||
}
|
||||
};
|
||||
|
||||
const filteredFolders = useMemo(() => {
|
||||
if (!bookmarkFolders) return [];
|
||||
|
||||
const filtered = search(bookmarkFolders, searchTerm);
|
||||
|
||||
return filtered;
|
||||
}, [bookmarkFolders, searchTerm]);
|
||||
|
||||
let items;
|
||||
|
||||
if (features.bookmarkFoldersMultiple) {
|
||||
items = (bookmarkFolders || []).map((folder) => (
|
||||
items = (filteredFolders).map((folder) => (
|
||||
<ListItem
|
||||
key={folder.id}
|
||||
label={
|
||||
@ -99,7 +120,7 @@ const SelectBookmarkFolderModal: React.FC<SelectBookmarkFolderModalProps & BaseM
|
||||
];
|
||||
|
||||
if (!isFetching) {
|
||||
items.push(...((bookmarkFolders || []).map((folder) => (
|
||||
items.push(...((filteredFolders).map((folder) => (
|
||||
<RadioItem
|
||||
key={folder.id}
|
||||
label={
|
||||
@ -123,7 +144,7 @@ const SelectBookmarkFolderModal: React.FC<SelectBookmarkFolderModalProps & BaseM
|
||||
|
||||
const body = isFetching ? <Spinner /> : (
|
||||
<Stack space={4}>
|
||||
<NewFolderForm />
|
||||
<NewFolderForm search onChange={handleSearchChange} />
|
||||
|
||||
<RadioGroup onChange={onChange}>
|
||||
{items}
|
||||
|
||||
@ -20,17 +20,28 @@ import toast from 'pl-fe/toast';
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
|
||||
label: { id: 'bookmark_folders.new.title_placeholder', defaultMessage: 'New folder title' },
|
||||
labelWithSearch: { id: 'bookmark_folders.new.title_with_search_placeholder', defaultMessage: 'Search or create new folder' },
|
||||
createSuccess: { id: 'bookmark_folders.add.success', defaultMessage: 'Bookmark folder created successfully' },
|
||||
createFail: { id: 'bookmark_folders.add.fail', defaultMessage: 'Failed to create bookmark folder' },
|
||||
});
|
||||
|
||||
const NewFolderForm: React.FC = () => {
|
||||
interface INewFolderForm {
|
||||
search?: boolean;
|
||||
onChange?: React.ChangeEventHandler<HTMLInputElement>;
|
||||
}
|
||||
|
||||
const NewFolderForm: React.FC<INewFolderForm> = ({ search, onChange }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const name = useTextField();
|
||||
|
||||
const { mutate: createBookmarkFolder, isPending } = useCreateBookmarkFolder();
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
name.onChange(e);
|
||||
if (onChange) onChange(e);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<Element>) => {
|
||||
e.preventDefault();
|
||||
createBookmarkFolder({
|
||||
@ -45,21 +56,20 @@ const NewFolderForm: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const label = intl.formatMessage(messages.label);
|
||||
const label = intl.formatMessage(search ? messages.labelWithSearch : messages.label);
|
||||
|
||||
return (
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<HStack space={2} alignItems='center'>
|
||||
<label className='grow'>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
|
||||
<Input
|
||||
type='text'
|
||||
placeholder={label}
|
||||
disabled={isPending}
|
||||
{...name}
|
||||
/>
|
||||
</label>
|
||||
<Input
|
||||
outerClassName='grow'
|
||||
type='text'
|
||||
placeholder={label}
|
||||
title={label}
|
||||
disabled={isPending}
|
||||
{...name}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
|
||||
<Button
|
||||
disabled={isPending}
|
||||
|
||||
Reference in New Issue
Block a user