Представление данных
<sf-table> показывает серверную коллекцию записей: поиск, фильтры, таблицу,
выбор строк, действия, настройки колонок и пагинацию. Данные, права и
выполнение действий остаются за приложением: компонент показывает то, что
ему передали, и сообщает о намерениях пользователя событиями.
<sf-data-view> оборачивает таблицу и связывает её с приложением через порт
хоста. Оба элемента входят в пакет компонентов larena.
Пример
Применение
Подключение пакета
Элементы <sf-table> и <sf-data-view> поставляет Larena, а не выпуск
Framework: пакет компонентов Loader с именем larena из репозитория
simai/larena-ui (лицензия Apache-2.0). В пакет входят ещё
<sf-property-view> и
<sf-admin-menu>. Объявите
пакет до подключения Core:
window.SF_BOOT_CONFIG = {packages: {larena: {base: '/assets/larena/<коммит пакета>/smart'}}}.
Сборка пакета публикуется выпуском репозитория simai/larena-ui
(см. Пакет larena для проекта без Larena). Подробнее — в разделе
Подключение Smart-компонентов.
Имя и ключ настроек
aria-label называет область таблицы; доступное имя у неё обязательно.
table-settings-key выбирает, под каким ключом хранятся
настройки колонок пользователя. Без него ключом служит id элемента, а без
id — номер экземпляра на странице, поэтому у таблицы, которая живёт на
нескольких страницах, ключ задавайте явно.
Колонки и строки
Данные передаются методами, а не атрибутами. setColumns(columns) задаёт
разрешённые приложением колонки, setRows(rows) — текущие записи,
setTableData(patch) обновляет несколько частей сразу, addRows(rows)
добавляет следующую страницу в режиме «Показать ещё». У колонки нужен
стабильный key, у строки — стабильный id.
Колонка с sortable: true получает сортировку по нажатию на заголовок.
Таблица строки не переставляет: она отправляет sf-table-sort-change с
{key, direction} (asc, desc или null), приложение загружает
отсортированные данные и показывает текущую сортировку через
setSort(key, direction).
Состояние данных
data-state или setDataState(state) показывает, в каком состоянии данные:
loading, populated, empty, error. По умолчанию auto: есть строки —
таблица заполнена, нет — пуста. Повторная загрузка поверх уже показанных
строк оставляет их на месте притушенными. В состоянии error таблица
показывает кнопку повтора; нажатие отправляет sf-table-retry. Тексты
состояний задают loading-text, empty-text, error-text и retry-text;
без них компонент берёт тексты на языке страницы. loading-text на экране не
виден: пока стоит скелетон, его объявляет экранный диктор.
Части оболочки
Логические атрибуты включают и выключают части представления: toolbar —
панель инструментов, selectable — выбор строк и массовые действия,
settings — настройки колонок, actions — действия строки и кнопку
«Создать», row-actions — только колонку действий строки (кнопка «Создать»
остаётся). Встроенную пагинацию включает pagin; pagination-total,
pagination-page-size и page-sizes задают общее число записей, размер
страницы и выбор размеров. Её события sf-page-change,
sf-page-size-change и sf-show-more приходят от
Pagination.
vertical-scroll="page" отдаёт прокрутку странице вместо самой таблицы,
context-menu-columns (от 1 до 4) делит меню настроек на колонки.
У <sf-data-view> свои части перечисляет атрибут chrome: toolbar,
pagination, select, settings; без атрибута включены все. Часть
toolbar — запасная строка поиска самого представления. Если в слот
content вставлена своя <sf-table> со своей панелью инструментов,
запасная строка не показывается. Без toolbar в chrome представление
ставит таблице layout="table"; список на узком экране сохраняет
layout="auto", записанный на самой таблице.
Область представления называет aria-label самого <sf-data-view>; без
него — общее «Представление данных». Строка страниц с «Всего» появляется
после первого ответа приложения и скрыта, пока список не загрузился, при
ошибке и при отказе в доступе.
Поиск и фильтр
Поиск отправляет sf-table-search-change со строкой запроса в
event.detail после паузы ввода. setFilterFields(fields) передаёт
разрешённые поля и операторы фильтра; применение фильтра отправляет
sf-table-filter-change, в event.detail — шаблон (templateKey,
templateLabel), значения values и метки tags. getFilterPayload()
возвращает тот же объект в любой момент.
Варианты полей multi-select и single-select можно показать цветными
метками, как в ячейках: filter.optionRenderer: 'badge', цвет — в
option.color (схема Framework или hex), иконка — в option.icon.
Фильтр поля-файла предлагает виды файлов — изображения, документы, архивы,
другие, — а фильтр поля-цвета показывает палитру поля образцами; оба
отправляют выбранные значения с оператором in, а что входит в каждый вид,
решает приложение. Цвет без палитры ищется как текст. Текстовый фильтр
подписан «Содержит…».
Ячейки колонок и элементы фильтра по типу свойства рисует общий набор, описанный в главе Поля по типу свойства.
Действия и создание
Действие над строкой или над выбранными строками отправляет
sf-table-action-intent с {action_id, record_ids}. setBulkActions(actions)
задаёт массовые действия; пока ничего не выбрано, они выключены.
getSelectedRecordIds() возвращает выбранные идентификаторы.
Кнопка «Создать» отправляет sf-table-create-intent с {item: null};
setCreateItems([{id, name}]) добавляет ей меню, и выбор пункта приходит
как {item: id}. setToolbarActions(items) добавляет собственные действия
в меню панели инструментов; выбор отправляет отменяемое
sf-table-toolbar-action с {action, href}, и без preventDefault()
компонент переходит по href того же сайта.
В <sf-data-view> кнопку «Создать» показывает возможность create-record
адаптера. Отдельного права на создание у человека порт не передаёт: хост
объявляет create-record в адаптере, который строит для этого человека, а
на view.create_record без права отвечает refused.
Таблица или список
layout выбирает, как рисуются записи: table — таблицей (по умолчанию),
list — компактным списком, auto — таблицей, пока контейнер таблицы не
уже порога list-below, и списком, когда уже. Решает ширина контейнера, а
не окна: таблица рядом с открытой панелью тоже становится списком.
<sf-data-view> с панелью инструментов задаёт своей таблице auto;
встроенный список без панели остаётся таблицей, а layout, который
приложение написало на <sf-table> само, сохраняется.
В компактном списке каждая запись — блок: флажок выбора (при selectable),
заголовок, под ним несколько полей с подписями и меню строки. Заголовок — колонка с
role: 'title', а без неё — первая колонка данных. Поля выбираются по
priority колонок: меньшее значение важнее, колонки без priority идут
после остальных в своём порядке. list-fields задаёт число полей (по
умолчанию три). Остальное о записи показывает её открытие, как у строки
таблицы.
Панель инструментов списка занимает одну строку: поле поиска и рядом кнопки-иконки фильтра, сортировки (если есть колонки с сортировкой) и создания. Кнопка фильтра показывает число активных условий; сами условия идут ниже строкой меток, которая прокручивается вбок, и каждую можно снять. Фильтр открывается листом на весь экран.
Дерево
Адаптер с возможностью tree отвечает деревом, и <sf-data-view>
показывает записи по уровням. Ответ на запрос несёт tree: {mode: 'tree', parent: null}
и записи одного уровня — корни; каждая запись сообщает has_children.
Открытие узла поднимает намерение tree.expand с id узла, и адаптер
отвечает его детьми с tree.parent, равным этому id. Длинный уровень
приходит страницами: tree.has_next и tree.cursor добавляют в конец
уровня строку «Показать ещё», которая запрашивает следующую страницу того
же уровня. У дерева нет обычной пагинации. Режим flat — обычный список, а
flattened — результат поиска или фильтра по дереву: каждая запись несёт
path, путь от корня, и компактный список показывает его под заголовком
записи.
Дерево растёт из колонки заголовка (role: 'title') или из первой колонки
данных. В ней строка получает отступ по уровню, кнопку открытия, если у
узла есть дети, и иконку: icon и icon_expanded записи, а без них —
папку для узла с детьми и документ для листа. Поля icon и
icon_expanded входят в порт версии 1.9.0.
Перенос узла включает возможность tree-move. Действие move из
actions записи открывает выбор места: представление спрашивает адаптер
намерением node.targets, какие родители допустимы, показывает только их
(и корень, если адаптер его разрешает) и отправляет node.move с новым
parent, позицией index, если адаптер разрешает менять порядок, и
ревизией записи. Действие create создаёт запись внутри узла:
view.create_record уходит с parent. Оба действия появляются только у
записей, которые перечисляют их в actions.
<sf-table> дерево только рисует: строки с __tree пишет
<sf-data-view>. Нажатие на кнопку узла отправляет sf-table-tree-toggle
с {id, expanded}, строка «Показать ещё» — sf-table-tree-more с
{parent, cursor}; на оба отвечает представление. Намерения дерева, которые
оно поднимает, приходят событием sf-table-tree-intent с
{intent, payload}. Своими методами expandNode(id), collapseNode(id),
loadMoreNodes(parent, cursor) и openMoveDialog(record) представление
делает то же из кода.
Ширина и порядок колонок
Ширина. Ширину колонки тянут за линию между заголовками: вокруг неё
невидимая полоса захвата, курсор меняется на col-resize, а при наведении и
во время перетаскивания по строкам видна линия края колонки. Полоса — это
кнопка с доступным именем «Изменить ширину столбца: …»: с клавиатуры на неё
переходят по Tab и меняют ширину клавишами ArrowLeft и ArrowRight.
Пределы задают minWidth и maxWidth колонки.
Порядок. Колонку переставляют, перетаскивая её заголовок. Пока колонку тянут, она приглушена во всех строках, а на месте, куда она встанет, стоит пунктирный контур во всю высоту таблицы. Закреплённая колонка переставляется только среди закреплённых, остальные — среди остальных. Отпускание не срабатывает как клик по заголовку, поэтому сортировка от него не включается. С клавиатуры порядок меняют в списке колонок в настройках таблицы. Колонка действий — системная: её не растягивают и не переносят.
Перетаскивание заголовков устроено на SF.Sortable.attach (см. Колонки таблицы):
скрипт sf-sortable таблица подгружает сама при первом перетаскивании. Каждое
изменение ширины или порядка приходит приложению событием
sf-table-column-settings-change, как описано ниже.
Перетаскивание строк и файлов
drag-rows— строки можно вытащить из таблицы как записи: запись строки или все выбранные, если строка среди них. Начало перетаскивания сообщаетsf-table-rows-dragс{ids, source}. Принимает их, например, дерево сaccept-records— так записи раскладывают по папкам.row-reorder— строки можно переставить, перетаскивая их внутри списка. Во время перетаскивания отмечено место, куда встанет строка; после отпускания приходитsf-table-rows-reorderс{ids, before, order}: перемещённые идентификаторы, строка, перед которой они встали (null— в конце), и весь новый порядок. Если порядок не изменился, события нет. Порядок сохраняет приложение; в<sf-data-view>— через порт хоста (Перестановка строк и запись в ячейке).accept-filesна<sf-data-view>— на представление можно перетащить файлы с компьютера: пока они над ним, видна зона сброса, после отпускания приходитsf-data-view-files-dropс{files}. Загружает файлы приложение.
Флажки в ячейках
С атрибутом inline-edit логическая ячейка — ячейка колонки с
type: 'boolean' — становится флажком, если запись называет поле этой
колонки в своём списке editable. Так приложение решает по каждой записи,
что человек может менять:
table.setColumns([
{key: 'title', label: 'Название'},
{key: 'published', label: 'Опубликовано', type: 'boolean'}
]);
table.setRows([
{id: 7, title: 'Отчёт за квартал', published: true, editable: ['published'], revision: 'r12'},
{id: 8, title: 'Черновик', published: false, editable: []}
]);
Нажатие флажка отправляет sf-table-cell-set с {id, key, value, revision};
пока приложение отвечает, флажок выключен. Таблица сама значение не
сохраняет: строку перерисовывает то, что ответило приложение. В
<sf-data-view> запись в ячейке идёт через порт хоста
(Перестановка строк и запись в ячейке).
Кнопки действий строки по умолчанию видны в строке под указателем, в фокусе
или текущей (row-actions-visible="hover"); always показывает их во всех
строках.
Пустые состояния
Когда ответ пришёл, а строк нет, таблица показывает внутри себя
пустое состояние своего размера
и сама выбирает причину: нет прав (refused), ошибка с «Повторить», «Ничего не
найдено» с кнопкой сброса того, что включено — поиска, фильтров или обоих, —
или «нет записей» с «Создать», если создание разрешено. Пока идёт запрос,
остаётся скелетон. empty-text и error-text становятся заголовками
состояний «нет записей» и ошибки. Слот empty заменяет блок при любой
причине, включая ошибку: «Повторить» и встроенные слова тогда не
показываются.
Уже при первой отрисовке таблица вынимает элемент слота из документа, и
поиск по id после этого вернёт null — в том числе после
customElements.whenDefined('sf-data-view'). Поэтому ссылку на блок берите на
верхнем уровне скрипта в конце body, до того как придёт код пакета, и
меняйте блок по событию sf-data-view-state. Таблица записывает причину в
свойство emptyContext до того, как представление отправляет событие:
detail.state |
Что в table.emptyContext |
|---|---|
empty |
{kind, reset}: kind — no-access, nothing-found или no-data; reset у nothing-found — search, filters или both |
error |
{caused}: true, если ошибку вызвал поиск или фильтр человека |
Действия с id retry, create и reset-query в блоке слота таблица
выполняет сама: повторяет запрос, отправляет намерение создания (оно, как
кнопка на панели инструментов, проходит проверку возможности create-record)
или очищает то, что назвал reset. Второй запрос из кода страницы не нужен.
<sf-data-view id="solutions" preferences-key="cms.solutions" aria-label="Решения">
<sf-table slot="content" aria-label="Решения">
<sf-empty-state slot="empty" id="solutions-empty" heading-level="2"></sf-empty-state>
</sf-table>
</sf-data-view>
<script>
const view = document.getElementById('solutions');
const empty = document.getElementById('solutions-empty'); // сразу, до прихода кода
const words = {
'no-data': ['Решений пока нет', [{id: 'create', text: 'Создать'}]],
'nothing-found': ['Решения не найдены', [{id: 'reset-query', text: 'Очистить поиск', appearance: 'outline'}]],
'no-access': ['Нет доступа к решениям', []],
error: ['Не удалось загрузить решения', [{id: 'retry', text: 'Повторить', appearance: 'outline'}]],
};
view.addEventListener('sf-data-view-state', ({detail}) => {
if (detail.state !== 'empty' && detail.state !== 'error') return;
const context = view.querySelector('sf-table').emptyContext ?? {};
const kind = detail.state === 'error' ? 'error' : context.kind;
const [heading, actions] = words[kind] ?? words['no-data'];
empty.setAttribute('kind', kind);
empty.setAttribute('title', heading);
empty.setAttribute('live', context.caused ? 'alert' : 'auto');
empty.actions = actions;
});
</script>
Отдельная таблица без <sf-data-view> показывает только «нет записей» и
ошибку. Подробнее — в Пустые состояния.
Прокрутка на странице списка
vertical-scroll у <sf-table> выбирает, что прокручивается вверх и вниз:
self — таблица внутри своей рамки, page — сама страница. Представление
данных по умолчанию ставит своей таблице page, если приложение не выбрало
иное. Тогда таблица растёт на высоту своих строк, и на странице списка
прокручивается одно — сама страница. По горизонтали таблица прокручивается
внутри себя: её полосу заменяет направляющая, закреплённая у нижнего края
окна. Панель инструментов держится у верхнего края, а шапка таблицы встаёт
под ней. В настройках таблицы на вкладке «Вид» это переключает «Закреплять
фильтр и шапку»; по умолчанию закрепление включено.
Персональный вид
С атрибутом preferences-key на <sf-data-view> (обычно это имя
коллекции) раскладка таблицы, сортировка и размер страницы хранятся как
персональный вид под ключом
sf-table/<preferences-key> и применяются до первого запроса: первый
запрос несёт сортировку и размер страницы человека, а первый кадр с
колонками данных уже стоит в его раскладке. Скелетон до ответа колонок
данных не показывает. Без ключа ничего не запоминается. Поля значения —
columnSettings, widths, view, sort и pageSize — таблица объявляет в
манифесте (persistence.view_preference); все пять сервер может положить в
страницу для первой отрисовки. Их описание — в справочнике <sf-table> ниже
и в Персональный вид → Поля значения.
Шаблоны и настройки колонок
Ширина, порядок, видимость и закрепление колонок — настройки представления.
Их изменение отправляет sf-table-column-settings-change с reason,
tableSettingsKey и полными настройками columnSettings; отдельная таблица
отдаёт их приложению, и оно при следующей загрузке применяет их через
setTableSettings(settings). В <sf-data-view> то же изменение всегда
уходит в личный слой человека — намерением settings.save_personal с
base_revision — ревизией слоя
data.column_settings.layers.personal, а если хост этого слоя ещё не
прислал, — ревизией из data.column_settings.target. Если нет ни того, ни
другого, представление само отвечает unavailable и намерение до хоста не
отправляет; поэтому присылайте в ответе на запрос личный слой с ревизией или
target с ней.
Ответ conflict несёт рядом с answer текущую ревизию слоя
current_revision и колонки слоя, какими они стали, — columns.
Представление показывает message уведомлением и обновляет ревизию, по
которой пишет: берёт настройки колонок из data.column_settings, если хост
их прислал, иначе current_revision и columns, а без них перечитывает
список — ответ на запрос приносит слой с текущей ревизией. Поэтому следующая запись колонок с
этого устройства идёт уже против новой ревизии.
saveColumnSettings(columnSettings, {shared, base_revision}) записывает
раскладку из кода: shared: true — в общий слой намерением
settings.save_shared (возможность shared-column-settings), без него — в
личный. Выбора слоя таблица человеку не предлагает, поэтому общий слой
записывает только код страницы этим вызовом; base_revision заменяет ревизию, которую представление взяло бы из
слоя. resetColumnSettings() делает то же, что «Сбросить» в настройках
таблицы: записывает пустую раскладку {} в личный слой против его
ревизии; значения по умолчанию определяет хост. Где при этом хранится раскладка — в
Персональный вид → Раскладка таблицы.
Сохранённые представления передаёт setTemplates(templates); изменения на
вкладке «Шаблоны» приходят событиями sf-table-template-save,
sf-table-template-select ({key}) и sf-table-template-delete ({key},
по одному на шаблон). sf-table-template-save несёт один шаблон из формы
или массив шаблонов, если «Применить» на вкладке «Шаблоны» изменило
несколько (порядок, закрепление, выбор по умолчанию); <sf-data-view>
сохраняет каждый шаблон с key отдельным намерением view.save.
Общее сохранённое представление записывает код страницы: он отправляет на
<sf-data-view> событие sf-data-view-saved-view-intent с {key, label, revision, roles} (revision и roles необязательны), и представление
отправляет хосту view.save_shared с текущим запросом, колонками и
настройками. Нужна возможность shared-saved-views и порт версии 1.1.0 или
новее; на порте 1.0.0 представление само отвечает unavailable. То же
делает saveView({key, label, level: 'shared'}).
view.dispatchEvent(new CustomEvent('sf-data-view-saved-view-intent', {
detail: {key: 'overdue', label: 'Просроченные'},
}));
Порт хоста
<sf-table> получает данные методами и сообщает о намерениях событиями;
метода setHostPort() у него нет. Адаптер приложения подключается к
<sf-data-view>: setHostPort(host) принимает объект с полями version и
capabilities и методом raise(intent, payload, {signal}), который
отвечает {answer: 'applied' | 'conflict' | 'refused' | 'unavailable', …}.
<sf-data-view> говорит на порте simai.dataview-port версии 1.12.0 и
принимает адаптеры версий от 1.0.0 до 1.12.0; манифест <sf-table> записывает
версию порта 1.0.0. Если адаптер не передан, <sf-data-view> один раз
запрашивает его событием sf-host-port-request.
Возможности хост объявляет в capabilities: представление предлагает
только то, что хост объявил. Создание записи (create-record) и личные
настройки колонок (personal-column-settings) — тоже возможности: хосту на
порте 1.10.0, который их не объявил, представление не показывает кнопку
создания и шестерёнку настроек колонок. Хост на более ранней версии порта
получает обе, не объявляя их. Запись логического поля в ячейке
(inline-edit) и перестановка строк (row-reorder) — возможности порта
1.11.0; их получает только хост, который их объявил. Порт 1.12.0 новых
намерений и возможностей не добавляет: он точнее описывает ответ —
sequence, что несёт конфликт, известные причины — и требует
column_settings только от хоста, который объявил personal-column-settings
или shared-column-settings. Хост без них может не присылать
column_settings, и представление рисует колонки как есть.
У самой таблицы те же части включают и
выключают атрибуты creatable, settings-menu и filter-templates.
Ответ адаптера — конверт, а данные лежат в его поле data; порт описывает
и конверт (answer), и данные ответа на каждое намерение (answer_data).
Адаптер передаёт намерение серверу и возвращает его конверт как есть: сервер
сам решает, ответить applied, conflict или refused. Ошибку HTTP адаптер
превращает в unavailable, а намерение, которого приложение не
поддерживает, сервер отклоняет ответом unavailable с причиной
capability-not-provided:
const host = {
version: '1.12.0',
capabilities: ['row-actions', 'create-record'],
async raise(intent, payload, {signal}) {
const response = await fetch('/api/projects/port', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({intent, payload}),
signal,
});
if (!response.ok) {
return {answer: 'unavailable', reason: `http-${response.status}`, sequence: payload.sequence};
}
return response.json(); // {answer, reason?, message?, sequence?, data?}
},
};
document.querySelector('sf-data-view').setHostPort(host);
Транспорт, сессия и защита от подделки запроса (CSRF) — дело приложения:
порт описывает только намерение, его payload и конверт ответа.
-
answer—applied,conflict,refusedилиunavailable;message— текст для человека. -
reason— машинная причина отказа или недоступности, свободная строка, которую хост может дополнять своими. Если хост не прислалmessage, представление показывает для причин из порта свои слова на языке страницы;messageхоста важнее них:reasonСлова представления capability-not-providedХост не поддерживает это действие. confirm_requiredДействие нужно сначала подтвердить. not-confirmedДействие отменено. port-not-connectedИсточник данных не подключён. no-base-revisionНастройки пока нельзя сохранить: у представления нет их текущей версии. Обновите представление. record_invalidЗапись не найдена. forbiddenНедостаточно прав для этого действия. stale-revisionДанные успели изменить. Обновите представление и повторите действие. Для другой причины представление показывает общий текст по виду ответа, например «Действие отклонено.» у
refused. -
sequenceв ответе наquery.changeиquery.countповторяет номер запроса: ответ на устаревший запрос представление отбрасывает. Это проверяет случай набораquery-answer-names-its-sequence. -
Конфликт несёт рядом с
answer, а не вdata,current_revision— ревизию, против которой нужно было писать. Конфликт записи настроек колонок несёт ещёcolumns— колонки слоя, какими они стали, а конфликт пакетного действия —conflicts, по записи{id, current_revision}на каждую устаревшую запись. Безcurrent_revisionпредставление перечитывает список. -
data— данные порта:columns,records,total,has_next,echo,column_settings(обязательны только у хоста с личными или общими настройками колонок) и другие из его описания. Ответ на запрос (query.change) безdataпредставление считает ошибкой. В каждом ответе на запрос обязательныrecordsиecho.columnsнужны в первом ответе: представление рисует колонки по ним и держит их, пока следующий ответ не пришлёт новые.totalнужен, если хост не объявилcount-on-request; хост с этой возможностью может не считать записи и вместоtotalприсылаетhas_next. Ответ на изменение записи может вернуть вdata.recordsтолько изменённые записи или не вернутьdataвовсе — тогда представление перезапросит список. -
Отказ с причиной
confirm_requiredнесётdata.confirmсdigestиsummary: представление спрашивает человека и повторяет намерение с подтверждением.
Первый запрос query.change уходит сам, как только адаптер передан в
setHostPort(), с reason: 'host-connected'. Его payload —
{sequence, query, reason}, а query от представления всегда содержит пять
полей. Случаи набора проверок присылают только нужные им поля (например,
page и per_page), поэтому хост подставляет недостающим значения по
умолчанию из таблицы:
Поле query |
Что в нём |
|---|---|
search |
строка поиска, по умолчанию пустая |
filters |
объект значений фильтра, по умолчанию {} |
sort |
массив [{key, direction}] или пустой массив без сортировки |
page |
номер страницы с 1 |
per_page |
размер страницы, по умолчанию 25 |
Пагинация предлагает размеры из своего page-sizes, по умолчанию 10, 20,
30 и 40; действующий размер страницы представление добавляет в этот список,
поэтому 25 тоже есть в выборе. Свои размеры задаёт своя пагинация в слоте:
<sf-pagination slot="pagination" page-sizes="25,50,100">; page-sizes на
<sf-table> внутри представления не действует.
Поиск, фильтр и сортировка возвращают page к 1. reason называет
причину запроса: search, filter, sort, page-change,
page-size-change, show-more, retry, after-write (перезапрос после
изменения записи).
Действие над строкой превращается в намерение так:
| Что выбрал человек | Намерение |
|---|---|
действие view у одной записи |
record.open |
| другое действие у одной записи | record.mutate с ревизией записи в revisions — объекте, где ключ — идентификатор записи строкой: {"1": "r1"} |
| действие над выбранными записями — из панели выбора или над несколькими сразу | bulk.apply_selected |
| кнопка создания | view.create_record |
флажок в ячейке (возможность inline-edit) |
record.set |
перетаскивание строк внутри плоского списка (возможность row-reorder) |
record.reorder |
Действие, которого нет в списке actions записи, до хоста не доходит:
представление отвечает себе отказом action-not-allowed. Подписи есть
только у действий view, edit и delete; подпись и значок своего действия
приложение передаёт каталогом data.row_actions: [{id, label, icon}] в
ответе на запрос. Действие без записи в каталоге подписано «Действие», а в
консоли появляется предупреждение с его идентификатором.
Переход после record.open и view.create_record делает само приложение.
Поля фильтра порт не передаёт. Кнопку «Фильтр» таблица показывает, только
когда есть по чему фильтровать: страница передала поля фильтра, есть
сохранённые шаблоны или фильтр уже применён. Поля задаёт страница: она вставляет свою
<sf-table> в слот content представления и вызывает у неё
setFilterFields(fields) (см. Поиск и фильтр); применённый
фильтр представление отправляет хосту в query.filters.
Повторный вызов setHostPort() заменяет адаптер и заново запрашивает
список. Если raise выбросил ошибку или запрос прерван через signal,
представление показывает ошибку; ответ на устаревший запрос отбрасывается.
Отказ refused на query.change означает «нет доступа»: строки прежнего
ответа и строка страниц убираются, вместе с ними прячутся панель
инструментов (поиск, фильтр, «Создать») и выбор строк, а текст message
приходит уведомлением и в блок «Нет доступа» не попадает. Следующий ответ
applied возвращает панель и выбор такими, какими их задал автор; при unavailable
представление показывает свою ошибку и message не читает. Ответ на запрос
называет свой sequence и тогда, когда он refused или unavailable; ответ
без него представление считает ответом на текущий запрос.
У порта карточки записи (<sf-property-view>) свой формат: запись приходит
в самом ответе, без data.
Порт описывает стык отдельно от компонента:
| Часть порта | Что в ней |
|---|---|
| Данные | колонки, записи с ревизией, разрешёнными действиями и полями для записи на месте (editable), общее число, эхо запроса и настройки колонок по слоям |
| Эхо запроса | что человек выбрал, что приложение применило, что отклонило, поиск, сортировка, окно и что потерялось при смене представления |
| Намерения | запрос, действия над записью, запись поля в ячейке, перестановка записей, пакет по выбору и по фильтру, сохранение настроек лично и для всех, шаблоны, создание |
| Возможности | сохранённые представления, общие настройки колонок, пакет по фильтру, действия над записью, создание, личные настройки колонок, запись в ячейке, перестановка строк — приложение объявляет, что даёт |
| Настройки | слои package, shared, personal; запись всегда против ревизии слоя, сброс — запись пустой раскладки {} против ревизии |
| Словарь колонки | видимость, порядок, ширина, минимальная ширина, приоритет при нехватке места, закрепление |
Права приходят вместе с данными: каждая запись несёт список действий, которые приложение разрешает именно для неё, и представление предлагает только их. Текущая версия — порт 1.12.0 и его набор проверок; прежние версии опубликованы рядом. Приложение заявляет поддержку версии порта, когда прошло все проверки её набора. Правила стыка — в стандарте порта хоста.
Запрос и события для кода страницы
changeQuery(patch, reason) сливает patch с текущим запросом и
запрашивает список. В patch — поля query: search, filters, sort,
page, per_page. reason уходит хосту в payload.reason; по умолчанию
это query-change. Только причины page-change, page-size-change и
show-more сохраняют page, при любой другой она становится 1. Причины
search и filter отмечают запрос как действие человека: ошибка в ответ
на него объявляется сразу. Поле поиска на панели показывает строку поиска
запроса, кто бы её ни задал: после changeQuery({search: 'договор'}) в нём
стоит «договор». refresh(reason) повторяет текущий запрос.
| Событие | detail |
|---|---|
sf-data-view-state |
{state, message, instance_id}: state — loading, populated, empty или error; message — текст для экранного диктора: о загрузке, ошибке или уведомлении хоста |
sf-data-view-answer |
ответ хоста на любое намерение: {intent, answer, reason, message, data, sequence, effects, instance_id} — поля, которых не было в ответе, отсутствуют |
sf-table-column-settings-reset |
{target: 'personal', tableSettingsKey}; представление отвечает на него resetColumnSettings() |
Пример: связь с приложением
Пример подключает <sf-data-view> к приложению через порт хоста. Приложение
здесь — несколько строк JavaScript с массивом записей и задержкой ответа; на
своей странице raise() отправляет те же намерения на сервер и возвращает его
ответ в том же конверте. Переключатель над таблицей меняет ответ приложения:
данные, отказ в доступе или сбой сервера. При отказе представление убирает
строки, строку страниц и панель инструментов, показывает блок «Нет
доступа», а текст message приходит уведомлением. При сбое оно показывает ошибку с «Повторить».
Что делает адаптер в примере:
- объявляет версию порта
1.12.0и возможностиrow-actionsиpersonal-column-settings; без объявленной возможности намерение до приложения не доходит, а её часть интерфейса не показывается; - на намерение, которого приложение не поддерживает, отвечает
unavailableс причинойcapability-not-provided, а ответы на запрос, в том числе отказ и сбой, называютsequenceзапроса; - на
query.changeотвечаетappliedсdata: колонки, записи страницы,total, эхо запроса, каталог действийrow_actionsи настройки колонок с личным слоем; - кладёт значения записи прямо под ключи колонок:
{id, revision, actions, name, owner, status, updated_at}; - на
record.mutateвозвращает только изменённые записи — представление перерисовывает ровно эти строки; - на
settings.save_personalсверяетbase_revisionс ревизией личного слоя и при расхождении отвечаетconflict; - даты в ячейках пишет методом
formatFieldValue(field, value)того же объекта (см. Поля по типу свойства); без него в ячейке остаётся исходное значение2026-10-01T09:00:00Z.
Поля записи и эха:
| Поле | Что в нём |
|---|---|
id, revision |
идентификатор записи и её ревизия; ревизия уходит в revisions намерения record.mutate |
actions |
действия, которые приложение разрешает для этой записи |
<ключ колонки> |
значение ячейки; тип задаёт property_type колонки |
echo.search, echo.sort, echo.page, echo.per_page |
что приложение применило из запроса |
echo.filters_chosen, echo.filters, echo.filters_rejected |
выбранный фильтр, применённый фильтр и отклонённые поля |
Перестановка строк и запись в ячейке
Обе возможности описывает порт 1.11.0 — младшая версия, которая только
добавляет: хост, объявивший 1.10.0, продолжает работать, и представление не
предлагает ему ни одну из них. Хост на 1.11.0 получает их, только если
объявил в capabilities.
Запись в ячейке (inline-edit). Запись может назвать в списке
editable ключи полей, которые этот человек вправе менять на месте; без
списка таких полей нет. Представление ставит своей таблице inline-edit, и
логическое поле из editable становится флажком. Нажатие поднимает
намерение record.set:
| Поле | Что в нём |
|---|---|
record_id |
идентификатор записи, строка или целое число |
key |
ключ поля |
value |
новое значение, только true или false |
revision |
последняя увиденная ревизия записи |
confirm |
дайджест подтверждения, если хост его просил |
Хост перечитывает хранимое значение и сравнивает ревизию. Ответ applied
может нести в data.records запись в её нынешнем виде — представление
перерисует только эту строку; без неё список перезапрашивается. Запись,
которая меняет другие записи (например, делает поле обязательным), хост
может сначала отклонить с причиной confirm_required и data.confirm —
представление спросит человека и повторит намерение с confirm. Любой
ответ, кроме applied (conflict, refused, unavailable), оставляет в
ячейке значение, которое запись хранит.
Перестановка строк (row-reorder). Только для плоского списка: в режиме
дерева перенос остаётся за node.move. Представление ставит таблице
row-reorder, сразу показывает новый порядок и поднимает record.reorder:
| Поле | Что в нём |
|---|---|
record_ids |
перетащенные записи в порядке перетаскивания, не меньше одной, без повторов |
before |
запись, перед которой они встают, или null — в конец |
revision |
ревизия первой перемещённой записи |
confirm |
дайджест подтверждения, если хост его просил |
Ответ applied может нести перемещённые записи в data.records, иначе
список перезапрашивается. На любой другой ответ, например conflict с
текущей ревизией, представление возвращает прежний порядок.
Набор проверок порта 1.12.0 — 44 случая; три из них проверяют запись в
ячейке с ответом записью, запись с подтверждением и перестановку против
устаревшей ревизии, а query-answer-names-its-sequence — что ответ на запрос
называет его sequence.
Проверка на сервере
Идентификатор записи, действие, сортировку, фильтр и лимиты нельзя считать доверенными только потому, что их отправил компонент: приложение повторно проверяет их на сервере.
const table = document.querySelector('sf-table');
table.setDataState('loading');
const page = await api.listProjects({sort: table.getSort()});
table.setTableData({columns: page.columns, rows: page.rows});
table.setDataState('auto');
table.addEventListener('sf-table-action-intent', async (event) => {
const {action_id, record_ids} = event.detail;
await api.runAction(action_id, record_ids);
});
Доступность
- Таблица — именованная область (
role="region"): задайтеaria-label, который называет коллекцию. У<sf-data-view>своё обязательное имя. - Все действия только с иконкой и все меню имеют доступные имена. Меню,
фильтры и окна настроек закрываются по
Escapeи возвращают фокус на кнопку, которая их открыла. - Ширину колонки с клавиатуры меняют
ArrowLeftиArrowRightна её кнопке изменения ширины; фокус при этом остаётся на кнопке. - Дерево — таблица с ролью
treegrid: строки сообщают уровень, позицию среди соседей и открыты ли они.ArrowRightна строке открывает закрытый узел,ArrowLeftзакрывает открытый. Кнопка узла называет действие: «Развернуть» или «Свернуть». - Компактный список — список с именем таблицы; кнопки-иконки его панели имеют доступные имена, кнопка фильтра называет и число активных условий.
- Обновление данных не переносит фокус. Состояние загрузки объявляется как статус, ошибка — как срочное сообщение.
- Для узких экранов настраивайте видимость и приоритет колонок, а не уменьшайте текст и интерактивные цели.
Справочник API
Раздел собран из манифеста компонентов выпуска ui-106d5901f8c3 и обновляется вместе с выпуском. Чтобы исправить описание, исправьте манифест.
Каждому атрибуту соответствует свойство элемента с тем же именем в camelCase: search-placeholder — searchPlaceholder. Свойство атрибута template называется templateName.
<sf-data-view>
| Тег | <sf-data-view> |
| Назначение | Представление данных под управлением элемента: панель инструментов, содержимое и пагинация вокруг отдельного порта данных. |
| Контракт | smart.data-view-composite, версия 1.0.0 |
| Поставка | пакет larena (SF_BOOT_CONFIG.packages.larena) |
| Жизненный цикл | экспериментальный: API может меняться между выпусками |
| Готовность | host-required |
| Заготовка до прихода кода | сервер пишет data-sf-skeleton="rows"; пока ждёт данные, рисует свой скелетон (Заготовка до прихода кода) |
Атрибуты
aria-label—string, по умолчаниюПредставление данных.chrome—string, по умолчаниюtoolbar pagination select settings. Какие элементы оформления отображает список, через пробел.toolbarиpagination— собственные части этого элемента;selectиsettings— два служебных столбца таблицы, они указаны здесь, потому что относятся к той же категории: столбец флажков нужен для панели групповых действий, столбец с шестерёнкой — для доступа к настройкам столбцов, а во встроенном списке нет ни того, ни другого. Пустое значение не отображает ничего из этого — для списка внутри блока, у которого уже есть заголовок. Значения задаются явно, а не выводятся из панели инструментов, чтобы потребитель мог запросить во встроенном списке только выделение.statusне входит в список и не отключается: список, который не может сообщить об ошибке загрузки, выглядел бы пустым при сбое.row-cap—integer, по умолчанию0, значения: от 0. Максимальное число строк во встроенном списке; если часть скрыта, под ним отображается действие «показать все». Ноль — без ограничения, как в списке на странице. Отделено отpage-size— постраничного вывода хоста, — потому что ограничение определяет, сколько элемент показывает из уже имеющейся страницы.port-owner—string, по умолчанию пусто. Составной компонент, в который встроен этот список; передаётся с запросом к порту хоста, чтобы хост знал, на какой обмен отвечает. Указывает составной компонент, а не элемент, поэтому списку, встроенному в другое место, не нужно новое значение.port-record—string, по умолчанию пусто. Запись, которую показывает встраивающий составной компонент; передаётся с запросом к порту хоста.port-group—string, по умолчанию пусто. Группа внутри составного компонента, которую представляет этот список; передаётся с запросом к порту хоста.size—string, по умолчанию1, значения:1/2,1. Ступень лестницы размеров для элементов управления представления; её получают таблица, строка страниц и полоса выбора: 1 или плотная 1/2, как в админке.column-search-placeholder—string. Передаётся как column-search-placeholder в таблицу, которую рисует представление; без него действует собственная подпись таблицы на языке страницы.column-search-empty-text—string. Передаётся как column-search-empty-text в таблицу, которую рисует представление; без него действует собственная подпись таблицы на языке страницы.column-move-label—string. Передаётся как column-move-label в таблицу, которую рисует представление; без него действует собственная подпись таблицы на языке страницы.filter-apply-text—string. Передаётся как filter-apply-text в таблицу, которую рисует представление; без него действует собственная подпись таблицы на языке страницы.show-more-text—string. Передаётся как show-more-text в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.selected-label—string. Передаётся как selected-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.total-label—string. Передаётся как total-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.pages-label—string. Передаётся как pages-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.first-label—string. Передаётся как first-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.previous-label—string. Передаётся как previous-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.page-label—string. Передаётся как page-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.next-label—string. Передаётся как next-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.last-label—string. Передаётся как last-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.page-field-label—string. Передаётся как page-field-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.action-choose-label—string. Передаётся как action-choose-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.actions-label—string. Передаётся как actions-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.actions-region-label—string. Передаётся как actions-region-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.clear-selection-label—string. Передаётся как clear-selection-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.page-size-label—string. Передаётся как page-size-label в пагинацию, которую рисует представление; без него действует собственная подпись пагинации на языке страницы.pagination-aria-label—string. Передаётся как aria-label в пагинацию, которую рисует представление (собственный aria-label представления называет само представление).preferences-key—string, по умолчанию пусто. Ключ, под которым хранится персональный вид этого списка (SF.Preferences,sf-table/<key>): раскладка таблицы, сортировка и размер страницы; применяется до первой отрисовки и первого запроса. Обычно это коллекция.accept-files—boolean, по умолчаниюfalse. Файлы, перетащенные с компьютера на представление, загружает хост: пока файлы над ним, видна зона сброса, а sf-data-view-files-drop {files} сообщает, что их отпустили.
Свойства в документе макета
Узел типа smart.data-view-composite в документе макета (simai.composition.document.v1) принимает:
- свойства:
aria-label.
Значения свойств те же, что у одноимённых атрибутов, и того же типа, что в списке атрибутов: строковое значение пишется строкой ("size": "1", а не 1 — число отклоняется с диагностикой schema_enum или schema_type), логическое — true или false.
Методы
applyProjection(data: object)— возвращаетnull. Отображает ответ хоста во всех трёх слотах.applyUnderFilter(actionId: string, confirmedTotal: integer)— возвращаетobject. Выполняет подтверждённое действие над всем фильтром (bulk.apply_under_filter) по текущим фильтрам. Бросает TypeError, если confirmedTotal не равен текущему общему числу.changeQuery(patch: object, reason?: string)— возвращаетobject. Изменяет поиск, фильтры, сортировку или окно.deleteView(key: string)— возвращаетobject. Удаляет сохранённое представление.notify(text: string, options?: object)— возвращаетobject. Показывает одно уведомление поверх карточки или убирает его при пустом тексте. Отказ, конфликт и недоступность хоста выводят его сами.openView(key: string)— возвращаетobject. Открывает сохранённое представление.refresh(reason?: string)— возвращаетobject. Запрашивает текущую выборку с новым номером последовательности.resetColumnSettings()— возвращаетobject. Сбрасывает раскладку колонок человека: пустой личный слой, записанный против его ревизии, — это сброс по порту (запись, а не удаление). Значения по умолчанию даёт хост.saveColumnSettings(columnSettings: object, options?: object)— возвращаетobject. Записывает слой настроек колонок, личный или общий, против его ревизии: settings.save_personal или settings.save_shared. Базовая ревизия — переданная, иначе ревизия слоя, иначе ревизия цели порта. После конфликта представление берёт current_revision (и колонки слоя), которые ответ несёт рядом с answer, как требует simai.dataview-port 1.12.0; data.current_revision и data.column_settings оно тоже читает — для хоста, который следовал словам версии 1.10.0, — а если конфликт не называет ревизию, перечитывает список.saveView(view: object)— возвращаетobject. Сохраняет личное или общее представление: view.save или view.save_shared на порте 1.1.0 и новее. Бросает TypeError без key и label или для другого уровня.setHostPort(host: object)— возвращаетobject. Привязывает конкретный адаптер хоста.updateRecord(id: string|integer, patch?: object)— возвращаетboolean. Записывает одну запись на месте: строка перезаписывается, и у хоста ничего не запрашивается. Запись, на которую получен ответ только с записями, проходит этот путь сама.expandNode(id: string|integer)— возвращаетobject. Открывает узел, у которого по словам хоста есть потомки: из того, что уже есть у представления, или запросом к хосту через tree.expand.collapseNode(id: string|integer)— возвращаетnull. Закрывает открытый узел. Это дело самого представления, хост не спрашивается.loadMoreNodes(parent: string|integer|null, cursor?: string|null)— возвращаетobject. Следующая страница одного уровня: корни — через запрос, потомки узла — через tree.expand с его курсором.openMoveDialog(record: object)— возвращаетobject. Спрашивает хост, куда можно перенести запись (node.targets), даёт выбрать ровно из этих мест и переносит её туда (node.move).
События
Все события приходят на сам элемент и всплывают. Общие события базового элемента перечислены в главе Жизненный цикл → Этапы.
sf-count-request—detail:{ component: object, tagName: string, template: string, reason: string, current: integer, pageSize: integer }. Отправляет пагинация представления, когда человек просит общее число, последнюю страницу или действие над фильтром, пока число неизвестно. Представление отвечает на него намерением query.count; хосту слушать его не нужно.sf-data-view-answer—detail:{ intent: string, answer: string, reason?: string, data?: object, sequence?: integer, effects?: array, instance_id: string }. Приходит после каждого ответа порта, по которому действует представление, включая ответы, которые оно даёт себе само (порт не подключён, возможность не объявлена, действие не разрешено), — чтобы страница видела результаты, не оборачивая порт.sf-data-view-saved-view-intent—detail:{ key: string, label: string, revision?: string|integer, roles?: object }. Событие, которое страница отправляет на представление, чтобы сохранить общее сохранённое представление (порт 1.1.0 и новее): представление отправляет view.save_shared с key и label из detail.sf-data-view-state—detail:{ state: string, message: string, instance_id: string }. Приходит при каждой смене состояния данных представления (loading, populated, empty, error) вместе со строкой, которую оно объявляет в своей живой области; для страницы, которая показывает состояние где-то ещё.sf-table-action-intent—detail:{ action_id: string, record_ids: array }. Событие таблицы, которое слушает представление: в его таблице выбрано действие над строкой или групповое действие. Представление проверяет, что строка его разрешает, и отправляет record.open (view), record.mutate (одна строка) или bulk.apply_selected с ревизиями записей; перенос и создание в дереве открывают окно переноса или view.create_record.sf-table-column-settings-change—detail:{ reason: string, columnSettings: object, tableSettingsKey: string, target?: string, base_revision?: string|number }. Событие таблицы, которое слушает представление: человек изменил ширину колонки, переставил, закрепил или скрыл её. Представление записывает это в личный слой человека (settings.save_personal): выбора слоя таблица не предлагает, и таблица, которую оно рисует, не передаёт target. Общий слой записывает код страницы через saveColumnSettings(settings, {shared: true}).sf-table-column-settings-reset—detail:{ target: string, tableSettingsKey: string|null }. Событие таблицы, которое слушает представление: человек попросил вернуть колонки по умолчанию. Представление вызывает resetColumnSettings — пустой личный слой, записанный против его ревизии; значения по умолчанию даёт хост.sf-table-create-intent—detail:{ item: string|null }. Событие таблицы, которое слушает представление: нажата кнопка создания или пункт её меню. Представление отправляет view.create_record с detail в том виде, в каком он пришёл.sf-table-query-intent—detail:{ reason: any, context: object, sequence: integer }. Событие таблицы, которое слушает представление: изменился входной порт контекста его таблицы. Представление не берёт номер последовательности таблицы и перечитывает список своим query.change.sf-table-template-delete—detail:{ key: string }. Событие таблицы, которое слушает представление: на вкладке «Шаблоны» удалён сохранённый шаблон, по одному событию на шаблон. Представление отправляет view.delete с ключом.sf-table-template-save—detailне описан. Событие таблицы, которое слушает представление: сохранён шаблон фильтра. Представление превращает его в saveView (view.save) с key, label, revision, level, roles, layout и settings; массив в detail сохраняется по одному шаблону, каждый своим view.save, записи без key пропускаются.sf-table-template-select—detail:{ key: string }. Событие таблицы, которое слушает представление: на вкладке «Шаблоны» применён другой сохранённый шаблон. Представление отправляет view.open с ключом и восстанавливает поля фильтра, с которыми представление было сохранено.sf-host-port-request—detail:{ component?: object, context?: object }. Запрашивает у страницы порт хоста — один раз за подключение и только пока у элемента его нет. Общее соглашение для всех компонентов, принимающихsetHostPort: потребителю, встраивающему такой компонент в другой, не нужно искать элемент и вызывать у него метод. Ответ — вызовsetHostPortна цели события, сразу или позже.sf-data-view-list-open—detail:{ reason: any, query: object }. Пользователь запросил весь список, часть которого показана из-за ограничения числа строк. Передаёт запрос, чтобы хост открыл полное представление с теми же записями; компонент не строит собственный адрес.sf-table-tree-intent—detail:{ intent: string, payload: object }. Намерение дерева порта simai.dataview-port 1.8.0 — tree.expand, node.targets или node.move, — которое представление объявляет, когда его поднимает. Хост отвечает через порт.sf-data-view-files-drop—detail:{ files: array }. На представление с accept-files отпустили файлы с компьютера. Представление ничего не загружает, и файлы не идут через порт; их загружает хост.sf-table-cell-set—detail:{ id: string|integer, key: string, value: any, revision: string|integer|null }. Событие таблицы, которое слушает представление: нажат флажок в ячейке. Представление отправляет record.set порта simai.dataview-port 1.11.0 (возможность inline-edit) с record_id, key, value и revision и возвращает флажок, если ответ не applied.sf-table-rows-reorder—detail:{ ids: array, before: string|null, order: array }. Событие таблицы, которое слушает представление: строки перетащили в другой порядок. Представление сразу показывает новый порядок и отправляет record.reorder порта simai.dataview-port 1.11.0 (возможность row-reorder); отказ или конфликт возвращает прежний порядок.
Доступность
- Роль: область (
region). - Доступное имя: обязательно.
- Клавиши: Нативные элементы управления и дочерние Smart-компоненты.
- Фокус: Фокус не перемещается при обновлении данных
Минимальная разметка
<sf-data-view aria-label="Записи"><sf-table slot="content" aria-label="Записи"></sf-table><sf-pagination slot="pagination"></sf-pagination></sf-data-view>
Источник
simai/larena-ui@5f33ba576aebde377d5195ec71836eb4430aa84d:components/src/smart/data-viewsimai/larena-ui@5f33ba576aebde377d5195ec71836eb4430aa84d:components/src/smart/data-view/rule.js#name=cl-data-view
<sf-table>
| Тег | <sf-table> |
| Назначение | Полное представление коллекции с серверными данными: поиск, фильтры, настройки таблицы, действия над строками и массовые действия, пагинация. |
| Контракт | smart.data-view, версия 1.9.0 |
| Поставка | пакет larena (SF_BOOT_CONFIG.packages.larena) |
| Жизненный цикл | экспериментальный: API может меняться между выпусками |
| Готовность | host-required |
| Заготовка до прихода кода | сервер пишет data-sf-skeleton="rows"; пока ждёт данные, рисует свой скелетон (Заготовка до прихода кода) |
Атрибуты
template—string, по умолчаниюdefault, значения:default.aria-label—string, по умолчаниюData table, значения: не короче 1. Доступное имя таблицы. Читайте его из контекста props, а не из элемента:aria-labelотражается платформой, поэтому element.ariaLabel возвращает null без заданного атрибута и перекрывает значение, которое вычисляет компонент.selectable—boolean, по умолчаниюtrue.settings—boolean, по умолчаниюtrue.actions—boolean, по умолчаниюtrue.row-actions—boolean, по умолчаниюtrue.creatable—boolean, по умолчаниюtrue. Кнопка создания и её меню видов записи среди действий. Представление данных выключает её для хоста на порте 1.10.0, который не объявил create-record.filter-templates—boolean, по умолчаниюtrue. Кнопка, которая сохраняет фильтр как шаблон. Представление данных выключает её, если хост не хранит сохранённые представления.settings-menu—boolean, по умолчаниюtrue. Шестерёнка в заголовке колонки настроек, которая открывает настройки колонок. Без неё колонка по-прежнему несёт меню каждой строки. Представление данных выключает её для хоста на порте 1.10.0, который не объявил personal-column-settings.pagin—boolean, по умолчаниюfalse.filter-text—string, по умолчаниюFilter.clear-text—string, по умолчаниюClear.search-placeholder—string, по умолчаниюSearch.create-text—string, по умолчаниюCreate.pagination-total—integer, по умолчанию10, значения: от 0.pagination-page-size—integer, по умолчанию10, значения: от 1; до 100.page-sizes—string, по умолчанию10,20,30,40.context-menu-columns—integer, по умолчанию1, значения: от 1; до 4. На сколько столбцов меню настроек делит свои пункты. Один, пока хост не запросит больше: на протяжении всего существования этого входа значение читалось напрямую, поэтому ни объявленное значение по умолчанию, ни значение, переданное хостом, до меню не доходили.remember-widths—boolean, по умолчаниюfalse. Запоминать ширины колонок как персональный вид (SF.Preferences, под ключом preferences-key или table-settings-key) для таблицы, настройки которой хост не хранит; двойной щелчок по краю колонки забывает её ширину.table-settings-key—string, по умолчанию пусто, значения: не короче 1. Какие сохранённые настройки использует таблица. Пустое значение означает id элемента, а при его отсутствии — id экземпляра, поэтому каждая таблица на странице хранит свои настройки. Реальное значение по умолчанию заставило бы все таблицы использовать один ключ и перезаписывать друг друга.data-state—string, по умолчаниюauto, значения:auto,loading,populated,empty,error.loading-text—string, по умолчаниюLoading.empty-text—string, по умолчаниюNo data.error-text—string, по умолчаниюThe data could not be loaded.retry-text—string, по умолчаниюTry again.toolbar—boolean, по умолчаниюtrue. Показывает встроенную панель инструментов; составной компонент может передать свою панель через слот.pin-column-label—string, по умолчаниюPin the column. Доступное имя и подсказка элемента управления, закрепляющего столбец.unpin-column-label—string, по умолчаниюUnpin the column. Доступное имя и подсказка элемента управления, открепляющего столбец.column-move-label—string, по умолчаниюMove the column. Доступное имя маркера, перемещающего столбец в списке настроек; к нему добавляется подпись столбца.column-search-placeholder—string, по умолчаниюSearch the fields. Placeholder и доступное имя поля поиска над списком настроек столбцов.column-search-empty-text—string, по умолчаниюNo field matches. Выводится вместо списка настроек столбцов, когда поиск не нашёл ни одного поля.vertical-scroll—string, по умолчаниюself, значения:self,page. Прокручивается ли таблица внутри себя или прокручивается страница. Объявлено как строка с двумя значениями.filter-apply-text—string, по умолчаниюApply. Подпись кнопки подтверждения в окне фильтра; пока есть черновик, к ней добавляется число условий, которые изменит нажатие.layout—string, по умолчаниюtable, значения:auto,table,list. Как рисуются записи: таблицей, компактным списком или auto — таблицей, пока контейнер таблицы не уже list-below пикселей, и списком, когда уже. sf-data-view выбирает auto для списка на странице.list-below—number, по умолчанию600. Ширина контейнера в пикселях, уже которой режим auto рисует компактный список.list-fields—number, по умолчанию3. Сколько полей запись показывает под заголовком в компактном списке; поля выбираются по приоритету колонок.size—string, по умолчанию1, значения:1/2,1. Ступень лестницы размеров для элементов управления и строк таблицы: 1 или плотная 1/2 — элементы управления высотой 32 px, текст 14/20, строки высотой ступени. Размер 1, записанный на элементе, который рисует таблица, становится размером таблицы.preferences-key—string, по умолчанию пусто. Ключ, под которым раскладка таблицы — порядок, видимость, закрепление, ширины — хранится как персональный вид (SF.Preferences,sf-table/<key>); представление данных передаёт свой. Без него используется table-settings-key, а без обоих ничего не хранится.row-actions-visible—string, по умолчаниюhover, значения:hover,always. Где видны кнопки действий строки: в строке под указателем, в фокусе или текущей (hover, по умолчанию) или во всех строках (always).drag-rows—boolean, по умолчаниюfalse. Строки можно перетащить из списка как записи (application/x-sf-records+json): запись строки или все выбранные записи, если строка среди них.row-reorder—boolean, по умолчаниюfalse. Строки можно переставить, перетаскивая их внутри списка; отпускание отправляет sf-table-rows-reorder {ids, before, order}. Представление данных ставит атрибут, когда хост предлагает возможность row-reorder.inline-edit—boolean, по умолчаниюfalse. Логическая ячейка, поле которой запись называет вeditable, становится флажком; нажатие отправляет sf-table-cell-set {id, key, value, revision}, и ячейка ждёт ответа хоста. Представление данных ставит атрибут, когда хост предлагает возможность inline-edit.
Свойства в документе макета
Узел типа smart.data-view в документе макета (simai.composition.document.v1) принимает:
- свойства:
aria-label,table-settings-key,selectable,toolbar,settings,actions,row-actions,pagin,pagination-total,pagination-page-size,page-sizes,filter-text,clear-text,search-placeholder,create-text,loading-text,empty-text,error-text,retry-text,context-menu-columns,data-state; - данные:
columns,rows.
Значения свойств те же, что у одноимённых атрибутов, и того же типа, что в списке атрибутов: строковое значение пишется строкой ("size": "1", а не 1 — число отклоняется с диагностикой schema_enum или schema_type), логическое — true или false.
Методы
addRows(rows: array)— возвращаетobject. Добавляет ещё одну страницу с бэкенда в режиме «показать ещё».applyQueryResult(sequence: integer, rows: array)— возвращаетboolean. Применяет строки, отвечающие на последний ожидающий запрос контекста. Для устаревшей, повторной или неизвестной последовательности возвращаетfalseи ничего не меняет.clearSelection()— возвращаетobject. Сбрасывает явное выделение этого экземпляра; при изменении один раз отправляет порт выделения.getActiveFilterFieldKeys()— возвращаетarray. Какие поля фильтра отображаются, в порядке полей хоста.getColumnSettings()— возвращаетobject. Читает текущее изменение настроек столбцов.getFilterPayload()— возвращаетobject. Читает текущий черновик фильтра для проверки запроса на стороне хоста.getInstanceId()— возвращаетstring. Идентификатор экземпляра:idавтора, если он есть, иначе собственный счётчик. От него выводятся id, запасной ключ настроек и портал.getQueryContext()— возвращаетobject|null. Читает неизменяемый контекст, последним полученный через входной порт контекста, илиnull.getSelectedRecordIds()— возвращаетarrayизstring|integer. Читает типизированные идентификаторы записей, выбранных в этом результате таблицы, без разбора DOM.getSort()— возвращаетobject. Читает сортировку, отображаемую в заголовках.getToolbarActions()— возвращаетarray. Действия панели инструментов в объявленном виде, копия.requestActionIntent(actionId: string, recordIds: array)— возвращаетobject. Отправляет типизированный запрос действия над строкой или пакетного действия; выполнение и авторизация на стороне хоста. При недопустимых аргументах до отправки выбрасываетсяTypeError.requestContextQuery(sequence: integer)— возвращаетobject. Внутренний метод порта контекста: отправляетsf-table-query-intentдля текущего контекста с новой ожидающей последовательностью.requestCreateIntent(itemId: string|null)— возвращаетobject. Отправляетsf-table-create-intentдля основной кнопки (null) или объявленного id пункта создания.requestRetry()— возвращаетobject. Отправляет намерение повтора после неудачного запроса данных.setActiveFilterFields(keys: array)— возвращаетobject. Показывает в выбранном шаблоне фильтра ровно эти поля фильтра. Используется при открытии сохранённого представления: settings.filter_fields перечисляет поля, которые в нём были показаны.setBulkActions(actions: array)— возвращаетobject. Регистрирует подписанные непрозрачные действия для текущего выделения. При пустом выделении их элементы управления отключены.setColumns(columns: array)— возвращаетobject. Заменяет канонические столбцы, полученные от бэкенда.setCreateItems(items: array)— возвращаетobject. Задаёт пункты меню кнопки создания с разделённым действием (только {id, name}). Без пунктов у кнопки создания нет стрелки.setDataState(state: string)— возвращаетobject. Переключает состояние отображения: автоматическое, загрузка, заполнено, пусто и ошибка.setFilterFields(fields: array)— возвращаетobject. Регистрирует поля и элементы управления фильтра из разрешённого бэкендом списка.setRows(rows: array)— возвращаетobject. Заменяет текущие строки результата бэкенда.setSort(key: string|null, direction: any)— возвращаетobject. Показывает в заголовках текущую сортировку хоста без отправки события; (null, null) сбрасывает её.setTableData(patch: object)— возвращаетobject. Обновляет столбцы, строки или связанное состояние таблицы, переданные хостом.setTableSettings(settings: object)— возвращаетobject. Применяет изменение настроек столбцов без запроса на сохранение.setTemplates(templates: array)— возвращаетobject. Применяет сохранённые шаблоны фильтров и представлений.setToolbarActions(items: array)— возвращаетobject. Объявляет собственные действия представления для меню панели инструментов:{id, label, icon?, href?}.hrefдолжен указывать на этот же сайт. Без действий элемент управления не отрисовывается.sfPortInput(port: any, value: array, meta: object)— возвращаетnull. Входной порт композиции, используемыйsf-composition-scope. Принимает только объявленный порт контекста сrecord-ids.v1; при недопустимых портах или значениях до изменения состояния выбрасываетсяTypeError.isTree()— возвращаетboolean. Являются ли строки деревом: у какой-то строки есть __tree, которое представление данных пишет по уровням хоста.treeColumnKey(columns?: array)— возвращаетstring|null. Колонка, от которой растёт дерево: колонка заголовка или первая колонка данных.toggleTreeRow(row: object)— возвращаетobject. Сообщает, что строку дерева открыли или закрыли (sf-table-tree-toggle).requestTreeMore(row: object)— возвращаетobject. Запрашивает следующую страницу уровня дерева (sf-table-tree-more).
События
Все события приходят на сам элемент и всплывают. Общие события базового элемента перечислены в главе Жизненный цикл → Этапы.
sf-port-output—detail:{ port: any, value: array }. Выход порта композиции. Таблица отправляет портselection(record-ids.v1) один раз при каждом изменении явного выделения, включая сброс; маршрутизирует его общийsf-composition-scope.sf-table-action-intent—detail:{ action_id: string, record_ids: array }. Запрашивает одну операцию над строкой или групповую операцию, которую выполняет хост. Источник события — экземпляр таблицы-владельца; отправка события не означает успех операции.sf-table-column-settings-change—detail:{ reason: string, tableSettingsKey: string, columnSettings: object }. Запрашивает сохранение изменений настроек столбцов после изменения ширины, порядка или видимости.sf-table-column-settings-reset—detail:{ target: string, tableSettingsKey: string|null }. Человек попросил вернуть колонки по умолчанию. Таблица убирает свою сохранённую раскладку и значений по умолчанию не называет: личный слой сбрасывает хост (или представление данных) и отвечает своими значениями.sf-table-create-intent—detail:{ item: string|null }. Пользователь нажал кнопку создания (item равен null) или выбрал пункт из её меню (id пункта). Только намерение: хост открывает свою форму создания.sf-table-filter-change—detail:{ templateKey: string, templateLabel: string, values: object, tags: object }. Запрашивает у хоста выборку после изменения активного фильтра: «Применить» в окне фильтра, удалённая метка или очищенный фильтр. Представление данных превращает его в query.change с filters из values.sf-table-query-intent—detail:{ reason: any, context: object, sequence: integer }. Запрашивает у хоста выборку после изменения входного порта контекста. Хост разрешает связь и права доступа и отвечает вызовом applyQueryResult(sequence, rows); более старые номера последовательности игнорируются.sf-table-retry—detail:null. Запрашивает у хоста повторную загрузку после состояния ошибки.sf-table-search-change—detail:string. Запрашивает у хоста выборку после изменения значения поиска (с задержкой debounce).sf-table-sort-change—detail:{ key: string, direction: any }. Пользователь активировал сортируемый заголовок. Направление циклически меняется: asc, desc, null (без сортировки). Только намерение: таблица отмечает заголовок, но не переупорядочивает строки; переупорядочивает или перезагружает их хост.sf-table-template-delete—detail:{ key: string }. Пользователь удалил сохранённые шаблоны на вкладке «Шаблоны» и нажал «Применить». Только намерение: хост удаляет сохранённое представление. Одно событие на каждый удалённый шаблон.sf-table-template-save—detail:object|array. Запрашивает сохранение одного или нескольких шаблонов фильтра (порядок, закрепление, удаление, флаги по умолчанию и выбора или данные). О загрузке сохранённого представления сообщает событие sf-table-template-select.sf-table-template-select—detail:{ key: string }. Пользователь применил другой сохранённый шаблон на вкладке «Шаблоны». Только намерение: хост загружает это сохранённое представление.sf-table-toolbar-action—detail:{ action: string, href?: string|null }. Пользователь выбрал одно из собственных действий представления в меню панели инструментов. Только намерение, событие можно отменить: компонент переходит по href пункта на том же сайте, если хост не перехватит переход через preventDefault.sf-table-tree-toggle—detail:{ id?: string|integer, expanded?: boolean }. Пользователь открыл или закрыл строку дерева. Отвечает представление данных; таблица сообщает только, какая строка и была ли она открыта.sf-table-tree-more—detail:{ parent?: string|integer|null, cursor?: string|null }. Пользователь запросил следующую страницу уровня дерева.sf-table-rows-drag—detail:{ ids: array, source: string|null }. Строки с drag-rows начали перетаскивать из списка к цели вроде дерева; те же {ids, source} лежат в данных перетаскивания как application/x-sf-records+json.sf-table-rows-reorder—detail:{ ids?: array, before?: any, order?: array }. Строки перетащили в другой порядок внутри списка: перемещённые идентификаторы, строка, перед которой они теперь стоят (null — в конце), и весь показанный порядок.sf-table-cell-set—detail:{ id?: any, key?: string, value?: any, revision?: any }. Нажат флажок в ячейке: запись, поле, новое значение и ревизия записи.
Персональный вид
Ключ sf-table/<preferences-key>, общий для всех элементов этого вида — sf-table/*; версия формата — v: 1. В столбце «Для первой отрисовки» «да» значит, что сервер может положить поле в блок data-sf-preferences страницы.
| Поле | Тип | Пишет | Для первой отрисовки | Что в нём |
|---|---|---|---|---|
columnSettings |
object |
sf-table |
да | Раскладка колонок человека по ключу колонки: visible (логическое), width (длина CSS), order (число), pinned (start или end). Накладывается на настройки колонок хоста, поэтому колонка, которой в ней нет, остаётся как у хоста. |
widths |
object |
sf-table |
да | Ширины колонок, которые человек задал перетаскиванием, по ключу колонки, в виде "<N>px". Хранятся и без preferences-key, если у таблицы есть remember-widths, — тогда под собственным ключом таблицы. |
view |
object |
sf-table |
да | Вид таблицы: density (compact, normal, spacious), stripes, lines, singleLine, stickyHead. Под sf-table/* это вид всех таблиц человека; он читается под видом этой таблицы. |
sort |
array |
sf-data-view |
да | Сортировка, которую выбрал человек: [{key, direction}]. Пишет и читает представление данных, в котором стоит таблица, поэтому первый запрос уже приходит в этом порядке. |
pageSize |
integer |
sf-data-view |
да | Размер страницы, который выбрал человек. Пишет и читает представление данных, поэтому первый запрос уже просит этот размер. |
Доступность
- Роль: область (
region). - Доступное имя: обязательно.
- Клавиши:
Tab,Shift+Tab,Enter,Space,Escape, стрелки,ArrowLeftиArrowRightменяют ширину колонки в фокусе, если её можно менять. - Фокус: У каждого действия только с иконкой и каждой контекстной поверхности есть доступное имя. Оверлеи экземпляра остаются в пределах визуального окна просмотра, закрываются по Escape и возвращают фокус на свой триггер. При изменении ширины столбца фокус остаётся на его нативной кнопке.
Минимальная разметка
<sf-table aria-label="Записи" table-settings-key="package.records"></sf-table>
Источник
simai/larena-ui@5f33ba576aebde377d5195ec71836eb4430aa84d:components/src/smart/tablesimai/larena-ui@5f33ba576aebde377d5195ec71836eb4430aa84d:components/src/smart/table/rule.js#name=cl-table