Skip to content

Commit

Permalink
feat(select-with-tags): design review fixes (#472)
Browse files Browse the repository at this point in the history
* feat(select-with-tags): fix gaps

* feat(select-with-tags): add empty list placeholder, fix colors

* feat(select-with-tags): fix list open/close

* feat(select-with-tags): add input on new line

* feat(select-with-tags): fix label

* feat(select-with-tags): add tag render prop

* docs(select-with-tags): updates

* feat(select-with-tags): change renderTag fn to Component

* feat(input-autocomplete): arrow click behaviour

Co-authored-by: reme3d2y <AYatsenko@alfabank.ru>
  • Loading branch information
dmitrsavk and reme3d2y committed Feb 8, 2021
1 parent 410630e commit 0ff989f
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 99 deletions.
8 changes: 4 additions & 4 deletions packages/select-with-tags/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
"react": "^16.9.0"
},
"dependencies": {
"@alfalab/core-components-form-control": "3.1.1",
"@alfalab/core-components-form-control": "^4.0.0",
"@alfalab/core-components-select": "^4.3.1",
"@alfalab/core-components-tag": "1.6.4",
"@alfalab/hooks": "0.9.0",
"@alfalab/icons-glyph": "1.70.0",
"@alfalab/core-components-tag": "^1.7.1",
"@alfalab/hooks": "^0.9.0",
"@alfalab/icons-glyph": "^1.70.0",
"classnames": "^2.2.6"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ exports[`SelectWithTags Display tests should match snapshot 1`] = `
aria-autocomplete="list"
aria-controls="downshift-1-menu"
aria-labelledby="downshift-1-label"
autocomplete="off"
class="input"
id="downshift-0-input"
tabindex="0"
Expand Down Expand Up @@ -159,6 +160,7 @@ exports[`SelectWithTags Display tests should match snapshot with selected tags 1
aria-autocomplete="list"
aria-controls="downshift-3-menu"
aria-labelledby="downshift-3-label"
autocomplete="off"
class="input"
id="downshift-2-input"
placeholder=""
Expand Down
3 changes: 3 additions & 0 deletions packages/select-with-tags/src/component.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const matchOption = (option, inputValue) =>
value={value}
onChange={handleChange}
selected={selected}
emptyListPlaceholder={text('emptyListPlaceholder', 'Ничего не найдено')}
/>
</div>
);
Expand All @@ -89,6 +90,8 @@ export const matchOption = (option, inputValue) =>
import { SelectWithTags } from '@alfalab/core-components-select-with-tags';
```

Размеры `s` и `m` пока лучше не использовать, так как еще нет актуальных макетов.

<Preview>
{React.createElement(() => {
const [value, setValue] = React.useState('');
Expand Down
7 changes: 7 additions & 0 deletions packages/select-with-tags/src/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const SelectWithTags = forwardRef<HTMLInputElement, SelectWithTagsProps>(
autocomplete = true,
match,
allowUnselect = true,
emptyListPlaceholder = 'Ничего не найдено',
Tag,
...restProps
},
ref,
Expand Down Expand Up @@ -104,11 +106,16 @@ export const SelectWithTags = forwardRef<HTMLInputElement, SelectWithTagsProps>(
Arrow={Arrow}
multiple={true}
allowUnselect={allowUnselect}
showEmptyOptionsList={true}
fieldProps={{
value,
autocomplete: isAutocomplete,
onInput,
handleDeleteTag,
Tag,
}}
optionsListProps={{
emptyPlaceholder: emptyListPlaceholder,
}}
selected={selected || selectedTags}
autocomplete={isAutocomplete}
Expand Down
1 change: 1 addition & 0 deletions packages/select-with-tags/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './tag-list';
export * from './tag';
57 changes: 35 additions & 22 deletions packages/select-with-tags/src/components/tag-list/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@ import React, {
useState,
KeyboardEventHandler,
MouseEventHandler,
useEffect,
} from 'react';
import cn from 'classnames';
import { FieldProps } from '@alfalab/core-components-select';
import { FormControl, FormControlProps } from '@alfalab/core-components-form-control';
import { Tag } from '@alfalab/core-components-tag';
import { useFocus } from '@alfalab/hooks';
import { CrossCompactMIcon } from '@alfalab/icons-glyph/CrossCompactMIcon';

import styles from './index.module.css';

import { TagComponent } from '../../types';
import { Tag as DefaultTag } from '../tag';

type TagListOwnProps = {
value?: string;
handleDeleteTag?: (key: string) => void;
onInput?: (event: ChangeEvent<HTMLInputElement>) => void;
inputRef?: MutableRefObject<HTMLInputElement>;
autocomplete?: boolean;
Tag?: TagComponent;
};

export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
Expand All @@ -35,15 +38,17 @@ export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
innerProps,
className,
fieldClassName,
value,
value = '',
autocomplete,
label,
valueRenderer,
onInput,
handleDeleteTag,
Tag = DefaultTag,
...restProps
}) => {
const [focused, setFocused] = useState(false);
const [inputOnNewLine, setInputOnNewLine] = useState(false);

const wrapperRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
Expand All @@ -55,6 +60,11 @@ export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
const handleFocus = useCallback(() => setFocused(true), []);
const handleBlur = useCallback(() => setFocused(false), []);

const inputTextIsOverflow = useCallback(
() => inputRef.current && inputRef.current.scrollWidth > inputRef.current.clientWidth,
[],
);

const handleMouseDown = useCallback(event => {
event.preventDefault();
}, []);
Expand All @@ -64,11 +74,13 @@ export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
const handleClick = useCallback<MouseEventHandler<HTMLDivElement>>(
event => {
if (onClick && contentWrapperRef.current) {
const clickedInsideContent = contentWrapperRef.current.contains(
event.target as HTMLDivElement,
);
const eventTarget = event.target as HTMLDivElement;

if (!clickedInsideContent || (clickedInsideContent && !open)) {
const clickedInsideContent =
eventTarget !== contentWrapperRef.current &&
contentWrapperRef.current.contains(eventTarget);

if (!clickedInsideContent) {
onClick(event);
}
}
Expand All @@ -77,7 +89,7 @@ export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
inputRef.current.focus();
}
},
[onClick, open],
[onClick],
);

const handleKeyDown = useCallback<KeyboardEventHandler<HTMLInputElement>>(
Expand All @@ -91,6 +103,17 @@ export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
[handleDeleteTag, selectedMultiple, value],
);

useEffect(() => {
/**
* Если текст не помещается в инпут, то нужно перенести инпут на новую строку.
*/
if (inputTextIsOverflow() && !inputOnNewLine) {
setInputOnNewLine(true);
} else if (value.length === 0) {
setInputOnNewLine(false);
}
}, [value, inputOnNewLine, inputTextIsOverflow]);

const filled = Boolean(selectedMultiple.length > 0) || Boolean(value);

return (
Expand Down Expand Up @@ -125,30 +148,20 @@ export const TagList: FC<FieldProps & FormControlProps & TagListOwnProps> = ({
})}
ref={contentWrapperRef}
>
{selectedMultiple.map(({ content, key }) => (
<Tag key={key} size='xs' checked={true} className={styles.tag}>
<span className={styles.tagContentWrap}>
{content}
<CrossCompactMIcon
onClick={() => {
if (handleDeleteTag) {
handleDeleteTag(key);
}
}}
className={styles.tagCross}
/>
</span>
</Tag>
{selectedMultiple.map(option => (
<Tag key={option.key} option={option} handleDeleteTag={handleDeleteTag} />
))}

{autocomplete && (
<input
{...restInnerProps}
autoComplete='off'
ref={inputRef}
value={value}
onChange={onInput}
className={cn(styles.input, {
[styles.focusVisible]: inputFocusVisible,
[styles.block]: inputOnNewLine,
})}
disabled={disabled}
onKeyDown={handleKeyDown}
Expand Down
87 changes: 30 additions & 57 deletions packages/select-with-tags/src/components/tag-list/index.module.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
@import '../../../../vars/src/index.css';

.component.l .contentWrapper {
padding: var(--gap-l) var(--gap-m);
}

.component.m .contentWrapper {
padding: var(--gap-m) var(--gap-m);
}

.component.s .contentWrapper {
padding: var(--gap-s) var(--gap-m);
}

.component .contentWrapper {
display: flex;
flex-wrap: wrap;
Expand All @@ -8,25 +20,15 @@

&.hasLabel {
margin-top: var(--gap-xs);
padding-bottom: var(--gap-xs);
}

&.hasTags.hasLabel {
margin-top: var(--gap-s);
margin-top: var(--gap-m);
padding-bottom: var(--gap-s);
}
}

.component.l .contentWrapper {
padding: var(--gap-l) var(--gap-m);
}

.component.m .contentWrapper {
padding: var(--gap-m) var(--gap-m);
}

.component.s .contentWrapper {
padding: var(--gap-s) var(--gap-m);
}

.component:hover .addons {
opacity: 0.7;
}
Expand Down Expand Up @@ -56,47 +58,8 @@
}
}

.tag {
padding: 0;
padding-left: var(--gap-m);
margin: var(--gap-2xs);
cursor: default;

&:active:not(:disabled) {
background-color: var(--tag-background-color-checked);
color: var(--tag-text-color-checked);
}

&:hover:not(:disabled):not(:active) {
background-color: var(--tag-background-color-checked);
color: var(--tag-text-color-checked);
}

& > span {
width: 100%;
height: 100%;
}
}

.tagContentWrap {
display: flex;
justify-content: flex-start;
align-items: center;
height: 100%;
cursor: pointer;
}

.tagCross {
height: 100%;
padding-right: var(--gap-xs);

&:hover {
opacity: 0.7;
}

&:active {
opacity: 0.5;
}
.block {
min-width: 100%;
}

.focusVisible {
Expand All @@ -108,7 +71,7 @@
align-items: center;
min-height: 28px;
padding-left: var(--gap-2xs);
color: var(--color-light-text-secondary);
color: var(--color-light-text-tertiary);
}

.addons {
Expand All @@ -124,10 +87,20 @@
padding-top: var(--gap-xl);
}

.component.s .label {
top: 4px;
left: 16px;
}

.component.m .label {
top: 6px;
left: 16px;
}

.component .label {
top: 0;
top: 14px;
align-items: flex-start;
height: auto;
padding-top: var(--gap-s);
transform: none;
font-size: 14px;
}
25 changes: 25 additions & 0 deletions packages/select-with-tags/src/components/tag/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { useCallback } from 'react';
import { Tag as CoreTag } from '@alfalab/core-components-tag';
import { CrossCompactMIcon } from '@alfalab/icons-glyph/CrossCompactMIcon';

import { TagComponent } from '../../types';

import styles from './index.module.css';

export const Tag: TagComponent = ({ option: { content, key }, handleDeleteTag }) => {
const handleClick = useCallback(() => {
if (handleDeleteTag) {
handleDeleteTag(key);
}
}, [handleDeleteTag, key]);

return (
<CoreTag key={key} size='xs' checked={true} className={styles.tag}>
<span className={styles.tagContentWrap}>
{content}

<CrossCompactMIcon onClick={handleClick} className={styles.tagCross} />
</span>
</CoreTag>
);
};

0 comments on commit 0ff989f

Please sign in to comment.