/* Copyright (C) 2019 Monomax Software Pty Ltd
*
* This file is part of Dnote.
*
* Dnote is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dnote is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Dnote. If not, see .
*/
import React, { useState, useRef, useEffect } from 'react';
import classnames from 'classnames';
import { booksToOptions, filterOptions, Option } from 'jslib/helpers/select';
import { KEYCODE_BACKSPACE } from 'jslib/helpers/keyboard';
import { useSearchMenuKeydown, useScrollToFocused } from 'web/libs/hooks/dom';
import { useSelector } from '../../store';
import PopoverContent from '../Common/Popover/PopoverContent';
import CloseIcon from '../Icons/Close';
import { usePrevious } from 'web/libs/hooks';
import styles from './MultiSelect.scss';
function getTextInputWidth(term: string, active: boolean) {
if (!active && term === '') {
return '100%';
}
const val = 14 + term.length * 8;
return `${val}px`;
}
interface Props {
options: Option[];
currentOptions: Option[];
setCurrentOptions: (Option) => void;
placeholder?: string;
disabled?: boolean;
textInputId?: string;
wrapperClassName?: string;
inputInnerRef?: React.MutableRefObject;
}
// TODO: Make a generic Select component that works for both single and multiple selection
// by passing of a flag
const MultiSelect: React.FunctionComponent = ({
options,
currentOptions,
setCurrentOptions,
textInputId,
placeholder,
disabled,
wrapperClassName,
inputInnerRef
}) => {
const [isOpen, setIsOpen] = useState(false);
const [focusedIdx, setFocusedIdx] = useState(0);
const [focusedOptEl, setFocusedOptEl] = useState(null);
const [term, setTerm] = useState('');
const wrapperRef = useRef(null);
const inputRef = useRef(null);
const listRef = useRef(null);
const currentValues = currentOptions.map(o => {
return o.value;
});
const possibleOptions = options.filter(o => {
return currentValues.indexOf(o.value) === -1;
});
const filteredOptions = filterOptions(possibleOptions, term, false);
function appendOption(o: Option | undefined) {
if (!o) {
return;
}
setTerm('');
const newVal = [...currentOptions, o];
setCurrentOptions(newVal);
}
function removeOption(o: Option) {
setTerm('');
const newVal = currentOptions.filter(opt => {
return opt.value !== o.value;
});
setCurrentOptions(newVal);
}
function popOption() {
if (currentOptions.length === 0) {
return;
}
const newVal = currentOptions.slice(0, -1);
setCurrentOptions(newVal);
}
useSearchMenuKeydown({
options: filteredOptions,
containerEl: wrapperRef.current,
focusedIdx,
setFocusedIdx,
onKeydownSelect: appendOption,
disabled: !isOpen || disabled
});
useScrollToFocused({
shouldScroll: true,
focusedOptEl,
containerEl: listRef.current
});
useEffect(() => {
if (!isOpen) {
inputRef.current.blur();
setTerm('');
}
}, [isOpen]);
// useEffect(() => {
// if (term !== '' && !isOpen) {
// setIsOpen(true);
// }
// }, [term, isOpen]);
const active = currentOptions.length > 0;
const textInputWidth = getTextInputWidth(term, active);
return (
{
if (inputRef.current) {
inputRef.current.focus();
}
// setIsOpen(!isOpen);
}}
>