Skip to content

Commit

Permalink
feat(Select): add selected elements counter on multiple selection (#1368
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Arucard89 authored Apr 8, 2024
1 parent 77156ff commit 9f6aa9c
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 45 deletions.
128 changes: 84 additions & 44 deletions src/components/Select/README.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function
hasClear = false,
onClose,
id,
hasCounter,
renderCounter,
title,
} = props;
const mobile = useMobile();
Expand Down Expand Up @@ -339,6 +341,8 @@ export const Select = React.forwardRef<HTMLButtonElement, SelectProps>(function
popupId={`select-popup-${selectId}`}
selectId={`select-${selectId}`}
activeIndex={activeIndex}
hasCounter={multiple && hasCounter}
renderCounter={renderCounter}
title={title}
/>

Expand Down
21 changes: 20 additions & 1 deletion src/components/Select/components/SelectControl/SelectControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import type {
SelectRenderClearArgs,
SelectRenderControl,
SelectRenderControlProps,
SelectRenderCounter,
} from '../../types';
import {SelectClear} from '../SelectClear/SelectClear';
import {SelectCounter} from '../SelectCounter/SelectCounter';

import './SelectControl.scss';

type ControlProps = {
toggleOpen: () => void;
renderControl?: SelectRenderControl;
renderCounter?: SelectRenderCounter;
view: NonNullable<SelectProps['view']>;
size: NonNullable<SelectProps['size']>;
pin: NonNullable<SelectProps['pin']>;
Expand All @@ -37,8 +40,9 @@ type ControlProps = {
value: SelectProps['value'];
clearValue: () => void;
hasClear?: boolean;
hasCounter?: boolean;
title?: string;
} & Omit<SelectRenderControlProps, 'onClick' | 'onClear'>;
} & Omit<SelectRenderControlProps, 'onClick' | 'onClear' | 'renderCounter'>;

export const SelectControl = React.forwardRef<HTMLButtonElement, ControlProps>((props, ref) => {
const {
Expand All @@ -64,6 +68,8 @@ export const SelectControl = React.forwardRef<HTMLButtonElement, ControlProps>((
popupId,
selectId,
activeIndex,
renderCounter,
hasCounter,
title,
} = props;
const showOptionsText = Boolean(selectedOptionsContent);
Expand Down Expand Up @@ -105,6 +111,17 @@ export const SelectControl = React.forwardRef<HTMLButtonElement, ControlProps>((
clearValue();
}, [clearValue]);

const renderCounterComponent = () => {
if (!hasCounter) {
return null;
}
const count = Number(value?.length) || 0;
const counterComponent = <SelectCounter count={count} size={size} disabled={disabled} />;
return renderCounter
? renderCounter(counterComponent, {count, size, disabled})
: counterComponent;
};

const renderClearIcon = (args: SelectRenderClearArgs) => {
const hideOnEmpty = !value?.[0];
if (!hasClear || !clearValue || hideOnEmpty || disabled) {
Expand All @@ -128,6 +145,7 @@ export const SelectControl = React.forwardRef<HTMLButtonElement, ControlProps>((
onClear: clearValue,
onClick: toggleOpen,
renderClear: (arg) => renderClearIcon(arg),
renderCounter: renderCounterComponent,
ref,
open: Boolean(open),
popupId,
Expand Down Expand Up @@ -171,6 +189,7 @@ export const SelectControl = React.forwardRef<HTMLButtonElement, ControlProps>((
</span>
)}
</button>
{renderCounterComponent()}
{renderClearIcon({})}

{errorMessage && (
Expand Down
48 changes: 48 additions & 0 deletions src/components/Select/components/SelectCounter/SelectCounter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
@use '../../../variables';
@use '../../variables.scss' as select-css-variables;

$block: '.#{variables.$ns}select-counter';

#{$block} {
display: flex;
justify-content: center;
align-items: center;
margin-inline: 4px;

background-color: var(--g-color-base-generic);

&__text {
margin-inline: 4px;
flex-grow: 1;
text-align: center;
}

&_size_xl &__text {
margin-inline: 6px;
}

&_size {
&_s {
border-radius: var(--g-border-radius-xs);
height: 20px;
min-width: 20px;
}
&_m {
border-radius: var(--g-border-radius-s);
height: 24px;
min-width: 24px;
}
&_l {
border-radius: var(--g-border-radius-m);
height: 28px;
min-width: 28px;
}

&_xl {
border-radius: var(--g-border-radius-l);
margin-inline: 4px;
height: 36px;
min-width: 36px;
}
}
}
26 changes: 26 additions & 0 deletions src/components/Select/components/SelectCounter/SelectCounter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';

import {Text} from '../../../Text';
import {block} from '../../../utils/cn';
import type {SelectCounterProps} from '../../types';

import './SelectCounter.scss';

const b = block('select-counter');

export const SelectCounter = React.forwardRef(function SelectCouner(
{count, size, disabled}: SelectCounterProps,
ref: React.ForwardedRef<HTMLDivElement>,
) {
return (
<div className={b({size})} ref={ref}>
<Text
variant={size === 'xl' ? 'body-2' : 'body-1'}
color={disabled ? 'hint' : 'primary'}
className={b('text')}
>
{count}
</Text>
</div>
);
});
18 changes: 18 additions & 0 deletions src/components/Select/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type SelectRenderControlProps = {
onClick: () => void;
onKeyDown?: (e: React.KeyboardEvent<HTMLElement>) => void;
renderClear?: (args: SelectRenderClearArgs) => React.ReactNode;
renderCounter?: () => React.ReactNode;
ref: React.Ref<HTMLElement>;
open: boolean;
popupId: string;
Expand Down Expand Up @@ -51,6 +52,11 @@ export type SelectRenderPopup = (popupItems: {

export type SelectSize = InputControlSize;

export type SelectRenderCounter = (
originalComponent: React.ReactElement<SelectCounterProps>,
counterProps: SelectCounterProps,
) => React.ReactElement;

export type SelectProps<T = any> = QAProps &
Pick<ControlGroupProps, 'name' | 'disabled'> &
UseOpenProps & {
Expand All @@ -69,6 +75,7 @@ export type SelectProps<T = any> = QAProps &
renderSelectedOption?: (option: SelectOption<T>, index: number) => React.ReactElement;
renderEmptyOptions?: ({filter}: {filter: string}) => React.ReactElement;
renderPopup?: SelectRenderPopup;
renderCounter?: SelectRenderCounter;
getOptionHeight?: (option: SelectOption<T>, index: number) => number;
getOptionGroupHeight?: (option: SelectOptionGroup<T>, index: number) => number;
filterOption?: (option: SelectOption<T>, filter: string) => boolean;
Expand Down Expand Up @@ -112,6 +119,8 @@ export type SelectProps<T = any> = QAProps &
| React.ReactElement<SelectOptionGroup<T>, typeof OptionGroup>
| React.ReactElement<SelectOptionGroup<T>, typeof OptionGroup>[];
id?: string;
/**Shows selected options count if multiple selection is avalable */
hasCounter?: boolean;
title?: string;
};

Expand Down Expand Up @@ -148,4 +157,13 @@ export type SelectClearProps = SelectClearIconProps & {
onMouseLeave: (e: React.MouseEvent) => void;
};

export type SelectCounterProps = {
/** amount of selected elements to show */
count: number;
/** size of the parent element */
size: SelectSize;
/** disabled state of the parent element*/
disabled?: boolean;
};

export type SelectOptions<T = any> = NonNullable<SelectProps<T>['options']>;

0 comments on commit 9f6aa9c

Please sign in to comment.