Создайте селектор разделов отчёта с управляемым компонентом Transfer от Arco. Изучите реальные шкалы отступов и размеров, принципы именования переменных CSS, семантические поверхности и различие между выбором строк и их перемещением.
Самостоятельная интерпретация поведения Transfer из HTML и проверенных опорных значений темы; отдельный встроенный пример React приведён ниже. · Справочник по реализации
02
Справочник по дизайн-системе Arco Design
Исходный код Arco React. Константы Less указаны с префиксом @; переменные CSS во время выполнения — с --. Отступы и компоновка панелей предварительного просмотра — оригинальные решения для приложения, основанные на этих шкалах.
Дизайн-система Arco Design: Transfer показывает принадлежность к набору, а не просто список флажков
Продуманно выбирайте разделы отчёта
Arco Transfer подходит для случаев, когда оператору нужно сравнить два списка: доступные разделы и разделы, включённые в отчёт. Выбор строки отмечает её для последующего действия; раздел перемещается не сразу, если только не включён простой режим.
В примере сохранено явное разделение действия на два шага. Пользователь выбирает «Каналы привлечения», нажимает «Включить», а затем подготавливает план. План представляет собой отдельный снимок, поэтому последующие изменения в редакторе не изменяют подготовленную версию незаметно для пользователя.
Arco Design Справочник по дизайн-системе. Используйте шкалы размеров и отступов отдельно
Одинаковое число может обозначать разные величины
Константы размеров Arco увеличиваются с шагом 4 пикселя, тогда как шкала отступов начинается со значений 2, 4, 6, 8, 10 и 12, а затем переходит к более крупным шагам. Размер компонента по умолчанию соответствует 32px. Если считать, что суффикс каждого размера или отступа обозначает одну и ту же величину, элементы управления будут иметь непоследовательные размеры.
В этой оригинальной композиции у двух панелей переноса средний радиус скругления, а отступ страницы составляет 24px. Небольшие флажки и разделители строк поддерживают плотный список для выбора; более крупные области страницы обеспечивают визуальное разделение, не заменяя полезное содержимое чрезмерно большими карточками.
Arco Design Справочник по дизайн-системе. Сохраняйте семантические роли в стилях приложения
CSS Для переменных действует собственное соглашение об именовании
Тема по умолчанию экспортирует переменные, например --color-text-1 и --color-fill-2, без префикса arco. В пользовательских сборках этот префикс может отличаться, поэтому при интеграции в приложение с пространством имён проверьте скомпилированную тему. Переменные основной палитры содержат каналы RGB; используйте rgb(var(--primary-6)), а не воспринимайте переменную как готовый цвет в формате hex.
В примере для основного действия и выбранных строк используется проверенный эталонный синий Arco. Для недоступного раздела показаны неактивный флажок и поясняющий текст, а не красный цвет, который мог бы создать впечатление об ошибке, исправимой оператором.
Arco Design Справочник по дизайн-системе. Поддерживайте компактные подписи и длинные названия разделов
Практичный размер основного текста
Размер основного текста по умолчанию — 14px, межстрочный интервал — 1.5715 от размера шрифта. Опубликованный стек начинается с Inter и включает системные шрифты и шрифты с поддержкой CJK. Это стек CSS, а не подтверждение того, что файл шрифта Inter был загружен.
Название раздела отчёта может быть длиннее примера. В автономной версии текст строк переносится, а на узком экране панели располагаются одна над другой, чтобы подписи «Включить» и «Удалить» оставались читаемыми. Вместо этого весь виджет переноса не сжимается до размера, при котором текст становится неразборчивым.
Arco Design Справочник по дизайн-системе. Два управляемых массива исключают неоднозначное поведение
Принадлежность к списку и выбор — разные состояния
targetKeys определяет, какие элементы входят в отчёт. selectedKeys содержит флажки, отмеченные в данный момент в обоих списках. onSelectChange возвращает массивы левого и правого списков; объедините их при управлении selectedKeys. onChange возвращает новый состав списка, направление и перемещённые ключи.
Сохраняйте стабильность ключей исходных элементов, даже если меняются отображаемые подписи.
Объединяйте массивы выбранных элементов слева и справа в onSelectChange.
Заменяйте targetKeys новым массивом из onChange.
После перемещения сбрасывайте временный выбор и сообщайте о его результате в текущем списке.
Arco Design Справочник по дизайн-системе. Поиск должен сужать отображение, не меняя состав списка
Явно задайте правило поиска
Фильтр по умолчанию в исходном коде сравнивает запрос со значением элемента с помощью проверки наличия подстроки с учётом регистра. В примере эта проверка заменена на поиск в нижнем регистре. Если поиск скрыл выбранную строку, это не должно создавать впечатление, что она исключена из отчёта.
Для такого небольшого списка пагинация и виртуализация почти не дают преимуществ. Arco предоставляет эти возможности для более объёмных данных, но за стабильные ключи, полный набор целевых элементов и правила выбора элементов в отфильтрованных списках по-прежнему отвечает приложение.
Для доступных и включённых списков предусмотрены отдельные поля поиска с понятными подписями.
Сообщение об отсутствии совпадений относится к отфильтрованному представлению, а не к пустому набору данных.
При подготовке плана используйте targetKeys, а не только видимые сейчас строки.
Arco Design Справочник по дизайн-системе. В тёмном режиме меняются не только текст, но и поверхности
Задайте опубликованный селектор
Arco публикует переопределения для тёмной темы с тегом темы, которому задано arco-theme="dark". В сборке по умолчанию этот тег — body. Удаление атрибута восстанавливает обычные правила; класс с произвольным именем dark — не то же самое API.
При добавлении предпросмотра тёмной темы согласуйте роли контейнера и текста. Автономный пример намеренно оставлен светлым, а нативный пример может использовать уже импортированную тему CSS. Не инвертируйте миниатюры отчётов или иллюстрации с помощью общего фильтра.
Arco Design Справочник по дизайн-системе. Пакет, таблица стилей и реальные границы возможностей
План отчёта не экспортирует данные отчёта
Пакет React — @arco-design/web-react, а полная точка входа для CSS — dist/css/arco.css. В примере компоненты Transfer и Button импортируются из корневого модуля пакета, а состояние контролируется приложением; несуществующий API для построения отчётов не придумывается.
Автономный пример воспроизводит поведение выбора с помощью нативных флажков и кнопок, поэтому работает без подключения к сети. Он проверяет логику взаимодействия и адаптивную композицию, но не подтверждает, что нативный пакет скомпилирован в проекте-потребителе. Для экспорта в рабочей среде потребуются серверная часть и проверки разрешений, которых нет в примере с неактивной строкой.
Сохраните весь документ как index.html и откройте его в браузере. Автономная HTML демонстрация поведения Arco Transfer и проверенных эталонных значений темы; отдельный нативный пример React приведён отдельно.
html
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Arco report section transfer</title><style>
*{box-sizing:border-box}body{margin:0;color:#1d2129;background:#f7f8fa;font:14px/22px Arial,sans-serif}header{padding:20px 28px;background:white;border-bottom:1px solid #e5e6eb;display:flex;justify-content:space-between}.logo{font-size:18px;font-weight:bold;letter-spacing:-.02em}.logo i{font-style:normal;color:#165dff;margin-right:8px}header small{color:#4e5969}main{max-width:1050px;padding:28px;margin:auto}h1{font-size:24px;line-height:32px;margin:0 0 8px}.lead{color:#4e5969;margin:0 0 24px}.transfer{display:grid;grid-template-columns:minmax(0,1fr) 106px minmax(0,1fr);align-items:stretch}.list{border:1px solid #e5e6eb;background:white;border-radius:4px;min-width:0}.head{padding:16px;background:#f2f3f5;display:flex;gap:8px;justify-content:space-between}.head h2{font-size:14px;margin:0}.count{font-size:12px;color:#4e5969}.search{padding:12px;border-bottom:1px solid #e5e6eb}.search label{font-size:12px;display:block;margin-bottom:4px;color:#4e5969}.search input{height:32px;width:100%;border:1px solid transparent;background:#f2f3f5;padding:4px 12px;font:inherit;border-radius:2px}input:focus,button:focus-visible{outline:2px solid #165dff;outline-offset:2px}.items{min-height:290px;padding:8px}.item{display:flex;gap:10px;padding:12px 8px;border-radius:2px;align-items:start;cursor:pointer}.item:has(input:checked){background:#e8f3ff}.item input{width:16px;height:16px;accent-color:#165dff;margin-top:3px;flex-shrink:0}.item small{display:block;color:#4e5969;font-size:12px}.item:has(input:disabled){color:#86909c;cursor:not-allowed}.ops{padding:16px 12px;display:flex;flex-direction:column;gap:12px;justify-content:center}button{font:14px/22px Arial;border:1px solid #e5e6eb;background:white;color:#1d2129;border-radius:2px;padding:5px 12px;cursor:pointer}.ops button{padding:5px 4px}.primary{background:#165dff;border-color:#165dff;color:#fff}button:disabled{background:#f2f3f5;color:#86909c;border-color:#e5e6eb;cursor:not-allowed}.footer{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 0}.footer p{font-size:12px;color:#4e5969;margin:0}.outline{background:white;border:1px solid #e5e6eb;border-radius:4px;padding:24px}.outline h2{font-size:16px;margin:0 0 12px}.outline ol{margin:0;padding-left:20px}.note{color:#4e5969;font-size:12px;margin-top:20px}.empty{padding:20px 8px;color:#4e5969}.selection{font-size:12px;color:#4e5969;padding:10px 16px;border-top:1px solid #e5e6eb}@media(max-width:650px){main{padding:24px 16px}.transfer{grid-template-columns:1fr}.ops{flex-direction:row;padding:12px}.ops button{padding:6px 16px}.items{min-height:160px}.footer{align-items:start;flex-direction:column}header{padding:16px}}
</style><header><span class="logo"><i>A /</i> Analytics workspace</span><small>Report studio</small></header><main><h1>Choose report sections</h1><p class="lead">Build an outline from the sections available to this workspace.</p><div class="transfer"><section class="list"><div class="head"><h2>Available sections</h2><span id="left-count" class="count"></span></div><div class="search"><label for="left-search">Find available section</label><input id="left-search" type="search" placeholder="Search by section name"></div><div class="items" id="left"></div><div class="selection" id="left-selected"></div></section><div class="ops"><button class="primary" id="include" disabled>Include →</button><button id="remove" disabled>← Remove</button></div><section class="list"><div class="head"><h2>Included in report</h2><span id="right-count" class="count"></span></div><div class="search"><label for="right-search">Find included section</label><input id="right-search" type="search" placeholder="Search included sections"></div><div class="items" id="right"></div><div class="selection" id="right-selected"></div></section></div><div class="footer"><p id="status" role="status">Select a section, then choose Include or Remove.</p><button id="prepare" class="primary">Prepare outline</button></div><section class="outline"><h2>Prepared outline</h2><div id="snapshot">No outline prepared yet.</div></section><p class="note">HTML interpretation of Arco Transfer · Original sample sections · Local outline only, no analytics data exported</p></main><script>
const data=[{key:'overview',value:'Executive overview',detail:'A concise summary of the reporting period'},{key:'traffic',value:'Acquisition channels',detail:'How people arrive at your product'},{key:'retention',value:'Retention cohorts',detail:'Returning usage by cohort'},{key:'revenue',value:'Revenue breakdown',detail:'Revenue categories and trends'},{key:'raw',value:'Raw event export',detail:'Restricted · unavailable in this specimen',disabled:true}];const $=id=>document.getElementById(id);let target=new Set(['overview']),selected=new Set();
function render(){for(const side of ['left','right']){const items=data.filter(x=>target.has(x.key)===(side==='right')),query=$(side+'-search').value.toLowerCase(),host=$(side);host.replaceChildren();$(side+'-count').textContent=items.length+' sections';const visible=items.filter(x=>x.value.toLowerCase().includes(query));if(!visible.length){const p=document.createElement('p');p.className='empty';p.textContent=query?'No matching sections.':'No sections in this column.';host.append(p)}for(const item of visible){const label=document.createElement('label');label.className='item';const input=document.createElement('input');input.type='checkbox';input.id='check-'+item.key;input.checked=selected.has(item.key);input.disabled=!!item.disabled;input.setAttribute('aria-label',item.value);input.onchange=()=>{input.checked?selected.add(item.key):selected.delete(item.key);render();$('check-'+item.key)?.focus()};const text=document.createElement('span'),small=document.createElement('small');text.textContent=item.value;small.textContent=item.detail;text.append(small);label.append(input,text);host.append(label)}$(side+'-selected').textContent=items.filter(x=>selected.has(x.key)).length+' selected'}$('include').disabled=!data.some(x=>selected.has(x.key)&&!target.has(x.key)&&!x.disabled);$('remove').disabled=!data.some(x=>selected.has(x.key)&&target.has(x.key)&&!x.disabled);$('prepare').disabled=!target.size}
function move(toTarget){const moved=data.filter(x=>selected.has(x.key)&&target.has(x.key)!==toTarget&&!x.disabled);for(const item of moved)toTarget?target.add(item.key):target.delete(item.key);selected.clear();render();$('status').textContent=moved.length+' section(s) '+(toTarget?'included in':'removed from')+' the local draft.';$(toTarget?'right-search':'left-search').focus()}
$('include').onclick=()=>move(true);$('remove').onclick=()=>move(false);for(const side of ['left','right'])$(side+'-search').oninput=render;$('prepare').onclick=()=>{const ol=document.createElement('ol');for(const item of data.filter(x=>target.has(x.key))){const li=document.createElement('li');li.textContent=item.value;ol.append(li)}$('snapshot').replaceChildren(ol);$('status').textContent='Local report outline prepared. No data exported.'};render();
</script></html>
Переместить ограниченный раздел отчёта нельзя. Выбранные строки и включённые разделы — это разные состояния, как и в описании нативного Transfer.
Управляемое перемещение разделов отчёта
Transfer управляет составом исходного и целевого списков, а состояние приложения хранит отдельно подготовленный план. Недоступная строка демонстрирует отключённый вариант, но не имитирует серверную проверку прав доступа.
jsx
// npm install @arco-design/web-react react react-dom
import React,{useState} from 'react';
import {createRoot} from 'react-dom/client';
import {Transfer,Button} from '@arco-design/web-react';
import '@arco-design/web-react/dist/css/arco.css';
const data = [
{key:'overview',value:'Executive overview'},
{key:'traffic',value:'Acquisition channels'},
{key:'retention',value:'Retention cohorts'},
{key:'revenue',value:'Revenue breakdown'},
{key:'raw',value:'Raw event export (restricted)',disabled:true}
];
function ReportBuilder(){
const [target,setTarget]=useState(['overview']);
const [selected,setSelected]=useState([]);
const [snapshot,setSnapshot]=useState([]);
const [status,setStatus]=useState('Choose sections for a local report.');
return <main style={{padding:24,maxWidth:960,margin:'auto'}}>
<h1>Report sections</h1>
<Transfer dataSource={data} targetKeys={target} selectedKeys={selected}
titleTexts={['Available sections','Included in report']}
operationTexts={['Include','Remove']}
showSearch searchPlaceholder={['Find available section','Find included section']}
filterOption={(query,item)=>item.value.toLowerCase().includes(query.toLowerCase())}
onSelectChange={(left,right)=>setSelected([...left,...right])}
onChange={(keys,direction,moved)=>{
setTarget(keys);setSelected([]);
setStatus(`${moved.length} section(s) moved to ${direction}. Draft only.`);
}} />
<p role="status">{status}</p>
<Button type="primary" disabled={!target.length} onClick={()=>{
setSnapshot([...target]);setStatus('Local report outline prepared. No data exported.');
}}>Prepare outline</Button>
<h2>Prepared outline</h2>
{snapshot.length ? <ol>{snapshot.map(key=><li key={key}>{data.find(item=>item.key===key).value}</li>)}</ol> : <p>No outline prepared yet.</p>}
</main>;
}
createRoot(document.getElementById('root')).render(<ReportBuilder />);
Выбор с помощью флажков и включение в отчёт хранятся в разных массивах.
Пользовательский фильтр не учитывает регистр.
Данные примера созданы специально; аналитические данные не загружаются и не экспортируются. Нативная компиляция проверяется отдельно в рамках интеграции.
04
Дизайн-система Arco Design Настройка агента
Отправная точка для реализации
Импортируйте полную таблицу стилей arco.css один раз и смонтируйте пример React в корневой элемент. Не смешивайте пакет React с импортами Arco Vue.
Используйте Arco React Transfer со стабильными ключами dataSource и отдельными управляемыми значениями targetKeys и selectedKeys. Добавьте фильтр без учёта регистра, явные действия «Включить» и «Удалить», а также снимок подготовленного плана. Сохраняйте синтаксис переменных Arco CSS и корректно обозначайте автономную демонстрацию.
05
Дизайн-система Arco Design Источники и область применения
Независимый справочник OpenDesign. Оригинальные данные отчёта и локальный предпросмотр; отключённые строки не обеспечивают проверку прав доступа.