Dualist
Dualist показывает общее поле поиска сверху, полный список слева и выбранные элементы справа. Поиск, удаление и общий сброс помогают работать с длинным набором.
Статус: experimental · в системе с 2026-06 · интерактивность: always (всегда)
Когда использовать
- Множественный выбор из длинного списка (десятки элементов) с поиском.
- Выбранное важно видеть рядом со списком: порядок, удаление, «Clear All».
Не использовать:
- 2–3 варианта — хватит чекбоксов.
- Выбор одного значения.
Альтернативы:
checkbox-group— короткий набор с общей подписьюselect— одно значение из спискаmulti-select— компактный выбранный набор помещается внутри одного поля
Конструктор
Меняйте исходный выбор и устройство поиска, затем проверяйте фильтрацию, Select All, удаление и редактирование прямо в превью.
Базовый
Поиск без подписи и Select All. Выбранные элементы можно редактировать и удалять; счётчик показывает выбор относительно списка.
<script>
import Dualist from "$lib/ui/Dualist.svelte";
const NUMBERS = [
"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
"eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen",
"eighteen", "nineteen", "twenty",
];
const items = NUMBERS.map((w, i) => ({ value: `item-${i + 1}`, label: `Item ${w}`, editable: true }));
let value = $state(["item-8"]);
</script>
<Dualist {items} bind:value />
Поиск с подписью и Select All
Поиск с подписью и Select All. Запрос «R» переводит счётчик из режима «выбрано / всего» в «выбрано / найдено».
<script>
import Dualist from "$lib/ui/Dualist.svelte";
const items = [
{ value: "regalith", label: "Regalith" },
{ value: "russia", label: "Russia", editable: true },
{ value: "object-one", label: "Object one", warning: true },
{ value: "rochelia", label: "Rochelia" },
{ value: "ruloria", label: "Ruloria" },
{ value: "rivermore", label: "Rivermore" },
{ value: "romania", label: "Romania" },
{ value: "rwanda", label: "Rwanda" },
{ value: "rdplaa", label: "Rdplaa" },
{ value: "reserved", label: "Reserved" },
{ value: "really", label: "Really" },
{ value: "long", label: "Very-very long item name" },
];
let value = $state(["regalith", "russia", "object-one"]);
</script>
<Dualist {items} bind:value searchLabel="Search" searchRequired selectAll />
Пустой менеджер
Пока ничего не выбрано — правая панель пуста, остаётся только «Clear All».
<script>
import Dualist from "$lib/ui/Dualist.svelte";
const NUMBERS = ["one", "two", "three", "four", "five", "six", "seven", "eight"];
const items = NUMBERS.map((w, i) => ({ value: `item-${i + 1}`, label: `Item ${w}`, editable: true }));
</script>
<Dualist {items} value={[]} />
Режим и добавление
withMode — включить/исключить над панелями, creatable — кнопка добавления из поиска, loading — скелетоны в конце списка.
<!--
🟢
Режим и добавление (эталон: Vue-легаси dualList type + creatable):
переключатель включить/исключить над панелями, кнопка добавления из
поиска, скелетоны в конце списка при `loading`.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Dualist from "$lib/ui/Dualist.svelte";
let items = $state(Array.from({ length: 12 }, (_, i) => ({ value: `a${i + 1}`, label: `${t("Аудитория")} ${i + 1}` })));
let value = $state(["a2"]);
let mode = $state("include");
let loading = $state(false);
function add(query) {
const label = query.trim() || `${t("Аудитория")} ${items.length + 1}`;
const created = { value: `new-${Date.now()}`, label };
items = [created, ...items];
value = [...value, created.value];
}
</script>
<div style="display: flex; flex-direction: column; gap: var(--sp-2);">
<Dualist {items} bind:value bind:mode withMode creatable onadd={add} {loading} searchLabel={t("Поиск аудиторий")} />
<label style="font-size: var(--fs-xs);"><input type="checkbox" bind:checked={loading} /> loading</label>
<span aria-live="polite">{t("Режим:")} {mode}{t(", выбрано")} {value.length}</span>
</div>