Flipper
Flipper — круглая кнопка-значок: клик переводит value к следующему пункту values, с последнего — к первому. Значок и подпись текущего значения берутся из пункта. Два значения — это Toggle Button; Flipper нужен, когда значений больше двух и они идут по кругу.
Статус: experimental · в системе с 2026-08 · интерактивность: always (всегда)
Когда использовать
- Направление или порядок сортировки в тулбаре списка: без сортировки → сначала новые → сначала закреплённые.
- Циклический режим над рабочей областью секции: следующий шаг очевиден, а все значения показывать незачем.
- Место — один значок: ряд из всех значений в строке не помещается.
Не использовать:
- Значений два — это Toggle Button; закрепить и открепить — Toggle Button
short. - Все значения нужно видеть сразу и выбирать любое — используй Segmented Control.
- Список длинный или значения не образуют круг — используй Select.
Альтернативы:
toggle-button— два состояния одного режима, pin —shortsegmented-control— все значения видны сразу, выбор любого без перебораselect— длинный список значений
Базовый
Порядок выдачи из трёх значений по кругу; подпись рядом показывает текущее.
<!--
🟢
Базовый пример перебирает порядок выдачи по кругу: без сортировки →
сначала новые → сначала закреплённые → снова без сортировки. Подпись
рядом показывает текущее значение вне кнопки.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
const order = [
{ value: "none", label: t("Без сортировки"), icon: "ArrowsDownUp" },
{ value: "newest", label: t("Сначала новые"), icon: "CaretDown" },
{ value: "pinned", label: t("Сначала закреплённые"), icon: "PushPin" },
];
let value = $state("none");
let current = $derived(order.find((item) => item.value === value));
</script>
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<Flipper label={t("Порядок выдачи")} values={order} bind:value />
<span aria-live="polite">{current?.label}</span>
</div>
Плоский
variant="ghost" в строке тулбара рядом с переключателем представления.
Порядок: Без сортировки
<!--
🟢
Плоский Flipper в строке тулбара списка: рядом с переключателем
представления, как updown кита ADMY. Круг в покое не рисуется и проявляется
контуром только под указателем; строка под тулбаром называет текущий порядок.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
import SegmentedControl from "$lib/ui/SegmentedControl.svelte";
const views = [
{ value: "rows", label: t("Строки") },
{ value: "table", label: t("Таблица") },
];
const order = [
{ value: "none", label: t("Без сортировки"), icon: "ArrowsDownUp" },
{ value: "newest", label: t("Сначала новые"), icon: "CaretDown" },
{ value: "pinned", label: t("Сначала закреплённые"), icon: "PushPin" },
];
let view = $state("rows");
let result = $state(t("Без сортировки"));
</script>
<div style="display: grid; gap: var(--sp-2); justify-items: start;">
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<SegmentedControl items={views} bind:value={view} />
<Flipper
variant="ghost"
label={t("Порядок выдачи")}
values={order}
onchange={(item) => (result = item.label)}
/>
</div>
<p aria-live="polite" style="margin: 0; font-size: var(--fs-s); color: var(--c-text-muted);">
{t("Порядок:")} {result}
</p>
</div>
Размеры
s для плотной строки, m для самостоятельного контрола.
<!--
🟢
Пример размеров сравнивает компактный и обычный Flipper на одном списке
значений. Плотность меняет круг и значок, но не цикл.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
const order = [
{ value: "none", label: t("Без сортировки"), icon: "ArrowsDownUp" },
{ value: "newest", label: t("Сначала новые"), icon: "CaretDown" },
{ value: "pinned", label: t("Сначала закреплённые"), icon: "PushPin" },
];
</script>
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<Flipper size="s" label={t("Порядок выдачи, компактный")} values={order} />
<Flipper size="m" label={t("Порядок выдачи, обычный")} values={order} />
</div>
Панель фильтров
Двухпозиционный flipper-drawer легаси: панель показана или свёрнута, значок Funnel.
<!--
🟢
Панель фильтров: свёрнута или показана — значок Funnel в обоих
положениях, меняется подпись (эталон: Vue-легаси flipper-drawer).
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
const values = [
{ value: "closed", label: t("Показать фильтры"), icon: "Funnel" },
{ value: "open", label: t("Скрыть фильтры"), icon: "FunnelSimple" },
];
let value = $state("closed");
let current = $derived(values.find((item) => item.value === value));
</script>
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<Flipper label={t("Панель фильтров")} variant="ghost" size="s" {values} bind:value />
<span aria-live="polite">{current?.label}</span>
</div>
Раскрытие
flipper-dropdown легаси: свёрнуто или раскрыто, стрелка CaretDown и CaretUp.
<!--
🟢
Раскрытие блока: стрелка вниз — свёрнуто, вверх — раскрыто
(эталон: Vue-легаси flipper-dropdown).
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
const values = [
{ value: "collapsed", label: t("Раскрыть"), icon: "CaretDown" },
{ value: "expanded", label: t("Свернуть"), icon: "CaretUp" },
];
let value = $state("collapsed");
let current = $derived(values.find((item) => item.value === value));
</script>
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<Flipper label={t("Подробности")} variant="ghost" size="s" {values} bind:value />
<span aria-live="polite">{current?.label}</span>
</div>
Закрепление
flipper-pin легаси: закреплено или откреплено, PushPin и PushPinSlash.
<!--
🟢
Закрепление строки: PushPin — закрепить, PushPinSlash — открепить
(эталон: Vue-легаси flipper-pin).
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
const values = [
{ value: "unpinned", label: t("Закрепить"), icon: "PushPin" },
{ value: "pinned", label: t("Открепить"), icon: "PushPinSlash" },
];
let value = $state("unpinned");
let current = $derived(values.find((item) => item.value === value));
</script>
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<Flipper label={t("Закрепление")} variant="ghost" size="s" {values} bind:value />
<span aria-live="polite">{current?.label}</span>
</div>
Высота строк
flipper-rowHeight легаси: плотные или просторные строки таблицы, Rows и RowsPlusBottom.
<!--
🟢
Высота строк таблицы: плотные или просторные
(эталон: Vue-легаси flipper-rowHeight).
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Flipper from "$lib/ui/Flipper.svelte";
const values = [
{ value: "dense", label: t("Плотные строки"), icon: "Rows" },
{ value: "spacious", label: t("Просторные строки"), icon: "RowsPlusBottom" },
];
let value = $state("dense");
let current = $derived(values.find((item) => item.value === value));
</script>
<div style="display: flex; align-items: center; gap: var(--sp-2);">
<Flipper label={t("Высота строк")} variant="ghost" size="s" {values} bind:value />
<span aria-live="polite">{current?.label}</span>
</div>