Skip to content

Commit

Permalink
feat(core-components-select): inputs & types (#323)
Browse files Browse the repository at this point in the history
feat(core-components-form-control): pass rest props to inner

feat(core-components-input): pass mouse handlers to form-control

* refactor(core-components-select): change handlers & types

* feat(core-components-select): update stories

* refactor(core-components-select): change & simplify api

* feat(core-components-select): highlight first option by default

* fix(core-components-select): fix disabled state

* fix(core-components-select): pass classname to field

* fix(core-components-select): fix broken demo

* fix(core-components-select): fix popover styles

* fix(core-components-select): change unknown to any

* fix(core-components-select): fix popover styles

* fix(core-components-select): fix field styles

* fix(core-components-input-autocomplete): dont render empty addons

* fix(core-components-select): dont render empty addons

* fix(core-components-input-autocomplete): fix arrow stories

* feat(core-components-select): simplify arrow disabling

* feat(core-components-select): add focus & blur handlers

* fix(core-components-input-autocomplete): fix demo

* fix(core-components-select): add vars import

Co-authored-by: Dmitry Savkin <dmitrsavk@yandex.ru>
  • Loading branch information
reme3d2y and dmitrsavk committed Oct 27, 2020
1 parent eb1c617 commit 0f4d547
Show file tree
Hide file tree
Showing 22 changed files with 451 additions and 315 deletions.
3 changes: 1 addition & 2 deletions packages/form-control/src/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,8 @@ export const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
},
className,
)}
{...restProps}
>
<div className={styles.inner} ref={ref}>
<div {...restProps} className={styles.inner} ref={ref}>
{leftAddons && (
<div className={cn(styles.addons, styles.leftAddons, addonsClassName)}>
{leftAddons}
Expand Down
1 change: 1 addition & 0 deletions packages/form-control/src/index.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
border-bottom: var(--form-control-border-bottom);
transition: background 0.2s ease, box-shadow 0.2s ease, border 0.2s ease;
box-sizing: border-box;
outline: none;
}

.inputWrapper {
Expand Down
123 changes: 64 additions & 59 deletions packages/input-autocomplete/src/Component.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,25 @@ import { Arrow, Option } from '@alfalab/core-components-select';
import { InputAutocomplete } from './Component';

export const options = [
{ value: '1', text: 'Neptunium'},
{ value: '2', text: 'Plutonium' },
{ value: '3', text: 'Americium' },
{ value: '4', text: 'Curium' },
{ value: '5', text: 'Berkelium' },
{ value: '6', text: 'Californium' },
{ value: '7', text: 'Einsteinium' },
{ value: '8', text: 'Fermium' },
{ value: '9', text: 'Mendelevium' },
{ value: '10', text: 'Nobelium' },
{ value: '11', text: 'Lawrencium' },
{ value: '12', text: 'Rutherfordium' },
{ value: '13', text: 'Dubnium' },
{ value: '14', text: 'Seaborgium' },
{ value: '15', text: 'Bohrium' },
{ key: 'Neptunium', content: 'Neptunium' },
{ key: 'Plutonium', content: 'Plutonium' },
{ key: 'Americium', content: 'Americium' },
{ key: 'Curium', content: 'Curium' },
{ key: 'Berkelium', content: 'Berkelium' },
{ key: 'Californium', content: 'Californium' },
{ key: 'Einsteinium', content: 'Einsteinium' },
{ key: 'Fermium', content: 'Fermium' },
{ key: 'Mendelevium', content: 'Mendelevium' },
{ key: 'Nobelium', content: 'Nobelium' },
{ key: 'Lawrencium', content: 'Lawrencium' },
{ key: 'Rutherfordium', content: 'Rutherfordium' },
{ key: 'Dubnium', content: 'Dubnium' },
{ key: 'Seaborgium', content: 'Seaborgium' },
{ key: 'Bohrium', content: 'Bohrium' },
];

export const matchOption = (option, inputValue) =>
option.text.toLowerCase().includes(inputValue.toLowerCase());
option.content.toLowerCase().includes((inputValue || '').toLowerCase());


<Meta
Expand All @@ -48,8 +48,8 @@ export const matchOption = (option, inputValue) =>
const handleInput = event => {
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
setValue(selectedOptions.map(option => option.text).join(', '));
const handleChange = ({ selected }) => {
setValue(selected ? selected.key : '');
};
const filteredOptions = options.filter(option => matchOption(option, value));
return (
Expand All @@ -63,7 +63,7 @@ export const matchOption = (option, inputValue) =>
hint={text('hint', '')}
allowUnselect={boolean('allowUnselect', true)}
closeOnSelect={boolean('closeOnSelect', false)}
Arrow={boolean('Arrow', true) ? Arrow : undefined}
Arrow={boolean('Arrow', false) ? Arrow : undefined}
circularNavigation={boolean('circularNavigation', false)}
placeholder={text('placeholder', 'Введите элемент')}
label={text('label', 'Элемент')}
Expand All @@ -84,6 +84,10 @@ export const matchOption = (option, inputValue) =>
stage={1}
/>

```tsx
import { InputAutocomplete } from '@alfalab/core-components-input-autocomplete';
```

Компонент поля для ввода с автокомплитом

<Preview>
Expand All @@ -92,8 +96,8 @@ export const matchOption = (option, inputValue) =>
const handleInput = event => {
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
setValue(selectedOptions.map(option => option.text).join(', '));
const handleChange = ({ selected }) => {
setValue(selected ? selected.key : '');
};
const filteredOptions = options.filter(option => matchOption(option, value));
return (
Expand Down Expand Up @@ -121,8 +125,8 @@ export const matchOption = (option, inputValue) =>
const handleInput = event => {
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
setValue(selectedOptions.map(option => option.text).join(', '));
const handleChange = ({ selected }) => {
setValue(selected ? selected.key : '');
};
const handleClear = (event) => {
setValue('');
Expand Down Expand Up @@ -153,25 +157,24 @@ export const matchOption = (option, inputValue) =>
<Preview>
{React.createElement(() => {
const [value, setValue] = React.useState('');
const [selected, setSelected] = React.useState([]);
const [selectedTags, setSelectedTags] = React.useState([]);
const handleInput = event => {
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
const handleChange = ({ selected }) => {
setValue('');
const selectedText = selectedOptions.length ? selectedOptions[0].text : '';
if (selectedText && !selected.includes(selectedText)) {
setSelected(selected.concat([selectedText]));
if (selected && !selectedTags.includes(selected.key)) {
setSelectedTags(selectedTags.concat([selected.key]));
}
};
const renderTags = () => selected.length > 0 ?
selected.map(item => <Tag key={item} size='xs'>{item}</Tag>) :
const renderTags = () => selectedTags.length > 0 ?
selectedTags.map(item => <Tag key={item} size='xs'>{item}</Tag>) :
null;
const filteredOptions = options
.filter(option => !selected.includes(option.text))
.filter(option => !selectedTags.includes(option.content))
.filter(option => matchOption(option, value));
if (filteredOptions.length === 0 && value) {
filteredOptions.push({ text: value, value });
filteredOptions.push({ key: value, content: value });
}
return (
<React.Fragment>
Expand Down Expand Up @@ -203,8 +206,8 @@ export const matchOption = (option, inputValue) =>
if (!dirty) setDirty(true);
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
setValue(selectedOptions.length ? selectedOptions[0].text : '');
const handleChange = ({ selected }) => {
setValue(selected ? selected.key : '');
};
const filteredOptions = options.filter(option => matchOption(option, value));
return (
Expand All @@ -229,46 +232,41 @@ export const matchOption = (option, inputValue) =>
const mask = [/\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/, ' ', /\d/, /\d/, /\d/, /\d/];
const cards = [
{
text: '4035 5010 0000 0008',
name: 'Карта 1',
value: '4035 5010 0000 0008',
key: 'Карта 1',
content: '4035 5010 0000 0008',
},
{
text: '4360 0000 0100 0005',
name: 'Карта 2',
value: '4360 0000 0100 0005',
key: 'Карта 2',
content: '4360 0000 0100 0005',
},
{
text: '8171 9999 2766 0000',
name: 'Карта 3',
value: '8171 9999 2766 0000',
key: 'Карта 3',
content: '8171 9999 2766 0000',
},
{
text: '5204 2477 5000 1471',
name: 'Карта 4',
value: '5204 2477 5000 1471',
key: 'Карта 4',
content: '5204 2477 5000 1471',
},
{
text: '4111 1111 1111 1111',
name: 'Карта 5',
value: '4111 1111 1111 1111',
key: 'Карта 5',
content: '4111 1111 1111 1111',
},
];
const CardOption = React.forwardRef((props, ref) => (
<Option {...props} ref={ref}>
<div>
{props.option.name}
{props.option.key}
<br />
<sub>{props.option.text}</sub>
<sub>{props.option.content}</sub>
</div>
</Option>
));
const [value, setValue] = React.useState('');
const handleInput = (event) => {
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
setValue(selectedOptions.length ? selectedOptions[0].text : '');
const handleChange = ({ selected }) => {
setValue(selected ? selected.content : null);
};
const filteredOptions = cards.filter(option => matchOption(option, value));
return (
Expand Down Expand Up @@ -298,16 +296,16 @@ export const matchOption = (option, inputValue) =>
const handleInput = event => {
setValue(event.target.value);
};
const handleChange = ({ selectedOptions }) => {
const value = selectedOptions.length
? selectedOptions.map(option => option.text).join(', ') + ', '
const handleChange = ({ selectedMultiple }) => {
const value = selectedMultiple.length
? selectedMultiple.map(option => option.key).join(', ') + ', '
: '';
setValue(value);
};
const inputValues = value.split(',').map(v => v.trim()).filter(v => v);
const selectedOptions = options
.filter(option => inputValues.includes(option.text.trim()));
const selected = selectedOptions.map(option => option.value);
.filter(option => inputValues.includes(option.content.trim()));
const selected = selectedOptions.map(option => option.key);
const filteredOptions = inputValues.length === selected.length ?
options :
options.filter(option => {
Expand All @@ -324,8 +322,15 @@ export const matchOption = (option, inputValue) =>
onInput={handleInput}
allowUnselect={true}
value={value}
Arrow={Arrow}
/>
);
})}
</Preview>

### Автокомплит со стрелкой

```tsx
import { Arrow } from '@alfalab/core-components/select';

<InputAutocomplete Arrow={Arrow} />
```
4 changes: 2 additions & 2 deletions packages/input-autocomplete/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, ChangeEvent, forwardRef } from 'react';
import React, { FC, ChangeEvent, forwardRef, RefAttributes } from 'react';
import { InputProps } from '@alfalab/core-components-input';
import {
BaseSelectProps,
Expand All @@ -14,7 +14,7 @@ export type InputAutocompleteProps = Omit<BaseSelectProps, 'Field' | 'nativeSele
/**
* Компонент ввода значения
*/
Input?: FC<InputProps>;
Input?: FC<InputProps & RefAttributes<HTMLInputElement>>;

/**
* Пропсы, которые будут прокинуты в инпут
Expand Down
76 changes: 51 additions & 25 deletions packages/input-autocomplete/src/autocomplete-field/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback, useRef } from 'react';
import { Input as DefaultInput } from '@alfalab/core-components-input';
import { FieldProps } from '@alfalab/core-components-select';
import { InputAutocompleteProps } from '../Component';
Expand All @@ -18,27 +18,53 @@ export const AutocompleteField = ({
disabled,
onInput,
inputProps = {},
innerProps = {},
}: AutocompleteFieldProps) => (
<Input
{...innerProps}
{...inputProps}
wrapperRef={innerProps.ref}
disabled={disabled}
block={true}
label={label}
placeholder={placeholder}
size={size}
error={error}
hint={hint}
rightAddons={
<React.Fragment>
{inputProps.rightAddons}
{Arrow}
</React.Fragment>
}
onChange={onInput}
autoComplete='off'
value={value}
/>
);
innerProps,
}: AutocompleteFieldProps) => {
const inputRef = useRef<HTMLInputElement>(null);

const { onClick } = innerProps;

const handleMouseDown = useCallback(event => {
event.preventDefault();
}, []);

const handleClick = useCallback(
event => {
if (onClick) onClick(event);

if (inputRef.current) {
inputRef.current.focus();
}
},
[onClick],
);

return (
<Input
{...inputProps}
{...innerProps}
wrapperRef={innerProps.ref}
ref={inputRef}
disabled={disabled}
block={true}
label={label}
placeholder={placeholder}
size={size}
error={error}
hint={hint}
rightAddons={
(Arrow || inputProps.rightAddons) && (
<React.Fragment>
{inputProps.rightAddons}
{Arrow}
</React.Fragment>
)
}
onChange={onInput}
onMouseDown={handleMouseDown}
onClick={handleClick}
autoComplete='off'
value={value}
/>
);
};

0 comments on commit 0f4d547

Please sign in to comment.