732 lines
43 KiB
Markdown
732 lines
43 KiB
Markdown
# Панель экосистемы
|
||
|
||
Статическая навигационная панель для быстрого доступа к сервисам инфраструктуры. Поддерживает гибкую иерархию данных, поиск, дерево навигации, несколько режимов отображения и настройку внешнего вида.
|
||
|
||
Стек: HTML, CSS, vanilla JavaScript. Без сборщиков и фреймворков.
|
||
|
||
## Оглавление
|
||
|
||
- [Возможности](#возможности)
|
||
- [Быстрый старт](#быстрый-старт)
|
||
- [Структура проекта](#структура-проекта)
|
||
- [Конфигурация](#конфигурация)
|
||
- [`settings.json`](#settingsjson)
|
||
- [Пресет организации (`defaults` + `features`)](#пресет-организации-defaults--features)
|
||
- [Форматы иерархии](#форматы-иерархии)
|
||
- [Схема `panel-data`](#схема-panel-data)
|
||
- [Поля данных](#поля-данных)
|
||
- [Примеры данных](#примеры-данных)
|
||
- [Готовые демо-страницы (`examples/`)](#готовые-демо-страницы-examples)
|
||
- [Типы сервисов (`serviceMeta`)](#типы-сервисов-servicemeta)
|
||
- [Рекомендуемые типы сервисов](#рекомендуемые-типы-сервисов)
|
||
- [Настройки интерфейса (⚙)](#настройки-интерфейса-)
|
||
- [Узоры панелей](#узоры-панелей)
|
||
- [Разделение слоёв узоров](#разделение-слоёв-узоров)
|
||
- [Свой узор (custom)](#свой-узор-custom)
|
||
- [Экспорт и импорт настроек](#экспорт-и-импорт-настроек)
|
||
- [Навигация по коду](#навигация-по-коду)
|
||
- [Поведение интерфейса](#поведение-интерфейса)
|
||
- [localStorage](#localstorage)
|
||
- [Разработка](#разработка)
|
||
- [Ограничения](#ограничения)
|
||
- [Лицензия](#лицензия)
|
||
|
||
## Возможности
|
||
|
||
- **Древовидная навигация** слева в стиле файлового менеджера
|
||
- **Каталог карточек** справа с collapsible-разделами
|
||
- **Поиск** по названию, тегам, типу сервиса и родительским разделам
|
||
- **4 режима отображения**: плитки, список, компактные плитки, компактный список
|
||
- **Темы**: светлая / тёмная, 4 пресета акцента + свой градиент
|
||
- **Узоры панелей** — 13 встроенных пресетов + свой URL/SVG; отдельно для nav / section / group / item; слайдеры прозрачности, высоты и перехода
|
||
- **Разделение слоёв** — подложка и blur, чтобы вложенные узоры не «наезжали» друг на друга
|
||
- **Экспорт / импорт / сброс** настроек ⚙ в JSON-файл
|
||
- **Настройки** через popup (иконка ⚙)
|
||
- **Изменяемая ширина** бокового меню и **высота** верхней шапки (drag, можно скрыть ручки)
|
||
- **Габариты** — лимиты ширины контента, меню и шапки (⚙, для администратора)
|
||
- **Колонки каталога** — число элементов в строке для каждого из 4 режимов отображения
|
||
- **Сохранение состояния** в `localStorage` (настройки UI, раскрытие веток)
|
||
- **Конфигурация через JSON** — локальный объект или загрузка по URL
|
||
- **Гибкая иерархия**: полная вложенность, раздел без подраздела, плоский список
|
||
|
||
## Быстрый старт
|
||
|
||
Панель использует `fetch`, поэтому её нужно открывать через HTTP-сервер, а не напрямую как `file://`.
|
||
|
||
```bash
|
||
cd ecosystem
|
||
npx serve .
|
||
```
|
||
|
||
Открой в браузере адрес, который покажет сервер (обычно `http://localhost:3000`).
|
||
|
||
## Структура проекта
|
||
|
||
```
|
||
ecosystem/
|
||
├── index.html # Разметка страницы
|
||
├── styles.css # Стили, темы, адаптив
|
||
├── script.js # Логика, рендер, примеры структур
|
||
├── settings.json # Данные + пресет UI организации
|
||
├── settings-top-bar.example.json # Пример пресета «шапка сверху»
|
||
├── panel-data.json # Данные навигации (рабочий файл)
|
||
├── examples/ # Готовые демо-страницы (см. examples/README.md)
|
||
│ ├── index.html # Оглавление примеров
|
||
│ ├── shared/ # styles, script, assets для демо
|
||
│ ├── full/ # section → group → item
|
||
│ ├── section-items/ # section → item
|
||
│ ├── flat/ # плоский items[]
|
||
│ └── mixed/ # items + groups
|
||
├── panel-data-full.example.json # JSON-сниппет (без страницы)
|
||
├── assets/ # SVG-иконки сервисов
|
||
└── README.md
|
||
```
|
||
|
||
## Конфигурация
|
||
|
||
### `settings.json`
|
||
|
||
Указывает, откуда загружать данные панели.
|
||
|
||
**Вариант 1 — загрузка из файла или URL:**
|
||
|
||
```json
|
||
{
|
||
"dataUrl": "./panel-data.json"
|
||
}
|
||
```
|
||
|
||
Можно указать внешний адрес или любой из example-файлов:
|
||
|
||
```json
|
||
{
|
||
"dataUrl": "./panel-data-flat.example.json"
|
||
}
|
||
```
|
||
|
||
**Вариант 2 — inline-объект в том же файле:**
|
||
|
||
```json
|
||
{
|
||
"dataUrl": "",
|
||
"data": {
|
||
"title": "Заголовок панели",
|
||
"sections": []
|
||
}
|
||
}
|
||
```
|
||
|
||
> Если `dataUrl` не пустой, он имеет **приоритет** над полем `data`.
|
||
|
||
При ошибке загрузки или невалидном JSON на странице показывается экран с описанием проблемы.
|
||
|
||
#### Пресет организации (`defaults` + `features`)
|
||
|
||
В `settings.json` можно задать дефолтный вид панели для организации и ограничить, что пользователь может менять в ⚙.
|
||
|
||
```json
|
||
{
|
||
"dataUrl": "./panel-data.json",
|
||
"defaults": {
|
||
"navLayout": "sidebar",
|
||
"showTree": true,
|
||
"showResizeHandles": true,
|
||
"theme": "dark",
|
||
"view": "cards",
|
||
"accent": "violet",
|
||
"customAccentFrom": "#8b5cf6",
|
||
"customAccentTo": "#22d3ee",
|
||
"customAccentAngle": 135,
|
||
"panelPatternNav": "hex",
|
||
"panelPatternNavOpacity": 58,
|
||
"panelPatternNavHeight": 38,
|
||
"panelPatternNavFade": 18,
|
||
"panelPatternNavGradientOpacity": 62,
|
||
"panelPatternNavGradientHeight": 34,
|
||
"panelPatternNavGradientFade": 16,
|
||
"panelPatternSection": "hex",
|
||
"panelPatternSectionOpacity": 58,
|
||
"panelPatternSectionHeight": 38,
|
||
"panelPatternSectionFade": 18,
|
||
"panelPatternSectionGradientOpacity": 62,
|
||
"panelPatternSectionGradientHeight": 34,
|
||
"panelPatternSectionGradientFade": 16,
|
||
"panelPatternGroup": "none",
|
||
"panelPatternGroupOpacity": 58,
|
||
"panelPatternGroupHeight": 38,
|
||
"panelPatternGroupFade": 18,
|
||
"panelPatternGroupGradientOpacity": 62,
|
||
"panelPatternGroupGradientHeight": 34,
|
||
"panelPatternGroupGradientFade": 16,
|
||
"panelPatternItem": "none",
|
||
"panelPatternItemOpacity": 58,
|
||
"panelPatternItemHeight": 38,
|
||
"panelPatternItemFade": 18,
|
||
"panelPatternItemGradientOpacity": 62,
|
||
"panelPatternItemGradientHeight": 34,
|
||
"panelPatternItemGradientFade": 16,
|
||
"patternIsolation": 46,
|
||
"showNumbers": false,
|
||
"showTreeTags": true,
|
||
"showTreeType": false,
|
||
"treeMetaLayout": "inline",
|
||
"treeColumns": 1,
|
||
"autoCollapseSearch": true,
|
||
"sidebarWidth": 360,
|
||
"topBarHeight": 140,
|
||
"shellMaxWidth": 2240,
|
||
"sidebarMinWidth": 240,
|
||
"sidebarMaxWidth": 720,
|
||
"sidebarMaxWidthWide": 1040,
|
||
"treeSidebarBaseWidth": 360,
|
||
"treeSidebarColumnWidth": 96,
|
||
"topBarMinHeight": 80,
|
||
"topBarMaxHeight": 560,
|
||
"catalogCardsColumns": 3,
|
||
"catalogListColumns": 1,
|
||
"catalogCompactCardsColumns": 4,
|
||
"catalogCompactListColumns": 2,
|
||
"collapseAnimation": "animated",
|
||
"collapseDuration": 260,
|
||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||
"settingsPanelWidth": 440
|
||
},
|
||
"limits": {
|
||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||
"sidebarMinWidth": { "min": 220, "max": 480 },
|
||
"sidebarMaxWidth": { "min": 400, "max": 1200 },
|
||
"sidebarMaxWidthWide": { "min": 560, "max": 2000 },
|
||
"treeSidebarBaseWidth": { "min": 280, "max": 720 },
|
||
"treeSidebarColumnWidth": { "min": 60, "max": 200 },
|
||
"topBarMinHeight": { "min": 64, "max": 240 },
|
||
"topBarMaxHeight": { "min": 160, "max": 800 },
|
||
"catalogCardsColumns": { "min": 1, "max": 6 },
|
||
"catalogListColumns": { "min": 1, "max": 4 },
|
||
"catalogCompactCardsColumns": { "min": 2, "max": 8 },
|
||
"catalogCompactListColumns": { "min": 1, "max": 6 }
|
||
},
|
||
"features": {
|
||
"allowNavLayoutChange": true,
|
||
"allowShowTreeChange": true,
|
||
"allowSearch": true,
|
||
"allowViewChange": true,
|
||
"allowCatalogColumnsChange": true,
|
||
"allowShowNumbers": true,
|
||
"allowTreeOptions": true,
|
||
"allowSidebarResize": true,
|
||
"allowLayoutLimitsChange": true,
|
||
"allowThemeChange": true,
|
||
"allowAccentChange": true,
|
||
"allowPanelPatternChange": true
|
||
}
|
||
}
|
||
```
|
||
|
||
| `defaults` | Варианты | Описание |
|
||
|------------|----------|----------|
|
||
| `navLayout` | `"sidebar"`, `"top"` | `"sidebar"` — панель слева; `"top"` — шапка с названием и поиском |
|
||
| `showTree` | `true`, `false` | Показывать дерево навигации (в любом режиме макета) |
|
||
| `showResizeHandles` | `true`, `false` | Показывать ручки drag-resize меню / шапки |
|
||
| `theme` | `"dark"`, `"light"` | Стартовая цветовая тема |
|
||
| `view` | `"cards"`, `"list"`, `"compact-cards"`, `"compact-list"` | Режим отображения каталога |
|
||
| `accent` | `"violet"`, `"cyan"`, `"lime"`, `"amber"`, `"custom"` | Пресет акцента; при `"custom"` используются поля градиента ниже |
|
||
| `customAccentFrom` | `#rrggbb` | Цвет «от» для `accent: "custom"` (по умолчанию `#8b5cf6`) |
|
||
| `customAccentTo` | `#rrggbb` | Цвет «до» для `accent: "custom"` (по умолчанию `#22d3ee`) |
|
||
| `customAccentAngle` | `0`–`360` | Угол градиента в градусах (по умолчанию `135`) |
|
||
| `panelPatternNav` … `panelPatternItem` | см. [узоры панелей](#узоры-панелей) | Тип узора для каждого типа блоков (`none`, `hex`, …) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}Opacity` | `0`–`100` | Прозрачность узора в % (по умолчанию `58`) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}Height` | `0`–`100` | Высота узора в % (по умолчанию `38`; `0` — скрыть) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}Fade` | `0`–`100` | Ширина зоны затухания узора в % высоты блока: `0` — резкий обрыв на линии высоты, `100` — плавное затухание от верха (по умолчанию `18`) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}GradientOpacity` | `0`–`100` | Прозрачность акцентного градиента в % (по умолчанию `62`) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}GradientHeight` | `0`–`100` | Высота градиента в % (по умолчанию `34`) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}GradientFade` | `0`–`100` | Зона затухания градиента (по умолчанию `16`) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}CustomImage` | строка URL | Изображение для `custom` (пусто по умолчанию) |
|
||
| `panelPattern{Nav\|Section\|Group\|Item}CustomSize` | `"WxH"` в px | Размер плитки custom (по умолчанию `32px 32px`) |
|
||
| `patternIsolation` | `0`–`100` | Разделение слоёв узоров (подложка + blur; по умолчанию `46`) |
|
||
| `showNumbers` | `true`, `false` | Показывать номера разделов, подразделов и элементов |
|
||
| `showTreeTags` | `true`, `false` | Теги элементов в дереве (оформление как на карточках) |
|
||
| `showTreeType` | `true`, `false` | Подпись типа сервиса в дереве |
|
||
| `treeMetaLayout` | `"inline"`, `"stacked"` | Расположение тегов и типа: сбоку от названия или снизу |
|
||
| `treeColumns` | `1`–`6` (по умолчанию `1`) | Число колонок элементов внутри папок дерева |
|
||
| `autoCollapseSearch` | `true`, `false` | При поиске сворачивать ветки дерева без совпадений |
|
||
| `sidebarWidth` | `sidebarMinWidth`–`sidebarMaxWidth` (или `sidebarMaxWidthWide` при `treeColumns` > 1) | Текущая ширина бокового меню (drag-resize); при `navLayout: "top"` не применяется |
|
||
| `topBarHeight` | `topBarMinHeight`–`topBarMaxHeight` (до ~75% высоты окна) | Текущая высота верхней шапки (drag-resize); только при `navLayout: "top"` |
|
||
| `shellMaxWidth` | см. `limits.shellMaxWidth` | Максимальная ширина контейнера `.shell` (вся страница) |
|
||
| `sidebarMinWidth` | см. `limits.sidebarMinWidth` | Минимальная ширина бокового меню при drag-resize |
|
||
| `sidebarMaxWidth` | см. `limits.sidebarMaxWidth` | Максимальная ширина меню при `treeColumns: 1` |
|
||
| `sidebarMaxWidthWide` | см. `limits.sidebarMaxWidthWide` | Потолок ширины меню при нескольких колонках дерева |
|
||
| `treeSidebarBaseWidth` | см. `limits.treeSidebarBaseWidth` | База формулы ширины меню: `base + columns × step` |
|
||
| `treeSidebarColumnWidth` | см. `limits.treeSidebarColumnWidth` | Прирост ширины меню за каждую колонку дерева |
|
||
| `topBarMinHeight` | см. `limits.topBarMinHeight` | Минимальная высота шапки в режиме «Сверху» |
|
||
| `topBarMaxHeight` | см. `limits.topBarMaxHeight` | Максимальная высота шапки (также ограничена ~75% окна) |
|
||
| `catalogCardsColumns` | см. `limits.catalogCardsColumns` | Колонок в строке в режиме «Плитки» |
|
||
| `catalogListColumns` | см. `limits.catalogListColumns` | Колонок в строке в режиме «Список» |
|
||
| `catalogCompactCardsColumns` | см. `limits.catalogCompactCardsColumns` | Колонок в строке в режиме «Компакт плитки» |
|
||
| `catalogCompactListColumns` | см. `limits.catalogCompactListColumns` | Колонок в строке в режиме «Компакт список» |
|
||
| `collapseAnimation` | `"instant"`, `"animated"` | Режим раскрытия списков, дерева и collapsible-блоков |
|
||
| `collapseDuration` | `100`–`800` | Длительность плавного раскрытия в миллисекундах (по умолчанию `260`) |
|
||
| `searchPlaceholder` | произвольная строка | Текст-подсказка в поле поиска |
|
||
| `settingsPanelWidth` | `limits.settingsPanelWidth` (по умолчанию `320`–`720`) | Ширина popup настроек в px (по умолчанию `440`) |
|
||
|
||
| `limits` | Формат | Описание |
|
||
|----------|--------|----------|
|
||
| `shellMaxWidth` | `{ "min", "max" }` | Диапазон слайдера «Ширина контента» в ⚙ → Габариты |
|
||
| `sidebarMinWidth` | `{ "min", "max" }` | Диапазон «Меню: мин. ширина» |
|
||
| `sidebarMaxWidth` | `{ "min", "max" }` | Диапазон «Меню: макс. (1 кол.)» |
|
||
| `sidebarMaxWidthWide` | `{ "min", "max" }` | Диапазон «Меню: макс. (неск. кол.)» |
|
||
| `treeSidebarBaseWidth` | `{ "min", "max" }` | Диапазон «Дерево: база ширины» |
|
||
| `treeSidebarColumnWidth` | `{ "min", "max" }` | Диапазон «Дерево: ширина колонки» |
|
||
| `topBarMinHeight` | `{ "min", "max" }` | Диапазон «Шапка: мин. высота» |
|
||
| `topBarMaxHeight` | `{ "min", "max" }` | Диапазон «Шапка: макс. высота» |
|
||
| `catalogCardsColumns` | `{ "min", "max" }` | Диапазон колонок каталога «Плитки» |
|
||
| `catalogListColumns` | `{ "min", "max" }` | Диапазон колонок каталога «Список» |
|
||
| `catalogCompactCardsColumns` | `{ "min", "max" }` | Диапазон колонок «Компакт плитки» |
|
||
| `catalogCompactListColumns` | `{ "min", "max" }` | Диапазон колонок «Компакт список» |
|
||
|
||
При `treeColumns > 1` фактический потолок ширины меню: `min(sidebarMaxWidthWide, treeSidebarBaseWidth + treeColumns × treeSidebarColumnWidth)`.
|
||
|
||
`shellMaxWidth` автоматически не опускается ниже `sidebarMaxWidthWide + 320`, чтобы оставалось место для каталога.
|
||
|
||
| `features` | Варианты | Описание |
|
||
|------------|----------|----------|
|
||
| `allowNavLayoutChange` | `true`, `false` | Переключатель «Сбоку / Сверху» |
|
||
| `allowShowTreeChange` | `true`, `false` | Toggle «Показывать дерево» в ⚙ → Макет |
|
||
| `allowSearch` | `true`, `false` | Поле поиска |
|
||
| `allowViewChange` | `true`, `false` | Режимы каталога (плитки / список / компакт) |
|
||
| `allowCatalogColumnsChange` | `true`, `false` | Слайдеры «колонок в строке» для каждого вида каталога |
|
||
| `allowShowNumbers` | `true`, `false` | Нумерация |
|
||
| `allowTreeOptions` | `true`, `false` | Колонки дерева (1–6), теги, тип, расположение мета, авто-раскрытие |
|
||
| `allowSidebarResize` | `true`, `false` | Drag-resize и toggle «Ручки изменения размера» |
|
||
| `allowLayoutLimitsChange` | `true`, `false` | Раздел ⚙ «Габариты»: лимиты ширины контента и навигации |
|
||
| `allowThemeChange` | `true`, `false` | Светлая / тёмная тема |
|
||
| `allowAccentChange` | `true`, `false` | Пресеты и свой градиент |
|
||
| `allowPanelPatternChange` | `true`, `false` | Выбор узора панелей в ⚙ → Оформление |
|
||
|
||
Пользовательские изменения сохраняются в `localStorage`, но заблокированные `features` всегда возвращаются к `defaults`.
|
||
|
||
Пример пресета «шапка сверху»: [`settings-top-bar.example.json`](settings-top-bar.example.json) — `navLayout: "top"`, дерево по умолчанию скрыто (`showTree: false`), но его можно включить.
|
||
|
||
### Форматы иерархии
|
||
|
||
Панель поддерживает три базовых формата. Старый **section → group → item** работает без изменений.
|
||
|
||
| Формат | Структура | Дерево | Каталог |
|
||
|--------|-----------|--------|---------|
|
||
| **Полная** | `sections[].groups[].items[]` | раздел → подраздел → файлы | collapsible разделы и подразделы |
|
||
| **Раздел → item** | `sections[].items[]` | раздел → файлы | карточки внутри раздела |
|
||
| **Плоский список** | корневой `items[]` | плоский список файлов | только сетка карточек |
|
||
| **Смешанный** | `sections[].items[]` + `groups[]` | прямые файлы, затем подразделы | прямые карточки, затем подразделы |
|
||
|
||
Минимум данных: корень должен содержать `sections` **или** `items`. Каждый раздел — `groups` **или** `items`. Каждый элемент — `title` и `type`.
|
||
|
||
### Схема `panel-data`
|
||
|
||
Абстрактная структура файла данных (без привязки к конкретным сервисам):
|
||
|
||
```json
|
||
{
|
||
"title": "<заголовок панели>",
|
||
"sections": [
|
||
{
|
||
"id": "<slug-раздела>",
|
||
"number": "<опционально>",
|
||
"title": "<название раздела>",
|
||
"description": "<опционально>",
|
||
"items": [
|
||
{
|
||
"id": "<стабильный-ключ>",
|
||
"number": "<опционально>",
|
||
"title": "<название>",
|
||
"type": "<ключ из serviceMeta>",
|
||
"tags": ["<тег>"],
|
||
"url": "<ссылка>"
|
||
}
|
||
],
|
||
"groups": [
|
||
{
|
||
"id": "<slug-подраздела>",
|
||
"number": "<опционально>",
|
||
"title": "<название подраздела>",
|
||
"description": "<опционально>",
|
||
"items": ["<элементы — та же форма>"]
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"items": ["<альтернатива sections: плоский список>"],
|
||
"serviceMeta": {
|
||
"<type-key>": {
|
||
"label": "<подпись типа>",
|
||
"icon": "assets/<иконка>.svg"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Используй **либо** `sections`, **либо** корневой `items`. Внутри раздела — **либо** `groups`, **либо** `items`, **либо** оба.
|
||
|
||
### Поля данных
|
||
|
||
| Уровень | Поля | Описание |
|
||
|---------|------|----------|
|
||
| **Корень** | `title` | Заголовок панели (sidebar + `<title>`) |
|
||
| | `sections` | Массив разделов (если нет корневого `items`) |
|
||
| | `items` | Плоский список без разделов |
|
||
| | `serviceMeta` | Дополнительные или переопределённые типы сервисов |
|
||
| **section** | `id`, `title` | Обязательные идентификатор и название |
|
||
| | `number`, `description` | Опционально |
|
||
| | `groups`, `items` | Хотя бы одно из двух |
|
||
| **group** | `id`, `title`, `items` | Подраздел с элементами |
|
||
| | `number`, `description` | Опционально |
|
||
| **item** | `title`, `type` | Обязательные название и тип |
|
||
| | `url` | Ссылка; `"#"` — заглушка |
|
||
| | `tags` | Массив строк для поиска |
|
||
| | `id` | Стабильный ключ (DOM, фокус из дерева) |
|
||
| | `number` | Отображаемый номер |
|
||
| | `icon` | Опционально: свой путь к SVG (иначе — иконка из `type`) |
|
||
|
||
- `type` — ключ из встроенного `defaultServiceMeta` или из `serviceMeta` в JSON; определяет иконку и подпись на карточке
|
||
- `id` элемента нужен, если нет `number` — иначе ключ генерируется автоматически
|
||
|
||
### Примеры данных
|
||
|
||
Готовые демо-страницы — в папке [`examples/`](examples/README.md). Корневые `panel-data-*.example.json` — те же структуры в виде JSON-сниппетов.
|
||
|
||
### Готовые демо-страницы (`examples/`)
|
||
|
||
Каждый пример — отдельная страница с `index.html`, `settings.json` и `panel-data.json`. Общие ресурсы в `examples/shared/`.
|
||
|
||
```bash
|
||
cd examples
|
||
npx serve .
|
||
```
|
||
|
||
| Путь | Формат |
|
||
|------|--------|
|
||
| `/` | оглавление примеров |
|
||
| `/full/` | section → group → item |
|
||
| `/section-items/` | section → item |
|
||
| `/flat/` | плоский `items[]` |
|
||
| `/mixed/` | items + groups в разделе |
|
||
|
||
Подробнее: [examples/README.md](examples/README.md).
|
||
|
||
### Типы сервисов (`serviceMeta`)
|
||
|
||
Базовые типы заданы в `script.js` (`defaultServiceMeta`):
|
||
|
||
`analytics`, `api`, `calendar`, `ceph`, `chat`, `cicd`, `cloudbeaver`, `config`, `drive`, `forum`, `gitea`, `grafana`, `ipmi`, `kubernetes`, `ldap`, `messages`, `opensearch`, `postgresql`, `prometheus`, `rabbitmq`, `redmine`, `rules`, `social`, `vault`, `video`, `vpn`, `wiki`, `xpath`, `xmpp`
|
||
|
||
Каждый тип имеет собственную SVG в `assets/` (например, `postgresql.svg`, `cloudbeaver.svg`). Иконка показывается на карточке каталога и в дереве навигации.
|
||
|
||
Переопределение или добавление типов в JSON:
|
||
|
||
```json
|
||
{
|
||
"serviceMeta": {
|
||
"my-type": {
|
||
"label": "Подпись",
|
||
"icon": "assets/config.svg"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Рекомендуемые типы сервисов
|
||
|
||
Популярные `type`, которые можно добавить в `serviceMeta` (иконки — переиспользовать из `assets/`):
|
||
|
||
| Категория | Типы (`type`) | Комментарий |
|
||
|-----------|---------------|-------------|
|
||
| **Прокси / балансировка** | `nginx`, `traefik` | reverse proxy, ingress |
|
||
| **Контейнеры** | `docker`, `portainer`, `argocd` | runtime, UI, GitOps |
|
||
| **CI/CD** | `jenkins`, `gitlab`, `harbor` | сборка, репозиторий, registry |
|
||
| **Базы данных** | `mysql`, `mongodb`, `redis` | рядом с уже имеющимися `postgresql`, `opensearch` |
|
||
| **Очереди / стриминг** | `kafka` | аналог `rabbitmq` |
|
||
| **Мониторинг / логи** | `loki`, `zabbix`, `sentry` | логи, метрики, ошибки |
|
||
| **Безопасность / IAM** | `vault`, `keycloak`, `consul` | секреты, SSO, service mesh |
|
||
| **Поиск** | `elasticsearch`, `sonarqube` | полнотекст, качество кода |
|
||
| **Хранилище** | `minio` | S3-совместимое |
|
||
| **IaC** | `terraform` | инфраструктура как код |
|
||
| **Трекеры** | `jira` | задачи, рядом с `redmine` |
|
||
|
||
Чтобы подключить тип:
|
||
|
||
1. Добавь запись в `serviceMeta` вашего JSON **или** в `defaultServiceMeta` в `script.js`
|
||
2. При необходимости положи отдельную SVG в `assets/`
|
||
3. Укажи `"type": "nginx"` (и т.д.) в элементах
|
||
|
||
## Настройки интерфейса (⚙)
|
||
|
||
Popup сгруппирован по разделам. Доступные блоки зависят от `features` в `settings.json`. Изменения пользователя сохраняются в `localStorage` (`ecosystem-panel-preferences`).
|
||
|
||
| Раздел | Настройки |
|
||
|--------|-----------|
|
||
| **Макет** | **Сбоку** / **Сверху**; toggle «Показывать дерево»; toggle «Ручки изменения размера»; слайдер «Ширина настроек» |
|
||
| **Габариты** | Лимиты ширины контента, меню и шапки (сворачиваемый блок; при `allowLayoutLimitsChange`) |
|
||
| **Каталог** | Вид карточек, колонок в строке для каждого вида, нумерация |
|
||
| **Дерево** | Колонок 1–6, теги, тип, расположение мета, авто-раскрытие (при `showTree: true`) |
|
||
| **Оформление** | Тема, акцент, узоры по типам блоков, разделение слоёв, свой градиент |
|
||
| **(footer)** | Сброс, экспорт и импорт всех preferences в JSON |
|
||
|
||
### Режим «Сверху»
|
||
|
||
Компактная шапка: заголовок, поиск и ⚙. Дерево по умолчанию скрыто, но его можно включить (`showTree: true`) — оно появится под шапкой на всю ширину.
|
||
|
||
### Скрытие дерева
|
||
|
||
Дерево управляется отдельно от макета (`showTree`). Переключение — в ⚙ → **Макет**.
|
||
|
||
### Свой градиент
|
||
|
||
Кнопка **«Свой»** открывает настройки:
|
||
|
||
- угол (0–360°)
|
||
- цвет «от» и «до»
|
||
|
||
Значения сохраняются в `localStorage`.
|
||
|
||
### Узоры панелей
|
||
|
||
Настраиваются в ⚙ → **Оформление** (если `allowPanelPatternChange: true`).
|
||
|
||
#### Четыре цели (targets)
|
||
|
||
У каждого типа блока свой независимый набор: пресет, слайдеры и (опционально) свой URL.
|
||
|
||
| Цель | Preference (префикс) | DOM-элемент | Градиент под узором |
|
||
|------|----------------------|-------------|---------------------|
|
||
| Панель навигации | `panelPatternNav*` | `.hero-content` | да |
|
||
| Раздел | `panelPatternSection*` | `.catalog-section` | да |
|
||
| Подраздел | `panelPatternGroup*` | `.catalog-group` | да |
|
||
| Элемент | `panelPatternItem*` | `.nav-card` | нет (только узор) |
|
||
|
||
#### Встроенные пресеты (`panelPattern*`)
|
||
|
||
| id | Подпись в ⚙ | Описание |
|
||
|----|-------------|----------|
|
||
| `none` | Нет | Без повторяющейся текстуры |
|
||
| `hex` | Соты | Крупные гексагоны |
|
||
| `hex-sm` | Соты м. | Мелкие гексагоны |
|
||
| `dots` | Точки | Средняя точечная сетка |
|
||
| `dots-sm` | Точки м. | Мелкие кружки |
|
||
| `dots-lg` | Точки кр. | Крупные кружки |
|
||
| `grid` | Сетка | Линейная сетка |
|
||
| `diagonal` | Диаг. | Диагональные полосы |
|
||
| `cross` | Клетка | Перекрёстная штриховка |
|
||
| `mesh` | Mesh | Точки цвета акцента |
|
||
| `tri-sm` | △ мел. | Мелкие треугольники |
|
||
| `tri-lg` | △ кр. | Крупные треугольники |
|
||
| `diamond` | Ромбы | Ромбовидная сетка |
|
||
| `custom` | Свой | Своя текстура (см. ниже) |
|
||
|
||
По умолчанию: **навигация** и **разделы** — `hex`; **подразделы** и **элементы** — `none`.
|
||
|
||
#### Слайдеры (для каждой цели)
|
||
|
||
| Сuffix preference | UI | Диапазон | По умолчанию |
|
||
|-------------------|-----|----------|--------------|
|
||
| `Opacity` | Узор: прозрачность | 0–100 % | 58 |
|
||
| `Height` | Узор: высота | 0–100 % высоты блока | 38 |
|
||
| `Fade` | Узор: переход | 0–100 % | 18 |
|
||
| `GradientOpacity` | Градиент: прозрачность | 0–100 % | 62 |
|
||
| `GradientHeight` | Градиент: высота | 0–100 % | 34 |
|
||
| `GradientFade` | Градиент: переход | 0–100 % | 16 |
|
||
|
||
**Переход (fade):** `0%` — резкий обрыв на линии «высоты»; большее значение — плавнее затухание к низу блока.
|
||
|
||
Для цели **Элемент** в UI только три слайдера узора (без градиента).
|
||
|
||
#### Как узор попадает на экран
|
||
|
||
1. `preferences` → `applyPanelPatternPreferences()` в `script.js`
|
||
2. На `<html>`: `data-panel-pattern-nav="hex"` (и section / group / item)
|
||
3. CSS-переменные: `--panel-pattern-{target}-opacity`, `-height`, `-fade-start`, `--panel-gradient-{target}-*`
|
||
4. Встроенные пресеты: `styles.css` → `--pattern-tile-*` и селекторы `html[data-panel-pattern-*="…"]`
|
||
5. Слои на блоке: `::before` (градиент), `::after` (текстура с mask по высоте/переходу)
|
||
|
||
Кнопки пресетов в ⚙ **не** захардкожены в HTML: контейнеры `data-pattern-swatches="nav|section|group|item"`, список — `panelPatternCatalog` в `script.js`.
|
||
|
||
### Разделение слоёв узоров
|
||
|
||
Слайдер **«Разделение слоёв»** в ⚙ → Оформление (`patternIsolation`, 0–100 %, по умолчанию `46`).
|
||
|
||
Когда узоры включены на нескольких уровнях вложенности (раздел → подраздел → карточка), текстуры могут визуально накладываться. Настройка добавляет на `.catalog-section`, `.catalog-group` и `.nav-card`:
|
||
|
||
- полупрозрачную подложку (`--panel-layer-fill`);
|
||
- `backdrop-filter: blur(...)` (`--panel-layer-blur`).
|
||
|
||
Так каждый уровень «отрезает» узоры снизу и остаётся читаемой граница. Значение задаётся в `settings.json` → `defaults.patternIsolation` и сохраняется в `localStorage`.
|
||
|
||
### Свой узор (custom)
|
||
|
||
1. В панели нужной цели выберите пресет **«Свой»**
|
||
2. Появится блок с полями:
|
||
- **Изображение** — `panelPattern{Nav|Section|Group|Item}CustomImage`
|
||
- **Размер плитки** — `panelPattern*CustomSize` (например `32px 32px`)
|
||
|
||
Допустимые форматы URL:
|
||
|
||
- `data:image/svg+xml,…` или `data:image/png;base64,…`
|
||
- `https://…` / `http://…`
|
||
- относительный путь: `./assets/my-pattern.svg`, `assets/pattern.png`
|
||
- `javascript:` — **отклоняется** при нормализации
|
||
|
||
Пример минимального SVG (data URI):
|
||
|
||
```
|
||
data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Ccircle cx='8' cy='8' r='2' fill='white' fill-opacity='.2'/%3E%3C/svg%3E
|
||
```
|
||
|
||
При `custom` без URL узор не рисуется (opacity = 0), пока не задано изображение.
|
||
|
||
### Экспорт и импорт настроек
|
||
|
||
Внизу popup ⚙ — три кнопки:
|
||
|
||
| Кнопка | Действие |
|
||
|--------|----------|
|
||
| **Сбросить** | Подтверждение → `panelDefaults` из `settings.json`, очистка `ecosystem-panel-preferences` |
|
||
| **Экспортировать** | Скачивание `ecosystem-panel-preferences.json` |
|
||
| **Импортировать** | Выбор JSON; применение с `normalizePreferences` + `enforceFeatureLocks` |
|
||
|
||
Формат экспорта:
|
||
|
||
```json
|
||
{
|
||
"version": 1,
|
||
"exportedAt": "2026-06-29T12:00:00.000Z",
|
||
"preferences": { "theme": "dark", "panelPatternNav": "hex", "patternIsolation": 46 }
|
||
}
|
||
```
|
||
|
||
Импорт принимает и обёртку с `preferences`, и «голый» объект настроек.
|
||
|
||
### Раскрытие списков
|
||
|
||
В ⚙ → **Каталог** — режим **Резко** / **Плавно** и длительность (100–800 ms). Плавное раскрытие применяется к разделам и подразделам каталога, дереву навигации и схлопывающимся блокам в настройках. Кнопки сворачивания — chevron в стеклянной рамке с акцентной подсветкой при наведении.
|
||
|
||
| Preference | Значения | По умолчанию |
|
||
|------------|----------|--------------|
|
||
| `collapseAnimation` | `"instant"`, `"animated"` | `"animated"` |
|
||
| `collapseDuration` | `100`–`800` (ms) | `240` |
|
||
|
||
Старые ключи (`panelPattern`, `panelPatternOnNav`, …) при загрузке автоматически мигрируют в per-target формат, если в `localStorage` ещё нет `panelPatternNav`.
|
||
|
||
### Навигация по коду
|
||
|
||
Быстрый поиск: в `script.js` и `styles.css` — секции с разделителем `// =====` / `/* =====`; в `index.html` — комментарий-оглавление в начале файла.
|
||
|
||
#### script.js
|
||
|
||
| Секция | Содержимое |
|
||
|--------|------------|
|
||
| `Panel pattern catalog and defaults` | `panelPatternCatalog`, `availablePanelPatterns`, defaults по целям |
|
||
| `Panel pattern preferences` | normalize, apply, custom URL, UI (`renderPatternSwatches`, …) |
|
||
| `Settings import, export and reset` | `applyStoredPreferences`, export/import JSON |
|
||
| `Preferences application` | `applyPreferences`, sync контролов ⚙ |
|
||
| `Collapse state handling` | анимация раскрытия details |
|
||
|
||
Ключевые функции узоров: `applyPanelPatternPreferences`, `applyPatternIsolationPreference`, `getPanelPatternPreferenceKey`, `setupPatternControls`.
|
||
|
||
#### styles.css
|
||
|
||
| Секция | Содержимое |
|
||
|--------|------------|
|
||
| `01. Theme Tokens` | `--pattern-tile-*`, mapping `html[data-panel-pattern-*]`, `--panel-layer-*` |
|
||
| `05. Sidebar Panel` | `::before` / `::after` узоров, layer isolation на catalog/group/card |
|
||
| `06. Settings Popup` | `.pattern-swatches`, `.pattern-custom-panel`, `.settings-actions` |
|
||
|
||
#### index.html
|
||
|
||
| Блок | Атрибуты / классы |
|
||
|------|-------------------|
|
||
| Popup ⚙ | `[data-settings-panel]`, секции `[data-settings-section="…"]` |
|
||
| Узоры | `[data-pattern-targets-panel]`, `[data-pattern-swatches="{target}"]` |
|
||
| Разделение слоёв | `[data-pattern-isolation]` |
|
||
| Экспорт | `[data-settings-reset]`, `[data-settings-export]`, `[data-settings-import]` |
|
||
|
||
#### Добавить новый встроенный пресет
|
||
|
||
1. Добавить id в `availablePanelPatterns` и запись в `panelPatternCatalog` (`script.js`)
|
||
2. Определить `--pattern-tile-{id}-image` и `-size` в `:root` (`styles.css`)
|
||
3. Добавить строки `html[data-panel-pattern-{nav|section|group|item}="{id}"]` для всех четырёх целей
|
||
4. Добавить превью `.pattern-swatch[data-panel-pattern="{id}"]::before` в секции настроек
|
||
5. При необходимости — override SVG для `html[data-theme="light"]`
|
||
|
||
Значения сохраняются в `localStorage` и выставляют `data-panel-pattern-*` на `<html>` и CSS-переменные `--panel-pattern-{nav|section|group|item}-*`, `--panel-gradient-{target}-*`.
|
||
|
||
## Поведение интерфейса
|
||
|
||
### Поиск
|
||
|
||
Фильтрует каталог и подсвечивает совпадения в дереве и карточках. Ищет по:
|
||
|
||
- названию элемента
|
||
- номеру
|
||
- тегам
|
||
- типу сервиса
|
||
- названиям родительских разделов
|
||
|
||
### Дерево навигации
|
||
|
||
- Клик по **папке** — раскрытие/сворачивание
|
||
- Клик по **названию** — прокрутка к элементу в каталоге
|
||
- Состояние раскрытия сохраняется отдельно для дерева и контента (`ecosystem-panel-collapse-state`)
|
||
|
||
### Адаптив
|
||
|
||
- **Touch-устройства** (`pointer: coarse` / `hover: none`): stacked-layout, меню сверху, ручки resize скрыты
|
||
- **Мышь** (`pointer: fine` + `hover: hover`): sidebar + resizer + каталог в колонках
|
||
- На узких экранах число колонок каталога дополнительно ограничивается в CSS (чтобы сетка не ломалась)
|
||
|
||
### Ширина и габариты layout
|
||
|
||
Лимиты задаются в `settings.json` → `defaults` и `limits`, настраиваются пользователем в ⚙ → **Габариты** (если `allowLayoutLimitsChange: true`):
|
||
|
||
- **`shellMaxWidth`** — максимальная ширина всей панели (по умолчанию до 2800px)
|
||
- **`sidebarMaxWidth`** / **`sidebarMaxWidthWide`** — потолок ширины меню (1 колонка дерева / несколько)
|
||
- **`topBarMinHeight`** / **`topBarMaxHeight`** — диапазон высоты шапки в режиме «Сверху»
|
||
- Текущие **`sidebarWidth`** и **`topBarHeight`** — drag-resize; сохраняются в `localStorage`
|
||
|
||
## localStorage
|
||
|
||
| Ключ | Содержимое |
|
||
|------|------------|
|
||
| `ecosystem-panel-preferences` | Тема, акцент, узоры, вид, колонки, `patternIsolation`, custom-URL, ширина sidebar, export/import |
|
||
| `ecosystem-panel-collapse-state` | Раскрытые/свернутые ветки дерева и каталога |
|
||
|
||
## Разработка
|
||
|
||
### Добавить элемент
|
||
|
||
1. Открой `panel-data.json` (или свой JSON из `dataUrl`)
|
||
2. Найди нужный раздел / подраздел **или** добавь в `items` / корневой `items`
|
||
3. Добавь объект с полями `title`, `type`, при необходимости `tags`, `url`, `id`, `number`
|
||
4. Обнови страницу
|
||
|
||
### Добавить иконку и тип
|
||
|
||
1. Положи SVG в `assets/` (или переиспользуй существующую)
|
||
2. Зарегистрируй тип в `serviceMeta` JSON или в `defaultServiceMeta` в `script.js`
|
||
|
||
### Внешний источник данных
|
||
|
||
```json
|
||
{
|
||
"dataUrl": "https://your-server/panel-data.json"
|
||
}
|
||
```
|
||
|
||
Убедись, что сервер отдаёт `Content-Type: application/json` и разрешает CORS при другом домене.
|
||
|
||
## Ограничения
|
||
|
||
- Требуется HTTP-сервер (`fetch` не работает с `file://`)
|
||
- Внешний `dataUrl` должен быть доступен по CORS
|
||
- Настройки UI хранятся локально в браузере и не синхронизируются между устройствами
|
||
|
||
## Лицензия
|
||
|
||
Приватный проект. Используй и изменяй по своему усмотрению.
|