UNPKG

@smkit/ui

Version:

UI Kit of SberMarketing

442 lines (362 loc) 14.3 kB
# 📋 Table Универсальный компонент таблицы, используется в списках сущностей и дашбордах. Содержит следующую иерархию компонент - Table - Exports - CSV - Excel - Root - Head - Row - Cell ## Компонент Table Обертка всей таблицы, может применятся как есть, без слотов. Создает контекст, инициализирует контроллер и отвечает за обработку данных ### Props: - **data** - входные данные в формате строк списка. Если columns не задан, вычисляет из keys ```typescript type RowData = (Record<Head['name'], Cell> & { class?: string })[]; // Пример const data: RowData = [ { col1: 'cell1', col2: 'cell2' }, { col1: 'cell3', col2: 'cell4' } ]; ``` - **columns** - (опционально) отвечает за свойства колонок и их отображение ```typescript export enum Types { Number = 'number', String = 'string', Date = 'date', Boolean = 'boolean', List = 'list' } export interface Head { title?: string; // Отображаемый заголовок name: string; // Ключ колонки, связывает значения в строках width?: number | string; // Ширина колонки type?: Types | string; // Тип 'number' | 'string' | 'date' | 'boolean' | 'list' unit?: string; class?: string; // Класс для стилей колонки skipRender?: boolean; sortable?: boolean; // Можно ли отсортировать колонку resizable?: boolean; // Можно ли менять размер pinnable?: boolean; // Можно ли закрепить колонку pinned?: boolean; // Закреплена ли колонка offsetLeft?: number; // Смещение закрепленной колонки (техническое поле, перерасчитывается само) element?: HTMLElement; // Элемент ресайза (техническое поле) index?: number; // Индекс позиции колонки при инициализации (нужно для возврата на место при изменении pinned) cast?: (cell: Cell) => string; // Кастомный каст колонки в строку onSort?: SortCallback; // Callback сортировки для данной колонки } // Пример const columns: Head[] = [ { name: 'col1', title: 'Первая колонка' }, { name: 'col2', title: 'Вторая колонка' } ]; ``` - **config** - (опционально) отвечает за конфигурацию таблицы и свойств по умолчанию ```typescript export type Config = { virtualizer?: { overscan?: number; // scrollElement: HTMLDivElement rowHeight: number; // Размер строк в таблице. Нужно как для отображения так и расчета виртуализации }; enumerate?: boolean; head?: { // Глобальный конфиг колонок по-умолчанию precision?: Record<string, number>; pinnable?: boolean; resizable?: boolean; sortable?: boolean; }; // Некоторые фичи могут еще не работать onSort?: SortCallback; onPagination?: undefined; onFilter?: undefined; replaceNull?: string; }; ``` - controller - (опционально) используется, если мы хотим создать контроллер самостоятельно выше по уровню иерархии компонент Свойства протекают вглубь компонентов и имеют приоритет по специфичности. То есть глобальная конфигурация может быть перезаписана частным случаем <p align='center'> default &lt; config.head &lt; columns: Head[] &lt;p Head props </p> `Head.svelte` ```typescript export let head: Head; export let sortable = head?.sortable ?? $config?.head?.sortable ?? true; export let resizable = head?.sortable ?? $config?.head?.resizable ?? true; export let pinnable = head?.pinnable ?? $config?.head?.pinnable ?? true; ``` ### Slots Table имеет два слота, с отображением по-умолчанию - _default_ - Для компонента Root - _top_ - Для Экспорта данных и других кнопок управления над таблицей ```html <script> import { Table, Exports, Root } from '@smkit/ui/table'; import { data } from './data'; </script> <table data="{data.body}" columns="{data.header}"> <Exports slot="top" /> <Root /> </table> ``` Пробрасывает наружу пропсы: - controller - экземпляр класса контроллера - header - обработанные заголовки для отображения - body - обработанное тело таблицы в виде массива объектов - config - обработанный конфиг ```html <table let:controller let:header let:body let:config> <Root /> </table> ``` ## Компонент Root Отвечает за структуру таблицы и ее виртуализацию ### Slots - _header_ - слот заголовка таблицы, используемый через svelte:fragment ```html ... <svelte:fragment slot="header"> {#each header as head} <head {head}> {head.title} {head.width} </head> {/each} </svelte:fragment> ``` - _row_ - слот для подстановки кастомной строки Row и кастомных клеток. Пробрасывает наружу пропсы: - row - объект строки - vRow - описание строки от [виртуализации](https://tanstack.com/virtual/latest/docs/api/virtual-item) - vIndex - виртуальный индекс ```html ... <Row slot="row" let:row let:vRow let:vIndex> {#each header as head} <Cell {head} /> {/each} </Row> ``` ## Компонент Row Компонент строки, получает информацию о текущем индексе, позиции и данные из контекста. Используется в цикле рендера строк ### Props - row - (опционально) достается из контекста строки - vRow - (опционально) достается из контекста строки - vIndex - (опционально) достается из контекста строки - style - (опционально) стили всей строки - class - (опционально) классы стилей всей строки ### Slots - default - отображение кастомных клеток, через цикл. Порядок клеток регулируется _header_ из **Table** ```html <table {columns} {data} {controller} let:header> <Root> <svelte:fragment slot="header"> {#each header as head} <head {head} class="p-1 px-2" /> {/each} </svelte:fragment> <Row slot="row"> {#each header as head} <Cell {head} /> {/each} </Row> </Root> </table> ``` ## Компонент Cell Компонент клетки, получает информацию о текущем head из пропсов и о текущем индексе из контекста строки. Имеет два режима, при закрепленном столбце и при обычном ### Props - value - (на выбор: value/head) - значение клетки напрямую - head - (на выбор: value/head) - заголовок колонки клетки, значение вычислится само - class - (опционально) классы стилей клетки ### Slots - default - Отображение клетки `Platina360 - team/[teamId]/settings/roles/+page.svelte` ```html ... <Row slot="row" class="hover:bg-[#7997FF33]" > <Cell head={{ name: '', width: '5%' }} class="opacity-0 group-hover:opacity-100"> <Checkbox checked={row.isChecked} class="opacity-0 group-hover:opacity-100" /> </Cell> <Cell head={header[0]}> <Account name={row.user.name} email={row.user.email} avatar={row.user.avatar} /> </Cell> <Cell head={header[1]}> <span class="text-base-content pr-2"> {row.roles} </span> <Popover class="opacity-0 group-hover:opacity-100"> <Trigger slot="trigger" class="btn-ghost hover:bg-inherit opacity-0 group-hover:opacity-100 border border-neutral/20 max-h-6 w-6 relative" ><span class="hidden"></span></Trigger > <button class="btn btn-sm bg-base-100 border-none w-full"> Личные </button> <button class="btn btn-sm bg-base-100 border-none"> Командные </button> </Popover> </Cell> <Cell head={header[2]}> <License license={row.licenses} /> </Cell> <Cell head={header[3]}> {row.status} </Cell> </Row> ``` ## Компонент Head Аналогично компоненту клетки, имеет два варианта с закреплением и без. Имеет пропсы, обозначенные в структуре Head выше, с наследованием и приоритетами по специфике ### Props - head: Head - объект описания заголовка - sortable - можно ли сортировать по колонке - resizable - можно ли менять размер колонки - pinnable - можно ли закреплять колонку ### Slots - default - отображение клетки заголовка `Platina - reports/components/table/Table.svelte` ```html ... <svelte:fragment slot="header"> {#each header as head} <head {head} on:sort="{onSort}"> <div class="flex flex-col items-start"> {head.title} <div class="flex gap-1"> {#if head.type === 'number'} <button title="Фильтр по метрике" class="flex h-5 w-5 items-center justify-center transition-colors" on:click|stopPropagation|preventDefault="{()" ="" > openFilter(head.name)} > <FunnelIcon bgColor="{globalFilters.includes(head.name)" ? Colors.selectedBg : Colors.normalBg} /> </button> <button title="Отобразить метрику на графике" class="flex h-5 w-5 items-center justify-center transition-colors" on:click|stopPropagation|preventDefault="{()" ="" > { $chartForm.metric = head.name }} > <PieIcon bgColor="{$chartForm.metric" ="" ="" ="head.name" ? Colors.selectedBg : Colors.normalBg} /> </button> {#if columns[head.name].format !== 'percent' && columns[head.name].format !== 'date'} <button title="Пересчитать в проценты" class="flex h-5 w-5 items-center justify-center transition-colors" on:click|stopPropagation|preventDefault="{()" ="" > togglePercentView(head.name)} > <PercentsIcon bgColor="{$viewForm.columns?.[head.name]?.isPercent" ? Colors.selectedBg : Colors.normalBg} /> </button> {/if} {#if $viewForm.columns?.[head.name]?.isRounded !== undefined && !$viewForm.columns[head.name]?.isPercent} <button title="Не округлять бюджеты до целого" class="flex h-5 w-5 items-center justify-center transition-colors" on:click|stopPropagation|preventDefault="{()" ="" > toggleRoundView(head.name)} > <RoundedIcon bgColor="{$viewForm.columns?.[head.name]?.isRounded" ? Colors.normalBg : Colors.selectedBg} /> </button> {/if} {:else if head.type === 'string'} <button title="Отобразить группу цветом на графике" class="flex h-5 w-5 items-center justify-center transition-colors" on:click|stopPropagation|preventDefault="{()" ="" > { $chartForm.group = head.name }} > <PieIcon bgColor="{$chartForm.group" ="" ="" ="head.name" ? Colors.selectedBg : Colors.normalBg} /> </button> {/if} </div> {#key showFilterDropdown[head.name]} <DropdownCard bind:show="{showFilterDropdown[head.name]}" x="-40px" y="0px"> <div class="flex w-[300px] flex-col gap-4 px-4 pb-3 pt-4 shadow-lg"> <div class="flex max-h-10 items-center gap-4" on:click|stopPropagation|preventDefault> <select selected="{operator}" options="{operators}" label="Фильтр" /> <InputFL bind:value="{filterQuery}" label="Значение" /> </div> <button class="btn btn-primary btn-sm mt-2 w-full py-2" disabled="{!filterQuery" || Number.isNaN(Number(filterQuery))} on:click="{saveFilter}" > Сохранить </button> </div> </DropdownCard> {/key} </div> </head> {/each} </svelte:fragment> ``` ## Компонент Exports Кнопка экспорта данных, содержащая Excel и CSV по-умолчанию. Должна находиться в контексте таблицы. Можно использовать вне таблицы, если использована ручная инициализация контроллера ```html <script> import { Table, Exports } from '@smkit/ui/table'; import { data } from './data'; </script> <table data="{data.body}" columns="{data.header}"> <Exports slot="top" /> </table> ``` ```html <script> import { Table, Exports, Controller } from '$lib/table'; import { columns, data } from './tableConfig'; const controller = new Controller(); </script> <Exports /> <div class="h-[600px]"> <table {columns} {data} {controller} /> </div> ```