/
Starolat
/
DeepDive
Обзор
Документация
Войти
/
Starolat
/
DeepDive
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
js/services/DataGridDataSource.js
280 строк
7 KB
Starolat Sergei
fix: сворачивание до уровня 0 и градиентная стилизация уровней в DataGrid
07 июл 2026, 22:50
07 июл 2026, 22:50
2bb0207
Код
Авторство
О чём код?
// @ts-check /** * @fileoverview DataGridDataSource - Базовый абстрактный источник данных для DataGrid * @module DataGridDataSource * @version 0.1.0 * * Абстрагирует загрузку, фильтрацию, группировку и управление деревом данных * для компонента <data-grid>. Конкретные DataSource наследуют этот класс * и реализуют доменную логику. */ /** * @typedef {import('../types.js').TableColumn} TableColumn * @typedef {import('../types.js').TableRow} TableRow * @typedef {import('../types.js').AutoFilter} AutoFilter */ /** * Конфигурация элемента toolbar. * @typedef {Object} ToolbarItem * @property {string} id * @property {'button'|'toggle-group'|'level-buttons'|'separator'} type * @property {string} [label] * @property {string} [title] * @property {boolean} [disabled] * @property {Array<{id: string, label: string, active?: boolean}>} [options] * @property {number} [maxLevel] * @property {function(): void} [onClick] * @property {function(optionId: string): void} [onToggle] * @property {function(level: number): void} [onLevelClick] */ /** * Деталь события data-changed. * @typedef {Object} DataChangedDetail * @property {TableRow[]} rows * @property {TableColumn[]} [columns] * @property {boolean} loading * @property {string|null} error */ /** * Деталь события toolbar-config-changed. * @typedef {Object} ToolbarConfigChangedDetail * @property {ToolbarItem[]} items */ class DataGridDataSource extends EventTarget { /** * @param {Object} options * @param {string} [options.databaseId] * @param {import('../services/FilterStateManager.js').default} [options.filterState] */ constructor({ databaseId, filterState } = {}) { super(); /** @type {string|null} */ this.databaseId = databaseId || null; /** @type {import('../services/FilterStateManager.js').default|null} */ this.filterState = filterState || null; /** @type {TableRow[]} */ this._rows = []; /** @type {TableColumn[]} */ this._columns = []; /** @type {ToolbarItem[]} */ this._toolbarItems = []; /** @type {boolean} */ this._loading = false; /** @type {string|null} */ this._error = null; /** @type {Set<string>} */ this._expandedRowIds = new Set(); /** @type {boolean} */ this._attached = false; } // ==================== Public API ==================== /** * Подписаться на внешние события (FilterStateManager и др.). * Переопределяется в наследниках. */ attach() { this._attached = true; } /** * Отписаться от внешних событий. * Переопределяется в наследниках. */ detach() { this._attached = false; } /** * Принудительно обновить данные. * @returns {Promise<void>} */ async refresh() { // Переопределяется в наследниках } /** * Получить текущие строки. * @returns {TableRow[]} */ getRows() { return this._rows; } /** * Получить текущие колонки. * @returns {TableColumn[]} */ getColumns() { return this._columns; } /** * Получить конфигурацию toolbar. * @returns {ToolbarItem[]} */ getToolbarConfig() { return this._toolbarItems; } /** * Идёт ли загрузка данных. * @returns {boolean} */ isLoading() { return this._loading; } /** * Получить текст ошибки или null. * @returns {string|null} */ getError() { return this._error; } /** * Установить ID базы данных и перезагрузить данные. * @param {string} databaseId */ setDatabaseId(databaseId) { if (this.databaseId === databaseId) return; this.databaseId = databaseId; this._expandedRowIds.clear(); this.refresh(); } // ==================== Tree management ==================== /** * Развернуть все строки с hasChildren. */ expandAll() { for (const row of this._rows) { if (row.hasChildren) { this._expandedRowIds.add(row.id); } } this._onExpandedChanged(); } /** * Свернуть все строки. */ collapseAll() { this._expandedRowIds.clear(); this._onExpandedChanged(); } /** * Свернуть дерево до указанного уровня. * @param {number} level */ collapseToLevel(level) { for (const row of this._rows) { if (row.hasChildren) { if (row.level < level) { this._expandedRowIds.add(row.id); } else { this._expandedRowIds.delete(row.id); } } } this._onExpandedChanged(); } // ==================== Protected helpers ==================== /** * Установить состояние загрузки. * @param {boolean} loading * @protected */ _setLoading(loading) { this._loading = loading; this._notifyDataChanged(); } /** * Установить ошибку. * @param {string|null} error * @protected */ _setError(error) { this._error = error; this._notifyDataChanged(); } /** * Обновить строки и оповестить подписчиков. * @param {TableRow[]} rows * @protected */ _setRows(rows) { this._rows = rows; this._notifyDataChanged(); } /** * Обновить колонки. * @param {TableColumn[]} columns * @protected */ _setColumns(columns) { this._columns = columns; } /** * Обновить конфиг toolbar и оповестить подписчиков. * @param {ToolbarItem[]} items * @protected */ _setToolbarItems(items) { this._toolbarItems = items; this.dispatchEvent( new CustomEvent("toolbar-config-changed", { detail: /** @type {ToolbarConfigChangedDetail} */ ({ items }), }), ); } /** * Оповестить подписчиков об изменении данных. * @protected */ _notifyDataChanged() { this.dispatchEvent( new CustomEvent("data-changed", { detail: /** @type {DataChangedDetail} */ ({ rows: this._rows, columns: this._columns, loading: this._loading, error: this._error, }), }), ); } /** * Хук, вызываемый при изменении набора развёрнутых строк. * Переопределяется в наследниках для перестройки видимых строк. * @protected */ _onExpandedChanged() { this._notifyDataChanged(); } } export default DataGridDataSource; export { DataGridDataSource };