2021-02-04 19:15:23 +01:00
|
|
|
import React, { useState, useRef, useEffect } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
import DropdownSearchList from '../dropdown-search-list';
|
|
|
|
import TextField from '../../../components/ui/text-field';
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
const characterWidthMap = {
|
2021-12-09 20:06:24 +01:00
|
|
|
1: 5.86,
|
|
|
|
2: 10.05,
|
|
|
|
3: 10.45,
|
|
|
|
4: 11.1,
|
|
|
|
5: 10,
|
|
|
|
6: 10.06,
|
|
|
|
7: 9.17,
|
|
|
|
8: 10.28,
|
|
|
|
9: 10.06,
|
|
|
|
0: 11.22,
|
2020-10-06 20:28:38 +02:00
|
|
|
'.': 4.55,
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
const getInputWidth = (value) => {
|
2021-02-04 19:15:23 +01:00
|
|
|
const valueString = String(value);
|
|
|
|
const charArray = valueString.split('');
|
2020-11-03 00:41:28 +01:00
|
|
|
return charArray.reduce(
|
|
|
|
(inputWidth, _char) => inputWidth + characterWidthMap[_char],
|
|
|
|
12,
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
|
|
|
};
|
2020-11-03 00:41:28 +01:00
|
|
|
export default function DropdownInputPair({
|
2020-10-06 20:28:38 +02:00
|
|
|
itemsToSearch = [],
|
|
|
|
onInputChange,
|
|
|
|
inputValue = '',
|
|
|
|
onSelect,
|
|
|
|
leftValue,
|
|
|
|
selectedItem,
|
|
|
|
SearchListPlaceholder,
|
|
|
|
maxListItems,
|
|
|
|
selectPlaceHolderText,
|
|
|
|
loading,
|
|
|
|
hideItemIf,
|
|
|
|
listContainerClassName,
|
2020-10-14 18:43:55 +02:00
|
|
|
autoFocus,
|
2020-10-06 20:28:38 +02:00
|
|
|
}) {
|
2021-02-04 19:15:23 +01:00
|
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const open = () => setIsOpen(true);
|
|
|
|
const close = () => setIsOpen(false);
|
|
|
|
const inputRef = useRef();
|
2020-10-06 20:28:38 +02:00
|
|
|
const onTextFieldChange = (event) => {
|
2021-02-04 19:15:23 +01:00
|
|
|
event.stopPropagation();
|
2020-10-06 20:28:38 +02:00
|
|
|
// Automatically prefix value with 0. if user begins typing .
|
2021-02-04 19:15:23 +01:00
|
|
|
const valueToUse = event.target.value === '.' ? '0.' : event.target.value;
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
// Regex that validates strings with only numbers, 'x.', '.x', and 'x.x'
|
2021-02-04 19:15:23 +01:00
|
|
|
const regexp = /^(\.\d+|\d+(\.\d+)?|\d+\.)$/u;
|
2020-10-06 20:28:38 +02:00
|
|
|
// If the value is either empty or contains only numbers and '.' and only has one '.', update input to match
|
|
|
|
if (valueToUse === '' || regexp.test(valueToUse)) {
|
2021-02-04 19:15:23 +01:00
|
|
|
onInputChange(valueToUse);
|
2020-10-06 20:28:38 +02:00
|
|
|
} else {
|
|
|
|
// otherwise, use the previously set inputValue (effectively denying the user from inputting the last char)
|
|
|
|
// or an empty string if we do not yet have an inputValue
|
2021-02-04 19:15:23 +01:00
|
|
|
onInputChange(inputValue || '');
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|
|
|
|
const [applyTwoLineStyle, setApplyTwoLineStyle] = useState(null);
|
2020-10-06 20:28:38 +02:00
|
|
|
useEffect(() => {
|
2020-11-03 00:41:28 +01:00
|
|
|
setApplyTwoLineStyle(
|
|
|
|
(inputRef?.current?.getBoundingClientRect()?.width || 0) +
|
|
|
|
getInputWidth(inputValue || '') >
|
|
|
|
137,
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
|
|
|
}, [inputValue, inputRef]);
|
2020-10-06 20:28:38 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="dropdown-input-pair">
|
|
|
|
<DropdownSearchList
|
|
|
|
itemsToSearch={itemsToSearch}
|
|
|
|
SearchListPlaceholder={SearchListPlaceholder}
|
2020-11-03 00:41:28 +01:00
|
|
|
fuseSearchKeys={[
|
|
|
|
{ name: 'name', weight: 0.499 },
|
|
|
|
{ name: 'symbol', weight: 0.499 },
|
|
|
|
{ name: 'address', weight: 0.002 },
|
|
|
|
]}
|
2020-10-06 20:28:38 +02:00
|
|
|
maxListItems={maxListItems}
|
|
|
|
onOpen={open}
|
|
|
|
onClose={close}
|
|
|
|
onSelect={onSelect}
|
|
|
|
className={isOpen ? 'dropdown-input-pair__list--full-width' : ''}
|
|
|
|
externallySelectedItem={selectedItem}
|
|
|
|
selectPlaceHolderText={selectPlaceHolderText}
|
|
|
|
selectorClosedClassName="dropdown-input-pair__selector--closed"
|
|
|
|
listContainerClassName={listContainerClassName}
|
|
|
|
loading={loading}
|
|
|
|
hideItemIf={hideItemIf}
|
|
|
|
defaultToAll
|
|
|
|
/>
|
|
|
|
{!isOpen && (
|
|
|
|
<TextField
|
|
|
|
className={classnames('dropdown-input-pair__input', {
|
|
|
|
'dropdown-input-pair__two-line-input': applyTwoLineStyle,
|
|
|
|
})}
|
|
|
|
type="text"
|
|
|
|
placeholder="0"
|
|
|
|
onChange={onTextFieldChange}
|
|
|
|
fullWidth
|
|
|
|
margin="dense"
|
2020-10-14 18:43:55 +02:00
|
|
|
value={inputValue}
|
|
|
|
autoFocus={autoFocus}
|
2020-10-06 20:28:38 +02:00
|
|
|
/>
|
|
|
|
)}
|
2020-11-03 00:41:28 +01:00
|
|
|
{!isOpen && leftValue && (
|
|
|
|
<div
|
|
|
|
className={classnames('dropdown-input-pair__left-value', {
|
|
|
|
'dropdown-input-pair__left-value--two-lines': applyTwoLineStyle,
|
|
|
|
})}
|
|
|
|
ref={inputRef}
|
|
|
|
>
|
|
|
|
≈ {leftValue}
|
|
|
|
</div>
|
|
|
|
)}
|
2020-10-06 20:28:38 +02:00
|
|
|
</div>
|
2021-02-04 19:15:23 +01:00
|
|
|
);
|
2020-10-06 20:28:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DropdownInputPair.propTypes = {
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Give items data for the component
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
itemsToSearch: PropTypes.array,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Handler for input change
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
onInputChange: PropTypes.func,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Show input value content
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
inputValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Handler for onSelect
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
onSelect: PropTypes.func,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Set value to left
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
leftValue: PropTypes.string,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Show selected item
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
selectedItem: PropTypes.object,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Doesn't look like this is used
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
SearchListPlaceholder: PropTypes.func,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Define maximum item per list
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
maxListItems: PropTypes.number,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Show select placeholder text
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
selectPlaceHolderText: PropTypes.string,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Check if the component is loading
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
loading: PropTypes.bool,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Handler for hide item
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
hideItemIf: PropTypes.func,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Add custom CSS class for list container
|
|
|
|
*/
|
2020-10-06 20:28:38 +02:00
|
|
|
listContainerClassName: PropTypes.string,
|
2021-12-07 18:44:25 +01:00
|
|
|
/**
|
|
|
|
* Check if the component is auto focus
|
|
|
|
*/
|
2020-10-14 18:43:55 +02:00
|
|
|
autoFocus: PropTypes.bool,
|
2021-02-04 19:15:23 +01:00
|
|
|
};
|