Segmented Buttons

Описание

Segmented buttons помогают пользователям выбирать параметры, переключать виды или сортировать элементы. Подробная информация на официальном сайте

Если кратко, есть два вида данных кнопок: Single-select и Multi-select.

Первый используется для выбора одной опции, в частности когда требуется переключить отображение (grid - rows), при сортировке.

Второй вариант пригодится для выбора нескольких опций: при фильтрации и похожих задачах.

Примеры

Single-select

1000 рублей = 0$

Multi-select

С иконками

Разные значения высоты

Свойство Density можно использовать в более компактных UI, где пространство ограничено. Density применяется только к высоте.

0 (40px)
-1 (36px)
-2 (32px)
-3 (28px)

Компонент

Скопируйте и вставьте код в свой проект.

Обратите внимание, что компонент имеет зависимости таких компонентов как Label и UIStateLayer. Если вы еще не добавили их в свой проект, следует заняться сперва ими.

Также необходимо установить зависимость @radix-ui/react-toggle-group

yarn add @radix-ui/react-toggle-group

Можно сохранить компонент в файл src/shared/ui/SegmentedButton.tsx:

"use client"
import React from "react"
import * as ToggleGroup from "@radix-ui/react-toggle-group"
import { cva, type VariantProps } from "class-variance-authority"
import Label from "@/shared/typography/Label"
import UIStateLayer from "@/shared/ui/UIStateLayer"
import { cn } from "@/lib/cn"
const segmentedRootVariants = cva(
"flex border border-outline rounded-full divide-x divide-outline min-w-[200px]",
{
variants: {
density: {
"0": "h-10",
"-1": "h-9",
"-2": "h-8",
"-3": "h-7",
},
},
defaultVariants: {
density: "0",
},
}
)
const SegmentedRoot = React.forwardRef<
React.ElementRef<typeof ToggleGroup.Root>,
React.ComponentPropsWithoutRef<typeof ToggleGroup.Root> &
VariantProps<typeof segmentedRootVariants>
>(({ className, density, ...props }, forwardedRef) => (
<ToggleGroup.Root
className={cn(segmentedRootVariants({ density }), className)}
{...props}
ref={forwardedRef}
/>
))
SegmentedRoot.displayName = "SegmentedRoot"
interface SegmentedButtonProps
extends React.ComponentPropsWithoutRef<typeof ToggleGroup.Item> {
icon?: React.ReactNode
}
const SegmentedButton = React.forwardRef<
React.ElementRef<typeof ToggleGroup.Item>,
SegmentedButtonProps
>(({ className, children, icon, ...props }, forwardedRef) => (
<ToggleGroup.Item
className={cn("group", className)}
{...props}
ref={forwardedRef}
asChild
>
<button className="flex-1 text-onSurface data-[state=on]:text-onSecondaryContainer data-[state=on]:bg-secondaryContainer first:rounded-l-full last:rounded-r-full transition-colors duration-short4 ease-standard">
<UIStateLayer className="flex justify-center items-center gap-2 py-2 px-3 bg-onSurface group-data-[state=on]:bg-onSecondaryContainer group-first:rounded-l-full group-last:rounded-r-full group-data-[state=on]:bg-opacity-0 group-data-[state=on]:group-hover:bg-opacity-[0.08] group-data-[state=on]:group-active:bg-opacity-[0.12]">
{icon && icon}
<Label size={"large"}>{children}</Label>
</UIStateLayer>
</button>
</ToggleGroup.Item>
))
SegmentedButton.displayName = "SegmentedButton"
export { SegmentedButton, SegmentedRoot }

Использование

Обратите внимание, файл экспортирует два компонента: SegmentedRoot и SegmentedButton.

Первый служит в качестве контейнера, второй - в качестве самой кнопки.

Single-select и Multi-select

import { SegmentedButton, SegmentedRoot } from "@/components/ui/SegmentedButton"
import { useState } from "react"
...
const [state, setState] = useState("usd")
...
<SegmentedRoot
className="w-[500px]"
type="single"
value={state}
onValueChange={(value) => value && setState(value)}
>
<SegmentedButton
name="currency"
value="try"
>
Лира
</SegmentedButton>
<SegmentedButton
name="currency"
value="usd"
>
Доллары
</SegmentedButton>
<SegmentedButton
name="currency"
value="cny"
>
Юань
</SegmentedButton>
</SegmentedRoot>

С иконками

Обратите внимание, что по стайл гайдам размеры иконок уменьшены с 24x24 до 18x18. Вам придётся делать это вручную.

import { List, ViewGrid } from "iconoir-react"
...
<SegmentedRoot type="single" defaultValue="grid">
<SegmentedButton
value="grid"
icon={<ViewGrid width={18} height={18} />}
>
Grid
</SegmentedButton>
<SegmentedButton
value="list"
icon={<List width={18} height={18} />}
>
List
</SegmentedButton>
</SegmentedRoot>

Density

// Density 0 уровня (по умолчанию)
<SegmentedRoot type="single" defaultValue="1">
<SegmentedButton value="1">1</SegmentedButton>
<SegmentedButton value="2">2</SegmentedButton>
<SegmentedButton value="3">3</SegmentedButton>
</SegmentedRoot>
// Density -1 уровня
<SegmentedRoot type="single" defaultValue="1" density="-1">
<SegmentedButton value="1">1</SegmentedButton>
<SegmentedButton value="2">2</SegmentedButton>
<SegmentedButton value="3">3</SegmentedButton>
</SegmentedRoot>
// Density -2 уровня
<SegmentedRoot type="single" defaultValue="1" density="-2">
<SegmentedButton value="1">1</SegmentedButton>
<SegmentedButton value="2">2</SegmentedButton>
<SegmentedButton value="3">3</SegmentedButton>
</SegmentedRoot>
// Density -3 уровня
<SegmentedRoot type="single" defaultValue="1" density="-3">
<SegmentedButton value="1">1</SegmentedButton>
<SegmentedButton value="2">2</SegmentedButton>
<SegmentedButton value="3">3</SegmentedButton>
</SegmentedRoot>