Counter
Counter in filters
FilterTrigger is normally used together with Select or Dropdown. Go to the guide for more information.
tsx
import React from 'react';
import Counter, { AnimatedNumber } from '@semcore/counter';
import { FilterTrigger } from '@semcore/base-trigger';
import { ScreenReaderOnly } from '@semcore/flex-box';
import Dropdown from '@semcore/dropdown';
const Demo = () => (
<Dropdown>
<Dropdown.Trigger aria-label='Link to website' tag={FilterTrigger}>
<FilterTrigger.Text aria-hidden>Link to website</FilterTrigger.Text>
<FilterTrigger.Addon>
<Counter theme='info'>
<AnimatedNumber value={500} delay={1000} formatValue={(x) => Math.round(x).toString()} />
<ScreenReaderOnly>selected</ScreenReaderOnly>
</Counter>
</FilterTrigger.Addon>
</Dropdown.Trigger>
<Dropdown.Popper aria-label='Link to website' p={4}>
Filter content
</Dropdown.Popper>
</Dropdown>
);
export default Demo;
Counter in Button
TIP
Don't forget to place counters inside the Addon
to create correct margins.
tsx
import React from 'react';
import Counter from '@semcore/counter';
import Button from '@semcore/button';
import SettingsM from '@semcore/icon/Settings/m';
const Demo = () => (
<>
<Button mr={4}>
<Button.Addon>
<SettingsM />
</Button.Addon>
<Button.Text>Manage columns</Button.Text>
<Button.Addon>
<Counter>23</Counter>
</Button.Addon>
</Button>
<Button use='primary'>
<Button.Addon>
<SettingsM />
</Button.Addon>
<Button.Text>Manage columns</Button.Text>
<Button.Addon>
<Counter theme='bg-primary-neutral'>23</Counter>
</Button.Addon>
</Button>
</>
);
export default Demo;
Counter in forms
As the design guide recommends, the counter changes color to orange shortly before the limit is reached, and then to red when the limit is exceeded.
tsx
import React from 'react';
import { Flex } from '@semcore/flex-box';
import { Text } from '@semcore/typography';
import Textarea from '@semcore/textarea';
import Counter from '@semcore/counter';
import { ScreenReaderOnly } from '@semcore/utils/lib/ScreenReaderOnly';
const maxSymbols = 150;
const Demo = () => {
const [value, setValue] = React.useState('');
const [valueLength, setValueLength] = React.useState(0);
const [theme, setTheme] = React.useState<string>('');
const handleChange = React.useCallback((value: string) => {
setValue(value);
}, []);
const valueTimer = React.useRef<number>();
React.useEffect(() => {
if (valueTimer.current) {
window.clearTimeout(valueTimer.current);
}
valueTimer.current = window.setTimeout(() => {
setValueLength(value.length);
}, 1000);
}, [value]);
React.useEffect(() => {
if (value.length >= 140) {
if (value.length <= maxSymbols) {
setTheme('warning');
} else {
setTheme('danger');
}
} else {
setTheme('');
}
}, [value]);
return (
<Flex direction='column' w={350}>
<Flex mb={2} justifyContent='space-between'>
<Flex alignItems={'center'}>
<Text size={200} tag='label' htmlFor='limited-text-field'>
Project description
</Text>
<Counter ml={1} theme={theme} id={'counter-for-textarea'}>
{value.length}
<span aria-hidden='true'>/</span>
<ScreenReaderOnly>of</ScreenReaderOnly>
{maxSymbols}
<ScreenReaderOnly>allowed characters</ScreenReaderOnly>
{theme === 'warning' && <ScreenReaderOnly>Limit is almost reached</ScreenReaderOnly>}
{theme === 'danger' && <ScreenReaderOnly>Limit is exceeded</ScreenReaderOnly>}
</Counter>
</Flex>
<Text size={200} color='text-secondary' id={'optional-for-textarea'}>
optional
</Text>
</Flex>
<Textarea
placeholder='The goal of your project, required resources, and so on'
id='limited-text-field'
aria-describedby='optional-for-textarea counter-for-textarea'
onChange={handleChange}
/>
<ScreenReaderOnly aria-live={'polite'} aria-atomic={true}>
{valueLength} of {maxSymbols} allowed characters
{valueLength >= 140 && valueLength <= 150 && (
<ScreenReaderOnly>Limit is almost reached</ScreenReaderOnly>
)}
{valueLength > 150 && <ScreenReaderOnly>Limit is exceeded</ScreenReaderOnly>}
</ScreenReaderOnly>
</Flex>
);
};
export default Demo;
Counter and typography
Plain text counters should be implemented using Typography, without the Counter
component.
tsx
import React from 'react';
import { Text } from '@semcore/typography';
const Demo = () => (
<>
<Text size={300}>
Lorem ipsum <Text color='text-secondary'>12,457</Text>
</Text>
<br />
<Text size={300}>
Dolor sit amet: <Text color='text-secondary'>149</Text>
</Text>
</>
);
export default Demo;
Counter in Pills
Counters inside Pills are implemented using Typography, without the Counter
component.
tsx
import React from 'react';
import Pills from '@semcore/pills';
import { Text } from '@semcore/typography';
const Demo = () => (
<Pills defaultValue='all' aria-label='Pills with counters'>
<Pills.Item value='all'>
<Pills.Item.Text>All</Pills.Item.Text>
<Pills.Item.Addon>
<Text color='text-secondary'>1,259</Text>
</Pills.Item.Addon>
</Pills.Item>
<Pills.Item value='follow'>
<Pills.Item.Text>Follow</Pills.Item.Text>
<Pills.Item.Addon>
<Text color='text-secondary'>557</Text>
</Pills.Item.Addon>
</Pills.Item>
<Pills.Item value='nofollow'>
<Pills.Item.Text>Nofollow</Pills.Item.Text>
<Pills.Item.Addon>
<Text color='text-secondary'>736</Text>
</Pills.Item.Addon>
</Pills.Item>
</Pills>
);
export default Demo;
Counter in limits
Displaying limits is done using Typography, without the Counter
component.
tsx
import React from 'react';
import { Text } from '@semcore/typography';
import { Flex } from '@semcore/flex-box';
import ProgressBar from '@semcore/progress-bar';
import WarningM from '@semcore/icon/Warning/m';
const limitsMax = 10;
const limitsUsed = 10;
const warning = limitsUsed >= limitsMax;
const Demo = () => (
<Flex direction='column' w={350}>
<Flex mb={1} justifyContent='space-between'>
<Text size={200}>SEO Ideas Units</Text>
<Flex alignItems='center'>
{warning ? <WarningM color='icon-primary-warning' /> : null}
<Text size={200} ml={1} bold aria-hidden>
{limitsUsed}
<Text color='text-secondary'>/{limitsMax}</Text>
</Text>
</Flex>
</Flex>
<ProgressBar
value={(limitsUsed / limitsMax) * 100}
aria-valuetext={`${limitsUsed} out of ${limitsMax}`}
aria-label={`limits used ${warning ? ', warning' : ''}`}
size='s'
>
<ProgressBar.Value theme={warning ? 'bg-primary-warning' : 'bg-primary-success'} />
</ProgressBar>
</Flex>
);
export default Demo;
Counter in Dot
The Dot
component also contains a text counter. For more information, refer to Dot.
tsx
import React from 'react';
import Button from '@semcore/button';
import NotificationM from '@semcore/icon/Notification/m';
import Dot from '@semcore/dot';
import { AnimatedNumber } from '@semcore/counter';
const notificationsCount = 18;
const Demo = () => (
<Button title={'Notifications'} aria-describedby='notification-count'>
<Button.Addon>
<NotificationM />
<Dot up>
<AnimatedNumber
initValue={10}
value={notificationsCount}
duration={1000}
delay={500}
formatValue={(x) => Math.round(x).toString()}
id='notification-count'
/>
</Dot>
</Button.Addon>
</Button>
);
export default Demo;
Animated number
The AnimatedNumber
component allows showing numeric value changes with animation.
tsx
import React from 'react';
import { AnimatedNumber } from '@semcore/counter';
import Button from '@semcore/button';
const Demo = () => {
const [value, setValue] = React.useState(20);
const handleClick = () => {
setValue(value + 20);
};
return (
<>
<AnimatedNumber value={value} />
<Button onClick={handleClick} mt={2}>
Rerender value
</Button>
</>
);
};
export default Demo;