добавлена возможность настраивать каждый элемент (узоры)
This commit is contained in:
parent
50a04e3856
commit
26dc3bac2b
74
README.md
74
README.md
@ -33,6 +33,7 @@
|
||||
- **Поиск** по названию, тегам, типу сервиса и родительским разделам
|
||||
- **4 режима отображения**: плитки, список, компактные плитки, компактный список
|
||||
- **Темы**: светлая / тёмная, 4 пресета акцента + свой градиент
|
||||
- **Узоры панелей** — соты, точки, сетка и др.; отдельные варианты для светлой темы; можно отключить
|
||||
- **Настройки** через popup (иконка ⚙)
|
||||
- **Изменяемая ширина** бокового меню и **высота** верхней шапки (drag, можно скрыть ручки)
|
||||
- **Габариты** — лимиты ширины контента, меню и шапки (⚙, для администратора)
|
||||
@ -126,6 +127,29 @@ ecosystem/
|
||||
"theme": "dark",
|
||||
"view": "cards",
|
||||
"accent": "violet",
|
||||
"customAccentFrom": "#8b5cf6",
|
||||
"customAccentTo": "#22d3ee",
|
||||
"customAccentAngle": 135,
|
||||
"panelPatternNav": "hex",
|
||||
"panelPatternNavOpacity": 70,
|
||||
"panelPatternNavHeight": 45,
|
||||
"panelPatternNavGradientOpacity": 80,
|
||||
"panelPatternNavGradientHeight": 40,
|
||||
"panelPatternSection": "hex",
|
||||
"panelPatternSectionOpacity": 70,
|
||||
"panelPatternSectionHeight": 45,
|
||||
"panelPatternSectionGradientOpacity": 80,
|
||||
"panelPatternSectionGradientHeight": 40,
|
||||
"panelPatternGroup": "none",
|
||||
"panelPatternGroupOpacity": 70,
|
||||
"panelPatternGroupHeight": 45,
|
||||
"panelPatternGroupGradientOpacity": 80,
|
||||
"panelPatternGroupGradientHeight": 40,
|
||||
"panelPatternItem": "none",
|
||||
"panelPatternItemOpacity": 70,
|
||||
"panelPatternItemHeight": 45,
|
||||
"panelPatternItemGradientOpacity": 80,
|
||||
"panelPatternItemGradientHeight": 40,
|
||||
"showNumbers": false,
|
||||
"showTreeTags": true,
|
||||
"showTreeType": false,
|
||||
@ -146,7 +170,8 @@ ecosystem/
|
||||
"catalogListColumns": 1,
|
||||
"catalogCompactCardsColumns": 5,
|
||||
"catalogCompactListColumns": 3,
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер..."
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||||
"settingsPanelWidth": 460
|
||||
},
|
||||
"limits": {
|
||||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||||
@ -173,7 +198,8 @@ ecosystem/
|
||||
"allowSidebarResize": true,
|
||||
"allowLayoutLimitsChange": true,
|
||||
"allowThemeChange": true,
|
||||
"allowAccentChange": true
|
||||
"allowAccentChange": true,
|
||||
"allowPanelPatternChange": true
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -189,6 +215,13 @@ ecosystem/
|
||||
| `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` | Прозрачность узора в % (по умолчанию `70`) |
|
||||
| `panelPattern{Nav\|Section\|Group\|Item}Height` | `0`–`100` | Высота узора в % (по умолчанию `45`; `0` — скрыть) |
|
||||
| `panelPattern{Nav\|Section\|Group\|Item}Fade` | `0`–`100` | Ширина зоны затухания узора в % высоты блока: `0` — резкий обрыв на линии высоты, `100` — плавное затухание от верха (по умолчанию `10`) |
|
||||
| `panelPattern{Nav\|Section\|Group\|Item}GradientOpacity` | `0`–`100` | Прозрачность акцентного градиента в % (по умолчанию `80`) |
|
||||
| `panelPattern{Nav\|Section\|Group\|Item}GradientHeight` | `0`–`100` | Высота градиента в % (по умолчанию `40`) |
|
||||
| `panelPattern{Nav\|Section\|Group\|Item}GradientFade` | `0`–`100` | Зона затухания градиента (по умолчанию `8`) |
|
||||
| `showNumbers` | `true`, `false` | Показывать номера разделов, подразделов и элементов |
|
||||
| `showTreeTags` | `true`, `false` | Теги элементов в дереве (оформление как на карточках) |
|
||||
| `showTreeType` | `true`, `false` | Подпись типа сервиса в дереве |
|
||||
@ -210,6 +243,7 @@ ecosystem/
|
||||
| `catalogCompactCardsColumns` | см. `limits.catalogCompactCardsColumns` | Колонок в строке в режиме «Компакт плитки» |
|
||||
| `catalogCompactListColumns` | см. `limits.catalogCompactListColumns` | Колонок в строке в режиме «Компакт список» |
|
||||
| `searchPlaceholder` | произвольная строка | Текст-подсказка в поле поиска |
|
||||
| `settingsPanelWidth` | `limits.settingsPanelWidth` (по умолчанию `320`–`720`) | Ширина popup настроек в px (по умолчанию `460`) |
|
||||
|
||||
| `limits` | Формат | Описание |
|
||||
|----------|--------|----------|
|
||||
@ -243,6 +277,7 @@ ecosystem/
|
||||
| `allowLayoutLimitsChange` | `true`, `false` | Раздел ⚙ «Габариты»: лимиты ширины контента и навигации |
|
||||
| `allowThemeChange` | `true`, `false` | Светлая / тёмная тема |
|
||||
| `allowAccentChange` | `true`, `false` | Пресеты и свой градиент |
|
||||
| `allowPanelPatternChange` | `true`, `false` | Выбор узора панелей в ⚙ → Оформление |
|
||||
|
||||
Пользовательские изменения сохраняются в `localStorage`, но заблокированные `features` всегда возвращаются к `defaults`.
|
||||
|
||||
@ -404,11 +439,11 @@ Popup сгруппирован по разделам. Доступные бло
|
||||
|
||||
| Раздел | Настройки |
|
||||
|--------|-----------|
|
||||
| **Макет** | **Сбоку** / **Сверху**; toggle «Показывать дерево»; toggle «Ручки изменения размера» |
|
||||
| **Макет** | **Сбоку** / **Сверху**; toggle «Показывать дерево»; toggle «Ручки изменения размера»; слайдер «Ширина настроек» |
|
||||
| **Габариты** | Лимиты ширины контента, меню и шапки (сворачиваемый блок; при `allowLayoutLimitsChange`) |
|
||||
| **Каталог** | Вид карточек, колонок в строке для каждого вида, нумерация |
|
||||
| **Дерево** | Колонок 1–6, теги, тип, расположение мета, авто-раскрытие (при `showTree: true`) |
|
||||
| **Оформление** | Тема, акцент, свой градиент |
|
||||
| **Оформление** | Тема, акцент, узор панелей, свой градиент |
|
||||
|
||||
### Режим «Сверху»
|
||||
|
||||
@ -427,6 +462,35 @@ Popup сгруппирован по разделам. Доступные бло
|
||||
|
||||
Значения сохраняются в `localStorage`.
|
||||
|
||||
### Узоры панелей
|
||||
|
||||
Настраиваются в ⚙ → **Оформление** (если `allowPanelPatternChange: true`): четыре схлопывающиеся панели — **Панель навигации**, **Раздел**, **Подраздел**, **Элемент**. В каждой — свой тип узора и слайдеры.
|
||||
|
||||
| Значение `panelPattern*` | Описание |
|
||||
|--------------------------|----------|
|
||||
| `none` | Без узора — только акцентный градиент (если применим) |
|
||||
| `hex` | Соты |
|
||||
| `dots` | Точечная сетка |
|
||||
| `grid` | Линейная сетка |
|
||||
| `diagonal` | Диагональные полосы |
|
||||
| `cross` | Перекрёстная штриховка |
|
||||
| `mesh` | Точки цвета акцента |
|
||||
|
||||
По умолчанию для **навигации** и **разделов** — `hex`; для **подразделов** и **элементов** — `none`.
|
||||
|
||||
| Панель | Preference (префикс) | DOM |
|
||||
|--------|----------------------|-----|
|
||||
| Панель навигации | `panelPatternNav*` | `.hero-content` |
|
||||
| Раздел | `panelPatternSection*` | `.catalog-section` |
|
||||
| Подраздел | `panelPatternGroup*` | `.catalog-group` |
|
||||
| Элемент | `panelPatternItem*` | `.nav-card` — только узор, без акцентного градиента |
|
||||
|
||||
В каждой панели (кроме «Элемент») — шесть слайдеров: прозрачность, высота и **переход** узора; то же для градиента. Для «Элемент» — три слайдера узора (без градиента). **Переход** задаёт ширину зоны затухания: `0%` — резкий обрыв на линии «высоты», большее значение — более плавное исчезновение (при значении ≥ высоты узор растворяется от самого верха блока).
|
||||
|
||||
Старые ключи (`panelPattern`, `panelPatternOnNav`, …) при загрузке автоматически мигрируют в per-target формат, если в `localStorage` ещё нет `panelPatternNav`.
|
||||
|
||||
Значения сохраняются в `localStorage` и выставляют `data-panel-pattern-*` на `<html>` и CSS-переменные `--panel-pattern-{nav|section|group|item}-*`, `--panel-gradient-{target}-*`.
|
||||
|
||||
## Поведение интерфейса
|
||||
|
||||
### Поиск
|
||||
@ -464,7 +528,7 @@ Popup сгруппирован по разделам. Доступные бло
|
||||
|
||||
| Ключ | Содержимое |
|
||||
|------|------------|
|
||||
| `ecosystem-panel-preferences` | Тема, акцент, вид, ширина sidebar, toggle-настройки |
|
||||
| `ecosystem-panel-preferences` | Тема, акцент, узор, вид, колонки каталога, ширина sidebar, toggle-настройки |
|
||||
| `ecosystem-panel-collapse-state` | Раскрытые/свернутые ветки дерева и каталога |
|
||||
|
||||
## Разработка
|
||||
|
||||
@ -126,6 +126,11 @@
|
||||
</span>
|
||||
<input type="checkbox" data-show-resize-handles checked />
|
||||
</label>
|
||||
<label class="accent-custom-row settings-width-row">
|
||||
<span>Ширина настроек</span>
|
||||
<input type="range" data-settings-panel-width />
|
||||
<output data-settings-panel-width-value>460px</output>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<details class="settings-section settings-section-collapsible" data-settings-section="dimensions">
|
||||
@ -368,6 +373,114 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-targets-panel" data-pattern-targets-panel data-feature="allowPanelPatternChange">
|
||||
<p class="settings-section-hint">Узор и градиент настраиваются отдельно для каждого типа блоков.</p>
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="nav">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Панель навигации</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Боковое меню и дерево</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="nav" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="nav" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="nav" data-panel-pattern-field="height" /><output data-panel-pattern-value="nav" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="nav" data-panel-pattern-field="fade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="section">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Раздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки верхнего уровня каталога</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="section" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="section" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="section" data-panel-pattern-field="height" /><output data-panel-pattern-value="section" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="section" data-panel-pattern-field="fade" /><output data-panel-pattern-value="section" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="group">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Подраздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Группы внутри раздела</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="group" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="group" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="group" data-panel-pattern-field="height" /><output data-panel-pattern-value="group" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="group" data-panel-pattern-field="fade" /><output data-panel-pattern-value="group" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="item">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Элемент</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки сервисов — только узор, без акцентного градиента</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="item" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="item" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="item" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="item" data-panel-pattern-field="height" /><output data-panel-pattern-value="item" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="item" data-panel-pattern-field="fade" /><output data-panel-pattern-value="item" data-panel-pattern-field="fade">10%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<!-- Настройки пользовательского linear-gradient акцента. -->
|
||||
<div class="accent-custom-panel" data-accent-custom-panel data-feature="allowAccentChange" hidden>
|
||||
<p class="accent-custom-title">Свой градиент</p>
|
||||
|
||||
@ -11,6 +11,26 @@
|
||||
"customAccentFrom": "#8b5cf6",
|
||||
"customAccentTo": "#22d3ee",
|
||||
"customAccentAngle": 135,
|
||||
"panelPatternNav": "hex",
|
||||
"panelPatternNavOpacity": 70,
|
||||
"panelPatternNavHeight": 45,
|
||||
"panelPatternNavGradientOpacity": 80,
|
||||
"panelPatternNavGradientHeight": 40,
|
||||
"panelPatternSection": "hex",
|
||||
"panelPatternSectionOpacity": 70,
|
||||
"panelPatternSectionHeight": 45,
|
||||
"panelPatternSectionGradientOpacity": 80,
|
||||
"panelPatternSectionGradientHeight": 40,
|
||||
"panelPatternGroup": "none",
|
||||
"panelPatternGroupOpacity": 70,
|
||||
"panelPatternGroupHeight": 45,
|
||||
"panelPatternGroupGradientOpacity": 80,
|
||||
"panelPatternGroupGradientHeight": 40,
|
||||
"panelPatternItem": "none",
|
||||
"panelPatternItemOpacity": 70,
|
||||
"panelPatternItemHeight": 45,
|
||||
"panelPatternItemGradientOpacity": 80,
|
||||
"panelPatternItemGradientHeight": 40,
|
||||
"showNumbers": false,
|
||||
"showTreeTags": true,
|
||||
"showTreeType": false,
|
||||
@ -31,7 +51,8 @@
|
||||
"catalogListColumns": 1,
|
||||
"catalogCompactCardsColumns": 5,
|
||||
"catalogCompactListColumns": 3,
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер..."
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||||
"settingsPanelWidth": 460
|
||||
},
|
||||
"limits": {
|
||||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||||
@ -45,7 +66,8 @@
|
||||
"catalogCardsColumns": { "min": 1, "max": 6 },
|
||||
"catalogListColumns": { "min": 1, "max": 4 },
|
||||
"catalogCompactCardsColumns": { "min": 2, "max": 8 },
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 }
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 },
|
||||
"settingsPanelWidth": { "min": 320, "max": 720 }
|
||||
},
|
||||
"features": {
|
||||
"allowNavLayoutChange": true,
|
||||
@ -58,6 +80,7 @@
|
||||
"allowSidebarResize": true,
|
||||
"allowLayoutLimitsChange": true,
|
||||
"allowThemeChange": true,
|
||||
"allowAccentChange": true
|
||||
"allowAccentChange": true,
|
||||
"allowPanelPatternChange": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +126,11 @@
|
||||
</span>
|
||||
<input type="checkbox" data-show-resize-handles checked />
|
||||
</label>
|
||||
<label class="accent-custom-row settings-width-row">
|
||||
<span>Ширина настроек</span>
|
||||
<input type="range" data-settings-panel-width />
|
||||
<output data-settings-panel-width-value>460px</output>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<details class="settings-section settings-section-collapsible" data-settings-section="dimensions">
|
||||
@ -368,6 +373,114 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-targets-panel" data-pattern-targets-panel data-feature="allowPanelPatternChange">
|
||||
<p class="settings-section-hint">Узор и градиент настраиваются отдельно для каждого типа блоков.</p>
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="nav">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Панель навигации</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Боковое меню и дерево</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="nav" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="nav" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="nav" data-panel-pattern-field="height" /><output data-panel-pattern-value="nav" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="nav" data-panel-pattern-field="fade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="section">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Раздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки верхнего уровня каталога</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="section" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="section" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="section" data-panel-pattern-field="height" /><output data-panel-pattern-value="section" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="section" data-panel-pattern-field="fade" /><output data-panel-pattern-value="section" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="group">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Подраздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Группы внутри раздела</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="group" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="group" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="group" data-panel-pattern-field="height" /><output data-panel-pattern-value="group" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="group" data-panel-pattern-field="fade" /><output data-panel-pattern-value="group" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="item">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Элемент</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки сервисов — только узор, без акцентного градиента</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="item" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="item" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="item" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="item" data-panel-pattern-field="height" /><output data-panel-pattern-value="item" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="item" data-panel-pattern-field="fade" /><output data-panel-pattern-value="item" data-panel-pattern-field="fade">10%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<!-- Настройки пользовательского linear-gradient акцента. -->
|
||||
<div class="accent-custom-panel" data-accent-custom-panel data-feature="allowAccentChange" hidden>
|
||||
<p class="accent-custom-title">Свой градиент</p>
|
||||
|
||||
@ -11,6 +11,26 @@
|
||||
"customAccentFrom": "#8b5cf6",
|
||||
"customAccentTo": "#22d3ee",
|
||||
"customAccentAngle": 135,
|
||||
"panelPatternNav": "hex",
|
||||
"panelPatternNavOpacity": 70,
|
||||
"panelPatternNavHeight": 45,
|
||||
"panelPatternNavGradientOpacity": 80,
|
||||
"panelPatternNavGradientHeight": 40,
|
||||
"panelPatternSection": "hex",
|
||||
"panelPatternSectionOpacity": 70,
|
||||
"panelPatternSectionHeight": 45,
|
||||
"panelPatternSectionGradientOpacity": 80,
|
||||
"panelPatternSectionGradientHeight": 40,
|
||||
"panelPatternGroup": "none",
|
||||
"panelPatternGroupOpacity": 70,
|
||||
"panelPatternGroupHeight": 45,
|
||||
"panelPatternGroupGradientOpacity": 80,
|
||||
"panelPatternGroupGradientHeight": 40,
|
||||
"panelPatternItem": "none",
|
||||
"panelPatternItemOpacity": 70,
|
||||
"panelPatternItemHeight": 45,
|
||||
"panelPatternItemGradientOpacity": 80,
|
||||
"panelPatternItemGradientHeight": 40,
|
||||
"showNumbers": false,
|
||||
"showTreeTags": true,
|
||||
"showTreeType": false,
|
||||
@ -31,7 +51,8 @@
|
||||
"catalogListColumns": 1,
|
||||
"catalogCompactCardsColumns": 5,
|
||||
"catalogCompactListColumns": 3,
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер..."
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||||
"settingsPanelWidth": 460
|
||||
},
|
||||
"limits": {
|
||||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||||
@ -45,7 +66,8 @@
|
||||
"catalogCardsColumns": { "min": 1, "max": 6 },
|
||||
"catalogListColumns": { "min": 1, "max": 4 },
|
||||
"catalogCompactCardsColumns": { "min": 2, "max": 8 },
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 }
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 },
|
||||
"settingsPanelWidth": { "min": 320, "max": 720 }
|
||||
},
|
||||
"features": {
|
||||
"allowNavLayoutChange": true,
|
||||
@ -58,6 +80,7 @@
|
||||
"allowSidebarResize": true,
|
||||
"allowLayoutLimitsChange": true,
|
||||
"allowThemeChange": true,
|
||||
"allowAccentChange": true
|
||||
"allowAccentChange": true,
|
||||
"allowPanelPatternChange": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +126,11 @@
|
||||
</span>
|
||||
<input type="checkbox" data-show-resize-handles checked />
|
||||
</label>
|
||||
<label class="accent-custom-row settings-width-row">
|
||||
<span>Ширина настроек</span>
|
||||
<input type="range" data-settings-panel-width />
|
||||
<output data-settings-panel-width-value>460px</output>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<details class="settings-section settings-section-collapsible" data-settings-section="dimensions">
|
||||
@ -368,6 +373,114 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-targets-panel" data-pattern-targets-panel data-feature="allowPanelPatternChange">
|
||||
<p class="settings-section-hint">Узор и градиент настраиваются отдельно для каждого типа блоков.</p>
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="nav">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Панель навигации</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Боковое меню и дерево</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="nav" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="nav" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="nav" data-panel-pattern-field="height" /><output data-panel-pattern-value="nav" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="nav" data-panel-pattern-field="fade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="section">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Раздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки верхнего уровня каталога</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="section" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="section" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="section" data-panel-pattern-field="height" /><output data-panel-pattern-value="section" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="section" data-panel-pattern-field="fade" /><output data-panel-pattern-value="section" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="group">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Подраздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Группы внутри раздела</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="group" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="group" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="group" data-panel-pattern-field="height" /><output data-panel-pattern-value="group" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="group" data-panel-pattern-field="fade" /><output data-panel-pattern-value="group" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="item">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Элемент</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки сервисов — только узор, без акцентного градиента</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="item" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="item" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="item" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="item" data-panel-pattern-field="height" /><output data-panel-pattern-value="item" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="item" data-panel-pattern-field="fade" /><output data-panel-pattern-value="item" data-panel-pattern-field="fade">10%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<!-- Настройки пользовательского linear-gradient акцента. -->
|
||||
<div class="accent-custom-panel" data-accent-custom-panel data-feature="allowAccentChange" hidden>
|
||||
<p class="accent-custom-title">Свой градиент</p>
|
||||
|
||||
@ -11,6 +11,26 @@
|
||||
"customAccentFrom": "#8b5cf6",
|
||||
"customAccentTo": "#22d3ee",
|
||||
"customAccentAngle": 135,
|
||||
"panelPatternNav": "hex",
|
||||
"panelPatternNavOpacity": 70,
|
||||
"panelPatternNavHeight": 45,
|
||||
"panelPatternNavGradientOpacity": 80,
|
||||
"panelPatternNavGradientHeight": 40,
|
||||
"panelPatternSection": "hex",
|
||||
"panelPatternSectionOpacity": 70,
|
||||
"panelPatternSectionHeight": 45,
|
||||
"panelPatternSectionGradientOpacity": 80,
|
||||
"panelPatternSectionGradientHeight": 40,
|
||||
"panelPatternGroup": "none",
|
||||
"panelPatternGroupOpacity": 70,
|
||||
"panelPatternGroupHeight": 45,
|
||||
"panelPatternGroupGradientOpacity": 80,
|
||||
"panelPatternGroupGradientHeight": 40,
|
||||
"panelPatternItem": "none",
|
||||
"panelPatternItemOpacity": 70,
|
||||
"panelPatternItemHeight": 45,
|
||||
"panelPatternItemGradientOpacity": 80,
|
||||
"panelPatternItemGradientHeight": 40,
|
||||
"showNumbers": false,
|
||||
"showTreeTags": true,
|
||||
"showTreeType": false,
|
||||
@ -31,7 +51,8 @@
|
||||
"catalogListColumns": 1,
|
||||
"catalogCompactCardsColumns": 5,
|
||||
"catalogCompactListColumns": 3,
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер..."
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||||
"settingsPanelWidth": 460
|
||||
},
|
||||
"limits": {
|
||||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||||
@ -45,7 +66,8 @@
|
||||
"catalogCardsColumns": { "min": 1, "max": 6 },
|
||||
"catalogListColumns": { "min": 1, "max": 4 },
|
||||
"catalogCompactCardsColumns": { "min": 2, "max": 8 },
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 }
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 },
|
||||
"settingsPanelWidth": { "min": 320, "max": 720 }
|
||||
},
|
||||
"features": {
|
||||
"allowNavLayoutChange": true,
|
||||
@ -58,6 +80,7 @@
|
||||
"allowSidebarResize": true,
|
||||
"allowLayoutLimitsChange": true,
|
||||
"allowThemeChange": true,
|
||||
"allowAccentChange": true
|
||||
"allowAccentChange": true,
|
||||
"allowPanelPatternChange": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +126,11 @@
|
||||
</span>
|
||||
<input type="checkbox" data-show-resize-handles checked />
|
||||
</label>
|
||||
<label class="accent-custom-row settings-width-row">
|
||||
<span>Ширина настроек</span>
|
||||
<input type="range" data-settings-panel-width />
|
||||
<output data-settings-panel-width-value>460px</output>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<details class="settings-section settings-section-collapsible" data-settings-section="dimensions">
|
||||
@ -368,6 +373,114 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-targets-panel" data-pattern-targets-panel data-feature="allowPanelPatternChange">
|
||||
<p class="settings-section-hint">Узор и градиент настраиваются отдельно для каждого типа блоков.</p>
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="nav">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Панель навигации</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Боковое меню и дерево</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="nav" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="nav" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="nav" data-panel-pattern-field="height" /><output data-panel-pattern-value="nav" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="nav" data-panel-pattern-field="fade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="section">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Раздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки верхнего уровня каталога</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="section" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="section" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="section" data-panel-pattern-field="height" /><output data-panel-pattern-value="section" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="section" data-panel-pattern-field="fade" /><output data-panel-pattern-value="section" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="group">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Подраздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Группы внутри раздела</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="group" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="group" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="group" data-panel-pattern-field="height" /><output data-panel-pattern-value="group" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="group" data-panel-pattern-field="fade" /><output data-panel-pattern-value="group" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="item">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Элемент</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки сервисов — только узор, без акцентного градиента</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="item" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="item" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="item" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="item" data-panel-pattern-field="height" /><output data-panel-pattern-value="item" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="item" data-panel-pattern-field="fade" /><output data-panel-pattern-value="item" data-panel-pattern-field="fade">10%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<!-- Настройки пользовательского linear-gradient акцента. -->
|
||||
<div class="accent-custom-panel" data-accent-custom-panel data-feature="allowAccentChange" hidden>
|
||||
<p class="accent-custom-title">Свой градиент</p>
|
||||
|
||||
@ -11,6 +11,26 @@
|
||||
"customAccentFrom": "#8b5cf6",
|
||||
"customAccentTo": "#22d3ee",
|
||||
"customAccentAngle": 135,
|
||||
"panelPatternNav": "hex",
|
||||
"panelPatternNavOpacity": 70,
|
||||
"panelPatternNavHeight": 45,
|
||||
"panelPatternNavGradientOpacity": 80,
|
||||
"panelPatternNavGradientHeight": 40,
|
||||
"panelPatternSection": "hex",
|
||||
"panelPatternSectionOpacity": 70,
|
||||
"panelPatternSectionHeight": 45,
|
||||
"panelPatternSectionGradientOpacity": 80,
|
||||
"panelPatternSectionGradientHeight": 40,
|
||||
"panelPatternGroup": "none",
|
||||
"panelPatternGroupOpacity": 70,
|
||||
"panelPatternGroupHeight": 45,
|
||||
"panelPatternGroupGradientOpacity": 80,
|
||||
"panelPatternGroupGradientHeight": 40,
|
||||
"panelPatternItem": "none",
|
||||
"panelPatternItemOpacity": 70,
|
||||
"panelPatternItemHeight": 45,
|
||||
"panelPatternItemGradientOpacity": 80,
|
||||
"panelPatternItemGradientHeight": 40,
|
||||
"showNumbers": false,
|
||||
"showTreeTags": true,
|
||||
"showTreeType": false,
|
||||
@ -31,7 +51,8 @@
|
||||
"catalogListColumns": 1,
|
||||
"catalogCompactCardsColumns": 5,
|
||||
"catalogCompactListColumns": 3,
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер..."
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||||
"settingsPanelWidth": 460
|
||||
},
|
||||
"limits": {
|
||||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||||
@ -45,7 +66,8 @@
|
||||
"catalogCardsColumns": { "min": 1, "max": 6 },
|
||||
"catalogListColumns": { "min": 1, "max": 4 },
|
||||
"catalogCompactCardsColumns": { "min": 2, "max": 8 },
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 }
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 },
|
||||
"settingsPanelWidth": { "min": 320, "max": 720 }
|
||||
},
|
||||
"features": {
|
||||
"allowNavLayoutChange": true,
|
||||
@ -58,6 +80,7 @@
|
||||
"allowSidebarResize": true,
|
||||
"allowLayoutLimitsChange": true,
|
||||
"allowThemeChange": true,
|
||||
"allowAccentChange": true
|
||||
"allowAccentChange": true,
|
||||
"allowPanelPatternChange": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,44 @@ const defaultServiceMeta = {
|
||||
|
||||
let serviceMeta = { ...defaultServiceMeta };
|
||||
|
||||
const availablePanelPatterns = ["none", "hex", "dots", "grid", "diagonal", "cross", "mesh"];
|
||||
const panelPatternTargetIds = ["nav", "section", "group", "item"];
|
||||
const panelPatternTuningDefaults = {
|
||||
opacity: 70,
|
||||
height: 45,
|
||||
fade: 10,
|
||||
gradientOpacity: 80,
|
||||
gradientHeight: 40,
|
||||
gradientFade: 8,
|
||||
};
|
||||
const panelPatternTargetMeta = {
|
||||
nav: { defaultPattern: "hex" },
|
||||
section: { defaultPattern: "hex" },
|
||||
group: { defaultPattern: "none" },
|
||||
item: { defaultPattern: "none" },
|
||||
};
|
||||
|
||||
function capitalizePanelPatternTarget(target) {
|
||||
return target.charAt(0).toUpperCase() + target.slice(1);
|
||||
}
|
||||
|
||||
function buildDefaultPanelPatternPreferences() {
|
||||
const prefs = {};
|
||||
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
prefs[`panelPattern${cap}`] = panelPatternTargetMeta[target].defaultPattern;
|
||||
prefs[`panelPattern${cap}Opacity`] = panelPatternTuningDefaults.opacity;
|
||||
prefs[`panelPattern${cap}Height`] = panelPatternTuningDefaults.height;
|
||||
prefs[`panelPattern${cap}Fade`] = panelPatternTuningDefaults.fade;
|
||||
prefs[`panelPattern${cap}GradientOpacity`] = panelPatternTuningDefaults.gradientOpacity;
|
||||
prefs[`panelPattern${cap}GradientHeight`] = panelPatternTuningDefaults.gradientHeight;
|
||||
prefs[`panelPattern${cap}GradientFade`] = panelPatternTuningDefaults.gradientFade;
|
||||
}
|
||||
|
||||
return prefs;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Default UI preferences
|
||||
// ============================================================================
|
||||
@ -76,6 +114,7 @@ const defaultPreferences = {
|
||||
customAccentFrom: "#8b5cf6",
|
||||
customAccentTo: "#22d3ee",
|
||||
customAccentAngle: 135,
|
||||
...buildDefaultPanelPatternPreferences(),
|
||||
navLayout: "sidebar",
|
||||
showTree: true,
|
||||
showResizeHandles: true,
|
||||
@ -100,6 +139,7 @@ const defaultPreferences = {
|
||||
catalogCompactCardsColumns: 5,
|
||||
catalogCompactListColumns: 3,
|
||||
searchPlaceholder: "Grafana, S3, журнал, сервер...",
|
||||
settingsPanelWidth: 460,
|
||||
};
|
||||
|
||||
const defaultPanelFeatures = {
|
||||
@ -114,6 +154,7 @@ const defaultPanelFeatures = {
|
||||
allowLayoutLimitsChange: true,
|
||||
allowThemeChange: true,
|
||||
allowAccentChange: true,
|
||||
allowPanelPatternChange: true,
|
||||
};
|
||||
|
||||
const defaultPanelLimits = {
|
||||
@ -129,6 +170,7 @@ const defaultPanelLimits = {
|
||||
catalogListColumns: { min: 1, max: 4 },
|
||||
catalogCompactCardsColumns: { min: 2, max: 8 },
|
||||
catalogCompactListColumns: { min: 1, max: 6 },
|
||||
settingsPanelWidth: { min: 320, max: 720 },
|
||||
};
|
||||
|
||||
const layoutLimitKeys = [
|
||||
@ -149,7 +191,7 @@ const catalogColumnKeys = [
|
||||
"catalogCompactListColumns",
|
||||
];
|
||||
|
||||
const panelLimitKeys = [...layoutLimitKeys, ...catalogColumnKeys];
|
||||
const panelLimitKeys = [...layoutLimitKeys, ...catalogColumnKeys, "settingsPanelWidth"];
|
||||
|
||||
const availableNavLayouts = ["sidebar", "top"];
|
||||
const availableTreeMetaLayouts = ["inline", "stacked"];
|
||||
@ -186,6 +228,7 @@ const themeButtons = document.querySelectorAll("[data-theme]");
|
||||
const navLayoutButtons = document.querySelectorAll("[data-nav-layout]");
|
||||
const viewButtons = document.querySelectorAll("[data-view]");
|
||||
const accentButtons = document.querySelectorAll("[data-accent]");
|
||||
const patternTargetsPanel = document.querySelector("[data-pattern-targets-panel]");
|
||||
const accentCustomPanel = document.querySelector("[data-accent-custom-panel]");
|
||||
const customAccentFromInput = document.querySelector("[data-custom-accent-from]");
|
||||
const customAccentToInput = document.querySelector("[data-custom-accent-to]");
|
||||
@ -201,6 +244,8 @@ const treeColumnsInput = document.querySelector("[data-tree-columns]");
|
||||
const treeColumnsValue = document.querySelector("[data-tree-columns-value]");
|
||||
const treeMetaLayoutButtons = document.querySelectorAll("[data-tree-meta-layout]");
|
||||
const autoCollapseSearchInput = document.querySelector("[data-auto-collapse-search]");
|
||||
const settingsPanelWidthInput = document.querySelector("[data-settings-panel-width]");
|
||||
const settingsPanelWidthValue = document.querySelector("[data-settings-panel-width-value]");
|
||||
const layoutLimitInputs = document.querySelectorAll("[data-layout-limit]");
|
||||
const catalogColumnInputs = document.querySelectorAll("[data-catalog-columns]");
|
||||
const settingsSections = document.querySelectorAll("[data-settings-section]");
|
||||
@ -388,6 +433,35 @@ accentButtons.forEach((button) => {
|
||||
});
|
||||
});
|
||||
|
||||
patternTargetsPanel?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-panel-pattern-target][data-panel-pattern]");
|
||||
|
||||
if (!button || !panelFeatures.allowPanelPatternChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = button.dataset.panelPatternTarget;
|
||||
preferences[getPanelPatternPreferenceKey(target, "pattern")] = button.dataset.panelPattern;
|
||||
applyPreferences(preferences);
|
||||
savePreferences(preferences);
|
||||
});
|
||||
|
||||
patternTargetsPanel?.addEventListener("input", (event) => {
|
||||
const input = event.target.closest("[data-panel-pattern-tuning]");
|
||||
|
||||
if (!input || !panelFeatures.allowPanelPatternChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = input.dataset.panelPatternTuning;
|
||||
const field = input.dataset.panelPatternField;
|
||||
const key = getPanelPatternPreferenceKey(target, field);
|
||||
const fallback = defaultPreferences[key] ?? panelPatternTuningDefaults[field] ?? 50;
|
||||
preferences[key] = clampPanelPatternPercent(input.value, fallback);
|
||||
applyPreferences(preferences);
|
||||
savePreferences(preferences);
|
||||
});
|
||||
|
||||
// Изменение параметров своего градиента сразу переключает accent на custom.
|
||||
function handleCustomAccentChange() {
|
||||
if (!panelFeatures.allowAccentChange) {
|
||||
@ -459,6 +533,17 @@ if (treeColumnsInput) {
|
||||
treeColumnsInput.addEventListener("input", handleTreeColumnsChange);
|
||||
}
|
||||
|
||||
function handleSettingsPanelWidthChange() {
|
||||
preferences.settingsPanelWidth = clampLayoutPreference(
|
||||
"settingsPanelWidth",
|
||||
settingsPanelWidthInput?.value,
|
||||
);
|
||||
applyPreferences(preferences);
|
||||
savePreferences(preferences);
|
||||
}
|
||||
|
||||
settingsPanelWidthInput?.addEventListener("input", handleSettingsPanelWidthChange);
|
||||
|
||||
treeMetaLayoutButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
if (!panelFeatures.allowTreeOptions || !isTreeVisible(preferences)) {
|
||||
@ -625,6 +710,9 @@ function normalizePreferences(raw) {
|
||||
next.accent = defaultPreferences.accent;
|
||||
}
|
||||
|
||||
migrateLegacyPanelPatternPreferences(next);
|
||||
normalizePanelPatternPreferences(next);
|
||||
|
||||
if (!availableNavLayouts.includes(next.navLayout)) {
|
||||
next.navLayout = defaultPreferences.navLayout;
|
||||
}
|
||||
@ -659,6 +747,7 @@ function normalizePreferences(raw) {
|
||||
getTopBarMinHeight(next),
|
||||
getTopBarMaxHeight(next),
|
||||
);
|
||||
next.settingsPanelWidth = clampLayoutPreference("settingsPanelWidth", next.settingsPanelWidth);
|
||||
|
||||
return next;
|
||||
}
|
||||
@ -724,6 +813,19 @@ function enforceFeatureLocks(rawPreferences) {
|
||||
next.customAccentAngle = panelDefaults.customAccentAngle;
|
||||
}
|
||||
|
||||
if (!panelFeatures.allowPanelPatternChange) {
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
next[`panelPattern${cap}`] = panelDefaults[`panelPattern${cap}`];
|
||||
next[`panelPattern${cap}Opacity`] = panelDefaults[`panelPattern${cap}Opacity`];
|
||||
next[`panelPattern${cap}Height`] = panelDefaults[`panelPattern${cap}Height`];
|
||||
next[`panelPattern${cap}Fade`] = panelDefaults[`panelPattern${cap}Fade`];
|
||||
next[`panelPattern${cap}GradientOpacity`] = panelDefaults[`panelPattern${cap}GradientOpacity`];
|
||||
next[`panelPattern${cap}GradientHeight`] = panelDefaults[`panelPattern${cap}GradientHeight`];
|
||||
next[`panelPattern${cap}GradientFade`] = panelDefaults[`panelPattern${cap}GradientFade`];
|
||||
}
|
||||
}
|
||||
|
||||
if (!panelFeatures.allowShowNumbers) {
|
||||
next.showNumbers = panelDefaults.showNumbers;
|
||||
}
|
||||
@ -1370,6 +1472,7 @@ function applyPreferences(nextPreferences) {
|
||||
|
||||
document.documentElement.dataset.theme = activePreferences.theme;
|
||||
document.documentElement.dataset.accent = activePreferences.accent;
|
||||
applyPanelPatternPreferences(activePreferences);
|
||||
document.documentElement.dataset.navLayout =
|
||||
activePreferences.navLayout === "top" ? "top" : "sidebar";
|
||||
document.documentElement.dataset.showTree = String(isTreeVisible(activePreferences));
|
||||
@ -1391,6 +1494,10 @@ function applyPreferences(nextPreferences) {
|
||||
getTopBarMaxHeight(activePreferences),
|
||||
)}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--settings-panel-width",
|
||||
`${clampLayoutPreference("settingsPanelWidth", activePreferences.settingsPanelWidth)}px`,
|
||||
);
|
||||
const treeColumns = getTreeColumns(activePreferences);
|
||||
const sidebarMaxWidth = getSidebarMaxWidth(activePreferences);
|
||||
|
||||
@ -1461,9 +1568,11 @@ function applyPreferences(nextPreferences) {
|
||||
setActiveButton(navLayoutButtons, "navLayout", activePreferences.navLayout);
|
||||
setActiveButton(viewButtons, "view", activePreferences.view);
|
||||
setActiveButton(accentButtons, "accent", activePreferences.accent);
|
||||
syncPatternTargetPanels(activePreferences);
|
||||
setActiveButton(treeMetaLayoutButtons, "treeMetaLayout", activePreferences.treeMetaLayout);
|
||||
syncLayoutLimitInputs(activePreferences);
|
||||
syncCatalogColumnInputs(activePreferences);
|
||||
syncSettingsPanelWidthInput(activePreferences);
|
||||
applyPanelFeatures(activePreferences);
|
||||
}
|
||||
|
||||
@ -1524,7 +1633,11 @@ function isSettingsSectionVisible(sectionName, activePreferences) {
|
||||
case "tree":
|
||||
return panelFeatures.allowTreeOptions && activePreferences.showTree;
|
||||
case "appearance":
|
||||
return panelFeatures.allowThemeChange || panelFeatures.allowAccentChange;
|
||||
return (
|
||||
panelFeatures.allowThemeChange ||
|
||||
panelFeatures.allowAccentChange ||
|
||||
panelFeatures.allowPanelPatternChange
|
||||
);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@ -1776,6 +1889,22 @@ function syncLayoutLimitInputs(activePreferences) {
|
||||
});
|
||||
}
|
||||
|
||||
function syncSettingsPanelWidthInput(activePreferences) {
|
||||
if (!settingsPanelWidthInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limits = getPanelLimitRange("settingsPanelWidth");
|
||||
settingsPanelWidthInput.min = String(limits.min);
|
||||
settingsPanelWidthInput.max = String(limits.max);
|
||||
settingsPanelWidthInput.step = "10";
|
||||
settingsPanelWidthInput.value = String(activePreferences.settingsPanelWidth);
|
||||
|
||||
if (settingsPanelWidthValue) {
|
||||
settingsPanelWidthValue.textContent = `${activePreferences.settingsPanelWidth}px`;
|
||||
}
|
||||
}
|
||||
|
||||
function syncCatalogColumnInputs(activePreferences) {
|
||||
catalogColumnInputs.forEach((input) => {
|
||||
const key = input.dataset.catalogColumns;
|
||||
@ -1802,6 +1931,194 @@ function getPanelLimitRange(key) {
|
||||
return getLayoutLimitRange(key);
|
||||
}
|
||||
|
||||
function clampPanelPatternPercent(value, fallback = 50) {
|
||||
const numeric = Number(value);
|
||||
const base = Number.isFinite(numeric) ? numeric : fallback;
|
||||
return clamp(Math.round(base), 0, 100);
|
||||
}
|
||||
|
||||
function getPanelPatternPreferenceKey(target, field) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
|
||||
switch (field) {
|
||||
case "pattern":
|
||||
return `panelPattern${cap}`;
|
||||
case "opacity":
|
||||
return `panelPattern${cap}Opacity`;
|
||||
case "height":
|
||||
return `panelPattern${cap}Height`;
|
||||
case "fade":
|
||||
return `panelPattern${cap}Fade`;
|
||||
case "gradientOpacity":
|
||||
return `panelPattern${cap}GradientOpacity`;
|
||||
case "gradientHeight":
|
||||
return `panelPattern${cap}GradientHeight`;
|
||||
case "gradientFade":
|
||||
return `panelPattern${cap}GradientFade`;
|
||||
default:
|
||||
return `panelPattern${cap}`;
|
||||
}
|
||||
}
|
||||
|
||||
function migrateLegacyPanelPatternPreferences(next) {
|
||||
if (next.panelPatternNav !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const legacyPattern = availablePanelPatterns.includes(next.panelPattern)
|
||||
? next.panelPattern
|
||||
: panelPatternTargetMeta.nav.defaultPattern;
|
||||
const legacyOpacity = next.panelPatternOpacity ?? panelPatternTuningDefaults.opacity;
|
||||
const legacyHeight = next.panelPatternHeight ?? panelPatternTuningDefaults.height;
|
||||
const legacyGradientOpacity = next.panelGradientOpacity ?? panelPatternTuningDefaults.gradientOpacity;
|
||||
const legacyGradientHeight = next.panelGradientHeight ?? panelPatternTuningDefaults.gradientHeight;
|
||||
const legacyTargets = [
|
||||
["Nav", next.panelPatternOnNav !== false],
|
||||
["Section", next.panelPatternOnSection !== false],
|
||||
["Group", Boolean(next.panelPatternOnGroup)],
|
||||
["Item", Boolean(next.panelPatternOnItem)],
|
||||
];
|
||||
|
||||
for (const [suffix, enabled] of legacyTargets) {
|
||||
next[`panelPattern${suffix}`] = enabled && legacyPattern !== "none" ? legacyPattern : "none";
|
||||
next[`panelPattern${suffix}Opacity`] = legacyOpacity;
|
||||
next[`panelPattern${suffix}Height`] = legacyHeight;
|
||||
next[`panelPattern${suffix}Fade`] = panelPatternTuningDefaults.fade;
|
||||
next[`panelPattern${suffix}GradientOpacity`] = legacyGradientOpacity;
|
||||
next[`panelPattern${suffix}GradientHeight`] = legacyGradientHeight;
|
||||
next[`panelPattern${suffix}GradientFade`] = panelPatternTuningDefaults.gradientFade;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePanelPatternPreferences(next) {
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
const patternKey = `panelPattern${cap}`;
|
||||
|
||||
if (!availablePanelPatterns.includes(next[patternKey])) {
|
||||
next[patternKey] = panelPatternTargetMeta[target].defaultPattern;
|
||||
}
|
||||
|
||||
next[`panelPattern${cap}Opacity`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}Opacity`],
|
||||
panelPatternTuningDefaults.opacity,
|
||||
);
|
||||
next[`panelPattern${cap}Height`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}Height`],
|
||||
panelPatternTuningDefaults.height,
|
||||
);
|
||||
next[`panelPattern${cap}Fade`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}Fade`],
|
||||
panelPatternTuningDefaults.fade,
|
||||
);
|
||||
next[`panelPattern${cap}GradientOpacity`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}GradientOpacity`],
|
||||
panelPatternTuningDefaults.gradientOpacity,
|
||||
);
|
||||
next[`panelPattern${cap}GradientHeight`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}GradientHeight`],
|
||||
panelPatternTuningDefaults.gradientHeight,
|
||||
);
|
||||
next[`panelPattern${cap}GradientFade`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}GradientFade`],
|
||||
panelPatternTuningDefaults.gradientFade,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getPanelPatternFadeStart(heightPercent, fadePercent) {
|
||||
const height = clampPanelPatternPercent(heightPercent, panelPatternTuningDefaults.height);
|
||||
const fade = clampPanelPatternPercent(fadePercent, panelPatternTuningDefaults.fade);
|
||||
return `${Math.max(0, height - fade)}%`;
|
||||
}
|
||||
|
||||
function applyPanelPatternPreferences(activePreferences) {
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
const pattern = activePreferences[`panelPattern${cap}`] || "none";
|
||||
const height = clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}Height`],
|
||||
panelPatternTuningDefaults.height,
|
||||
);
|
||||
const opacityPercent = clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}Opacity`],
|
||||
panelPatternTuningDefaults.opacity,
|
||||
);
|
||||
const patternOpacity = pattern === "none" || height === 0 ? 0 : opacityPercent / 100;
|
||||
|
||||
document.documentElement.dataset[`panelPattern${cap}`] = pattern;
|
||||
document.documentElement.style.setProperty(`--panel-pattern-${target}-opacity`, String(patternOpacity));
|
||||
document.documentElement.style.setProperty(`--panel-pattern-${target}-height`, `${height}%`);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-pattern-${target}-fade-start`,
|
||||
getPanelPatternFadeStart(height, activePreferences[`panelPattern${cap}Fade`]),
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-gradient-${target}-strength`,
|
||||
String(
|
||||
(clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}GradientOpacity`],
|
||||
panelPatternTuningDefaults.gradientOpacity,
|
||||
) /
|
||||
100) *
|
||||
0.28,
|
||||
),
|
||||
);
|
||||
const gradientHeight = clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}GradientHeight`],
|
||||
panelPatternTuningDefaults.gradientHeight,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-gradient-${target}-height`,
|
||||
`${gradientHeight}%`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-gradient-${target}-fade-start`,
|
||||
getPanelPatternFadeStart(gradientHeight, activePreferences[`panelPattern${cap}GradientFade`]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function syncPatternTargetPanels(activePreferences) {
|
||||
if (!patternTargetsPanel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disabled = !panelFeatures.allowPanelPatternChange;
|
||||
const tuningFields = ["opacity", "height", "fade", "gradientOpacity", "gradientHeight", "gradientFade"];
|
||||
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const pattern = activePreferences[getPanelPatternPreferenceKey(target, "pattern")];
|
||||
patternTargetsPanel
|
||||
.querySelectorAll(`[data-panel-pattern-target="${target}"][data-panel-pattern]`)
|
||||
.forEach((button) => {
|
||||
const isActive = button.dataset.panelPattern === pattern;
|
||||
button.classList.toggle("is-active", isActive);
|
||||
button.setAttribute("aria-pressed", String(isActive));
|
||||
button.disabled = disabled;
|
||||
});
|
||||
|
||||
for (const field of tuningFields) {
|
||||
const key = getPanelPatternPreferenceKey(target, field);
|
||||
const input = patternTargetsPanel.querySelector(
|
||||
`[data-panel-pattern-tuning="${target}"][data-panel-pattern-field="${field}"]`,
|
||||
);
|
||||
const output = patternTargetsPanel.querySelector(
|
||||
`[data-panel-pattern-value="${target}"][data-panel-pattern-field="${field}"]`,
|
||||
);
|
||||
|
||||
if (input) {
|
||||
input.value = String(activePreferences[key]);
|
||||
input.disabled = disabled;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
output.textContent = `${activePreferences[key]}%`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clampCatalogColumn(key, value) {
|
||||
const limits = getPanelLimitRange(key);
|
||||
const fallback = panelDefaults[key] ?? defaultPreferences[key];
|
||||
|
||||
@ -31,6 +31,46 @@
|
||||
--catalog-list-columns: 1;
|
||||
--catalog-compact-cards-columns: 5;
|
||||
--catalog-compact-list-columns: 3;
|
||||
--pattern-color: rgba(255, 255, 255, 0.16);
|
||||
--pattern-tile-hex-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.18' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
--pattern-tile-hex-size: 96px 84px;
|
||||
--pattern-tile-dots-image: radial-gradient(circle at center, var(--pattern-color) 0.95px, transparent 1px);
|
||||
--pattern-tile-dots-size: 18px 18px;
|
||||
--pattern-tile-grid-image:
|
||||
linear-gradient(var(--pattern-color) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--pattern-color) 1px, transparent 1px);
|
||||
--pattern-tile-grid-size: 26px 26px;
|
||||
--pattern-tile-diagonal-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
var(--pattern-color) 10px,
|
||||
var(--pattern-color) 11px
|
||||
);
|
||||
--pattern-tile-diagonal-size: auto;
|
||||
--pattern-tile-cross-image:
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 14px,
|
||||
var(--pattern-color) 14px,
|
||||
var(--pattern-color) 15px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 14px,
|
||||
var(--pattern-color) 14px,
|
||||
var(--pattern-color) 15px
|
||||
);
|
||||
--pattern-tile-cross-size: auto;
|
||||
--pattern-tile-mesh-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
rgba(var(--accent-rgb), 0.22) 1px,
|
||||
transparent 0
|
||||
);
|
||||
--pattern-tile-mesh-size: 20px 20px;
|
||||
--settings-panel-width: 460px;
|
||||
}
|
||||
|
||||
/* Светлая тема переопределяет только токены, структура стилей остается общей. */
|
||||
@ -45,8 +85,40 @@ html[data-theme="light"] {
|
||||
--muted: #5f6b7a;
|
||||
--border: rgba(15, 23, 42, 0.12);
|
||||
--shadow: 0 24px 70px rgba(56, 65, 91, 0.18);
|
||||
--pattern-color: rgba(15, 23, 42, 0.13);
|
||||
--pattern-tile-hex-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23101827' stroke-opacity='.14' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
--pattern-tile-mesh-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
rgba(var(--accent-rgb), 0.16) 1px,
|
||||
transparent 0
|
||||
);
|
||||
}
|
||||
|
||||
html[data-panel-pattern-nav="hex"] { --panel-pattern-image-nav: var(--pattern-tile-hex-image); --panel-pattern-size-nav: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-nav="dots"] { --panel-pattern-image-nav: var(--pattern-tile-dots-image); --panel-pattern-size-nav: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-nav="grid"] { --panel-pattern-image-nav: var(--pattern-tile-grid-image); --panel-pattern-size-nav: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-nav="diagonal"] { --panel-pattern-image-nav: var(--pattern-tile-diagonal-image); --panel-pattern-size-nav: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-nav="cross"] { --panel-pattern-image-nav: var(--pattern-tile-cross-image); --panel-pattern-size-nav: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-nav="mesh"] { --panel-pattern-image-nav: var(--pattern-tile-mesh-image); --panel-pattern-size-nav: var(--pattern-tile-mesh-size); }
|
||||
html[data-panel-pattern-section="hex"] { --panel-pattern-image-section: var(--pattern-tile-hex-image); --panel-pattern-size-section: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-section="dots"] { --panel-pattern-image-section: var(--pattern-tile-dots-image); --panel-pattern-size-section: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-section="grid"] { --panel-pattern-image-section: var(--pattern-tile-grid-image); --panel-pattern-size-section: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-section="diagonal"] { --panel-pattern-image-section: var(--pattern-tile-diagonal-image); --panel-pattern-size-section: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-section="cross"] { --panel-pattern-image-section: var(--pattern-tile-cross-image); --panel-pattern-size-section: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-section="mesh"] { --panel-pattern-image-section: var(--pattern-tile-mesh-image); --panel-pattern-size-section: var(--pattern-tile-mesh-size); }
|
||||
html[data-panel-pattern-group="hex"] { --panel-pattern-image-group: var(--pattern-tile-hex-image); --panel-pattern-size-group: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-group="dots"] { --panel-pattern-image-group: var(--pattern-tile-dots-image); --panel-pattern-size-group: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-group="grid"] { --panel-pattern-image-group: var(--pattern-tile-grid-image); --panel-pattern-size-group: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-group="diagonal"] { --panel-pattern-image-group: var(--pattern-tile-diagonal-image); --panel-pattern-size-group: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-group="cross"] { --panel-pattern-image-group: var(--pattern-tile-cross-image); --panel-pattern-size-group: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-group="mesh"] { --panel-pattern-image-group: var(--pattern-tile-mesh-image); --panel-pattern-size-group: var(--pattern-tile-mesh-size); }
|
||||
html[data-panel-pattern-item="hex"] { --panel-pattern-image-item: var(--pattern-tile-hex-image); --panel-pattern-size-item: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-item="dots"] { --panel-pattern-image-item: var(--pattern-tile-dots-image); --panel-pattern-size-item: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-item="grid"] { --panel-pattern-image-item: var(--pattern-tile-grid-image); --panel-pattern-size-item: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-item="diagonal"] { --panel-pattern-image-item: var(--pattern-tile-diagonal-image); --panel-pattern-size-item: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-item="cross"] { --panel-pattern-image-item: var(--pattern-tile-cross-image); --panel-pattern-size-item: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-item="mesh"] { --panel-pattern-image-item: var(--pattern-tile-mesh-image); --panel-pattern-size-item: var(--pattern-tile-mesh-size); }
|
||||
|
||||
/* Наборы акцентов. --accent-rgb нужен для прозрачных градиентов через rgba(). */
|
||||
html[data-accent="cyan"] {
|
||||
--accent: #06b6d4;
|
||||
@ -324,6 +396,8 @@ body.is-resizing-top-bar {
|
||||
background: var(--panel);
|
||||
box-shadow: var(--shadow);
|
||||
backdrop-filter: blur(22px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
@ -335,7 +409,6 @@ body.is-resizing-top-bar {
|
||||
gap: 8px;
|
||||
padding: clamp(16px, 2.6vw, 24px);
|
||||
border-radius: 22px;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
@ -344,21 +417,117 @@ body.is-resizing-top-bar {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Внутренний фон sidebar: градиент + hexagon pattern. */
|
||||
.hero-content::before {
|
||||
/* Акцентный градиент под узором — отдельно для каждого типа блоков. */
|
||||
html:not([data-panel-pattern-nav="none"]) .hero-content::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background:
|
||||
linear-gradient(120deg, rgba(var(--accent-rgb), 0.25), transparent 34%),
|
||||
url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.10' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
mask-image: linear-gradient(to bottom, black, transparent 88%);
|
||||
background: linear-gradient(120deg, rgba(var(--accent-rgb), 1), transparent 38%);
|
||||
opacity: var(--panel-gradient-nav-strength);
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-nav-fade-start), transparent var(--panel-gradient-nav-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-nav-fade-start), transparent var(--panel-gradient-nav-height));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-section="none"]) .catalog-section::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(120deg, rgba(var(--accent-rgb), 1), transparent 38%);
|
||||
opacity: var(--panel-gradient-section-strength);
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-section-fade-start), transparent var(--panel-gradient-section-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-section-fade-start), transparent var(--panel-gradient-section-height));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-group="none"]) .catalog-group::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(120deg, rgba(var(--accent-rgb), 1), transparent 38%);
|
||||
opacity: var(--panel-gradient-group-strength);
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-group-fade-start), transparent var(--panel-gradient-group-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-group-fade-start), transparent var(--panel-gradient-group-height));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-nav="none"]) .hero-content::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-nav-opacity);
|
||||
background-image: var(--panel-pattern-image-nav);
|
||||
background-size: var(--panel-pattern-size-nav);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-nav-fade-start), transparent var(--panel-pattern-nav-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-nav-fade-start), transparent var(--panel-pattern-nav-height));
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-section="none"]) .catalog-section::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-section-opacity);
|
||||
background-image: var(--panel-pattern-image-section);
|
||||
background-size: var(--panel-pattern-size-section);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-section-fade-start), transparent var(--panel-pattern-section-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-section-fade-start), transparent var(--panel-pattern-section-height));
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-group="none"]) .catalog-group::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-group-opacity);
|
||||
background-image: var(--panel-pattern-image-group);
|
||||
background-size: var(--panel-pattern-size-group);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-group-fade-start), transparent var(--panel-pattern-group-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-group-fade-start), transparent var(--panel-pattern-group-height));
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-item="none"]) .nav-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-item-opacity);
|
||||
background-image: var(--panel-pattern-image-item);
|
||||
background-size: var(--panel-pattern-size-item);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-item-fade-start), transparent var(--panel-pattern-item-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-item-fade-start), transparent var(--panel-pattern-item-height));
|
||||
}
|
||||
|
||||
.catalog-section > summary,
|
||||
.catalog-section > .section-groups,
|
||||
.catalog-group > summary,
|
||||
.catalog-group > .items-grid {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero-content > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Общий стиль маленьких uppercase-меток и номеров. */
|
||||
@ -414,7 +583,7 @@ html[data-show-tree-tags="false"][data-show-tree-type="false"] .tree-file-meta {
|
||||
z-index: 1001;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
width: min(460px, calc(100% - 48px));
|
||||
width: min(var(--settings-panel-width), calc(100% - 48px));
|
||||
max-height: calc(100vh - 48px);
|
||||
overflow: auto;
|
||||
display: grid;
|
||||
@ -512,6 +681,27 @@ html[data-show-tree-tags="false"][data-show-tree-type="false"] .tree-file-meta {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.settings-width-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
.settings-width-row span {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.settings-width-row output {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.settings-width-row input[type="range"] {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@ -901,6 +1091,166 @@ html[data-theme="light"] .accent-swatch-custom {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.pattern-group {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.pattern-targets-panel {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pattern-target-panel .settings-section-body {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.pattern-swatches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pattern-swatch {
|
||||
min-height: 54px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.pattern-swatch::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.95;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="none"]::before {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.12), transparent 70%);
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="hex"]::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.18' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-size: 48px 42px;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .pattern-swatch[data-panel-pattern="hex"]::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23101827' stroke-opacity='.14' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="dots"]::before {
|
||||
background-image: radial-gradient(circle at center, var(--pattern-color) 0.95px, transparent 1px);
|
||||
background-size: 12px 12px;
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="grid"]::before {
|
||||
background-image:
|
||||
linear-gradient(var(--pattern-color) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--pattern-color) 1px, transparent 1px);
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="diagonal"]::before {
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 7px,
|
||||
var(--pattern-color) 7px,
|
||||
var(--pattern-color) 8px
|
||||
);
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="cross"]::before {
|
||||
background-image:
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 9px,
|
||||
var(--pattern-color) 9px,
|
||||
var(--pattern-color) 10px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 9px,
|
||||
var(--pattern-color) 9px,
|
||||
var(--pattern-color) 10px
|
||||
);
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="mesh"]::before {
|
||||
background-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
rgba(var(--accent-rgb), 0.24) 1px,
|
||||
transparent 0
|
||||
);
|
||||
background-size: 12px 12px;
|
||||
}
|
||||
|
||||
.pattern-swatch-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
place-items: end start;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 6px 8px;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.28), transparent 58%);
|
||||
color: #f8fafc;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
html[data-theme="light"] .pattern-swatch-label {
|
||||
background: linear-gradient(to top, rgba(255, 255, 255, 0.82), transparent 58%);
|
||||
color: #101827;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.pattern-swatch.is-active {
|
||||
border-color: rgba(var(--accent-rgb), 0.72);
|
||||
box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18);
|
||||
}
|
||||
|
||||
.pattern-tuning {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row span {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row output {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row input[type="range"] {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
/* Панель настройки своего градиента появляется при data-accent="custom". */
|
||||
.accent-custom-panel {
|
||||
display: grid;
|
||||
@ -1329,6 +1679,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
}
|
||||
|
||||
.catalog-group {
|
||||
position: relative;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
@ -1427,6 +1778,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
opacity: 0;
|
||||
transition: opacity 180ms ease;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Hover задается классом .is-hovered из JS, чтобы убрать скачки на границах. */
|
||||
@ -1445,6 +1797,8 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
|
||||
/* Иконка сервиса берется из serviceMeta. */
|
||||
.nav-card img {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: contain;
|
||||
@ -1453,6 +1807,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
|
||||
.card-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
}
|
||||
@ -1493,6 +1848,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 14px;
|
||||
z-index: 1;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: grid;
|
||||
@ -1515,6 +1871,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
.shine {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 2;
|
||||
background: linear-gradient(
|
||||
110deg,
|
||||
transparent 0%,
|
||||
|
||||
113
index.html
113
index.html
@ -127,6 +127,11 @@
|
||||
</span>
|
||||
<input type="checkbox" data-show-resize-handles checked />
|
||||
</label>
|
||||
<label class="accent-custom-row settings-width-row">
|
||||
<span>Ширина настроек</span>
|
||||
<input type="range" data-settings-panel-width />
|
||||
<output data-settings-panel-width-value>460px</output>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<details class="settings-section settings-section-collapsible" data-settings-section="dimensions">
|
||||
@ -369,6 +374,114 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-targets-panel" data-pattern-targets-panel data-feature="allowPanelPatternChange">
|
||||
<p class="settings-section-hint">Узор и градиент настраиваются отдельно для каждого типа блоков.</p>
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="nav">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Панель навигации</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Боковое меню и дерево</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="nav" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="nav" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="nav" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="nav" data-panel-pattern-field="height" /><output data-panel-pattern-value="nav" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="nav" data-panel-pattern-field="fade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="nav" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="nav" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="section">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Раздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки верхнего уровня каталога</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="none" aria-pressed="false"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="section" data-panel-pattern="hex" aria-pressed="true"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="section" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="section" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="section" data-panel-pattern-field="height" /><output data-panel-pattern-value="section" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="section" data-panel-pattern-field="fade" /><output data-panel-pattern-value="section" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="section" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="section" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="group">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Подраздел</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Группы внутри раздела</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="group" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="group" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="group" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="group" data-panel-pattern-field="height" /><output data-panel-pattern-value="group" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="group" data-panel-pattern-field="fade" /><output data-panel-pattern-value="group" data-panel-pattern-field="fade">10%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: прозрачность</span><input type="range" min="0" max="100" step="1" value="80" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientOpacity" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientOpacity">80%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: высота</span><input type="range" min="0" max="100" step="1" value="40" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientHeight" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientHeight">40%</output></label>
|
||||
<label class="accent-custom-row pattern-gradient-tuning"><span>Градиент: переход</span><input type="range" min="0" max="100" step="1" value="8" data-panel-pattern-tuning="group" data-panel-pattern-field="gradientFade" /><output data-panel-pattern-value="group" data-panel-pattern-field="gradientFade">8%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="settings-section-collapsible pattern-target-panel" data-pattern-target="item">
|
||||
<summary class="settings-section-summary">
|
||||
<span class="settings-section-title">Элемент</span>
|
||||
<span class="collapse-indicator" aria-hidden="true">⌄</span>
|
||||
</summary>
|
||||
<div class="settings-section-body">
|
||||
<p class="settings-section-hint">Карточки сервисов — только узор, без акцентного градиента</p>
|
||||
<div class="pattern-swatches">
|
||||
<button class="pattern-swatch is-active" type="button" data-panel-pattern-target="item" data-panel-pattern="none" aria-pressed="true"><span class="pattern-swatch-label">Нет</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="hex" aria-pressed="false"><span class="pattern-swatch-label">Соты</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="dots" aria-pressed="false"><span class="pattern-swatch-label">Точки</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="grid" aria-pressed="false"><span class="pattern-swatch-label">Сетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="diagonal" aria-pressed="false"><span class="pattern-swatch-label">Диаг.</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="cross" aria-pressed="false"><span class="pattern-swatch-label">Клетка</span></button>
|
||||
<button class="pattern-swatch" type="button" data-panel-pattern-target="item" data-panel-pattern="mesh" aria-pressed="false"><span class="pattern-swatch-label">Mesh</span></button>
|
||||
</div>
|
||||
<div class="pattern-tuning">
|
||||
<label class="accent-custom-row"><span>Узор: прозрачность</span><input type="range" min="0" max="100" step="1" value="70" data-panel-pattern-tuning="item" data-panel-pattern-field="opacity" /><output data-panel-pattern-value="item" data-panel-pattern-field="opacity">70%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: высота</span><input type="range" min="0" max="100" step="1" value="45" data-panel-pattern-tuning="item" data-panel-pattern-field="height" /><output data-panel-pattern-value="item" data-panel-pattern-field="height">45%</output></label>
|
||||
<label class="accent-custom-row"><span>Узор: переход</span><input type="range" min="0" max="100" step="1" value="10" data-panel-pattern-tuning="item" data-panel-pattern-field="fade" /><output data-panel-pattern-value="item" data-panel-pattern-field="fade">10%</output></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<!-- Настройки пользовательского linear-gradient акцента. -->
|
||||
<div class="accent-custom-panel" data-accent-custom-panel data-feature="allowAccentChange" hidden>
|
||||
<p class="accent-custom-title">Свой градиент</p>
|
||||
|
||||
321
script.js
321
script.js
@ -65,6 +65,44 @@ const defaultServiceMeta = {
|
||||
|
||||
let serviceMeta = { ...defaultServiceMeta };
|
||||
|
||||
const availablePanelPatterns = ["none", "hex", "dots", "grid", "diagonal", "cross", "mesh"];
|
||||
const panelPatternTargetIds = ["nav", "section", "group", "item"];
|
||||
const panelPatternTuningDefaults = {
|
||||
opacity: 70,
|
||||
height: 45,
|
||||
fade: 10,
|
||||
gradientOpacity: 80,
|
||||
gradientHeight: 40,
|
||||
gradientFade: 8,
|
||||
};
|
||||
const panelPatternTargetMeta = {
|
||||
nav: { defaultPattern: "hex" },
|
||||
section: { defaultPattern: "hex" },
|
||||
group: { defaultPattern: "none" },
|
||||
item: { defaultPattern: "none" },
|
||||
};
|
||||
|
||||
function capitalizePanelPatternTarget(target) {
|
||||
return target.charAt(0).toUpperCase() + target.slice(1);
|
||||
}
|
||||
|
||||
function buildDefaultPanelPatternPreferences() {
|
||||
const prefs = {};
|
||||
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
prefs[`panelPattern${cap}`] = panelPatternTargetMeta[target].defaultPattern;
|
||||
prefs[`panelPattern${cap}Opacity`] = panelPatternTuningDefaults.opacity;
|
||||
prefs[`panelPattern${cap}Height`] = panelPatternTuningDefaults.height;
|
||||
prefs[`panelPattern${cap}Fade`] = panelPatternTuningDefaults.fade;
|
||||
prefs[`panelPattern${cap}GradientOpacity`] = panelPatternTuningDefaults.gradientOpacity;
|
||||
prefs[`panelPattern${cap}GradientHeight`] = panelPatternTuningDefaults.gradientHeight;
|
||||
prefs[`panelPattern${cap}GradientFade`] = panelPatternTuningDefaults.gradientFade;
|
||||
}
|
||||
|
||||
return prefs;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Default UI preferences
|
||||
// ============================================================================
|
||||
@ -76,6 +114,7 @@ const defaultPreferences = {
|
||||
customAccentFrom: "#8b5cf6",
|
||||
customAccentTo: "#22d3ee",
|
||||
customAccentAngle: 135,
|
||||
...buildDefaultPanelPatternPreferences(),
|
||||
navLayout: "sidebar",
|
||||
showTree: true,
|
||||
showResizeHandles: true,
|
||||
@ -100,6 +139,7 @@ const defaultPreferences = {
|
||||
catalogCompactCardsColumns: 5,
|
||||
catalogCompactListColumns: 3,
|
||||
searchPlaceholder: "Grafana, S3, журнал, сервер...",
|
||||
settingsPanelWidth: 460,
|
||||
};
|
||||
|
||||
const defaultPanelFeatures = {
|
||||
@ -114,6 +154,7 @@ const defaultPanelFeatures = {
|
||||
allowLayoutLimitsChange: true,
|
||||
allowThemeChange: true,
|
||||
allowAccentChange: true,
|
||||
allowPanelPatternChange: true,
|
||||
};
|
||||
|
||||
const defaultPanelLimits = {
|
||||
@ -129,6 +170,7 @@ const defaultPanelLimits = {
|
||||
catalogListColumns: { min: 1, max: 4 },
|
||||
catalogCompactCardsColumns: { min: 2, max: 8 },
|
||||
catalogCompactListColumns: { min: 1, max: 6 },
|
||||
settingsPanelWidth: { min: 320, max: 720 },
|
||||
};
|
||||
|
||||
const layoutLimitKeys = [
|
||||
@ -149,7 +191,7 @@ const catalogColumnKeys = [
|
||||
"catalogCompactListColumns",
|
||||
];
|
||||
|
||||
const panelLimitKeys = [...layoutLimitKeys, ...catalogColumnKeys];
|
||||
const panelLimitKeys = [...layoutLimitKeys, ...catalogColumnKeys, "settingsPanelWidth"];
|
||||
|
||||
const availableNavLayouts = ["sidebar", "top"];
|
||||
const availableTreeMetaLayouts = ["inline", "stacked"];
|
||||
@ -186,6 +228,7 @@ const themeButtons = document.querySelectorAll("[data-theme]");
|
||||
const navLayoutButtons = document.querySelectorAll("[data-nav-layout]");
|
||||
const viewButtons = document.querySelectorAll("[data-view]");
|
||||
const accentButtons = document.querySelectorAll("[data-accent]");
|
||||
const patternTargetsPanel = document.querySelector("[data-pattern-targets-panel]");
|
||||
const accentCustomPanel = document.querySelector("[data-accent-custom-panel]");
|
||||
const customAccentFromInput = document.querySelector("[data-custom-accent-from]");
|
||||
const customAccentToInput = document.querySelector("[data-custom-accent-to]");
|
||||
@ -201,6 +244,8 @@ const treeColumnsInput = document.querySelector("[data-tree-columns]");
|
||||
const treeColumnsValue = document.querySelector("[data-tree-columns-value]");
|
||||
const treeMetaLayoutButtons = document.querySelectorAll("[data-tree-meta-layout]");
|
||||
const autoCollapseSearchInput = document.querySelector("[data-auto-collapse-search]");
|
||||
const settingsPanelWidthInput = document.querySelector("[data-settings-panel-width]");
|
||||
const settingsPanelWidthValue = document.querySelector("[data-settings-panel-width-value]");
|
||||
const layoutLimitInputs = document.querySelectorAll("[data-layout-limit]");
|
||||
const catalogColumnInputs = document.querySelectorAll("[data-catalog-columns]");
|
||||
const settingsSections = document.querySelectorAll("[data-settings-section]");
|
||||
@ -388,6 +433,35 @@ accentButtons.forEach((button) => {
|
||||
});
|
||||
});
|
||||
|
||||
patternTargetsPanel?.addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-panel-pattern-target][data-panel-pattern]");
|
||||
|
||||
if (!button || !panelFeatures.allowPanelPatternChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = button.dataset.panelPatternTarget;
|
||||
preferences[getPanelPatternPreferenceKey(target, "pattern")] = button.dataset.panelPattern;
|
||||
applyPreferences(preferences);
|
||||
savePreferences(preferences);
|
||||
});
|
||||
|
||||
patternTargetsPanel?.addEventListener("input", (event) => {
|
||||
const input = event.target.closest("[data-panel-pattern-tuning]");
|
||||
|
||||
if (!input || !panelFeatures.allowPanelPatternChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = input.dataset.panelPatternTuning;
|
||||
const field = input.dataset.panelPatternField;
|
||||
const key = getPanelPatternPreferenceKey(target, field);
|
||||
const fallback = defaultPreferences[key] ?? panelPatternTuningDefaults[field] ?? 50;
|
||||
preferences[key] = clampPanelPatternPercent(input.value, fallback);
|
||||
applyPreferences(preferences);
|
||||
savePreferences(preferences);
|
||||
});
|
||||
|
||||
// Изменение параметров своего градиента сразу переключает accent на custom.
|
||||
function handleCustomAccentChange() {
|
||||
if (!panelFeatures.allowAccentChange) {
|
||||
@ -459,6 +533,17 @@ if (treeColumnsInput) {
|
||||
treeColumnsInput.addEventListener("input", handleTreeColumnsChange);
|
||||
}
|
||||
|
||||
function handleSettingsPanelWidthChange() {
|
||||
preferences.settingsPanelWidth = clampLayoutPreference(
|
||||
"settingsPanelWidth",
|
||||
settingsPanelWidthInput?.value,
|
||||
);
|
||||
applyPreferences(preferences);
|
||||
savePreferences(preferences);
|
||||
}
|
||||
|
||||
settingsPanelWidthInput?.addEventListener("input", handleSettingsPanelWidthChange);
|
||||
|
||||
treeMetaLayoutButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
if (!panelFeatures.allowTreeOptions || !isTreeVisible(preferences)) {
|
||||
@ -625,6 +710,9 @@ function normalizePreferences(raw) {
|
||||
next.accent = defaultPreferences.accent;
|
||||
}
|
||||
|
||||
migrateLegacyPanelPatternPreferences(next);
|
||||
normalizePanelPatternPreferences(next);
|
||||
|
||||
if (!availableNavLayouts.includes(next.navLayout)) {
|
||||
next.navLayout = defaultPreferences.navLayout;
|
||||
}
|
||||
@ -659,6 +747,7 @@ function normalizePreferences(raw) {
|
||||
getTopBarMinHeight(next),
|
||||
getTopBarMaxHeight(next),
|
||||
);
|
||||
next.settingsPanelWidth = clampLayoutPreference("settingsPanelWidth", next.settingsPanelWidth);
|
||||
|
||||
return next;
|
||||
}
|
||||
@ -724,6 +813,19 @@ function enforceFeatureLocks(rawPreferences) {
|
||||
next.customAccentAngle = panelDefaults.customAccentAngle;
|
||||
}
|
||||
|
||||
if (!panelFeatures.allowPanelPatternChange) {
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
next[`panelPattern${cap}`] = panelDefaults[`panelPattern${cap}`];
|
||||
next[`panelPattern${cap}Opacity`] = panelDefaults[`panelPattern${cap}Opacity`];
|
||||
next[`panelPattern${cap}Height`] = panelDefaults[`panelPattern${cap}Height`];
|
||||
next[`panelPattern${cap}Fade`] = panelDefaults[`panelPattern${cap}Fade`];
|
||||
next[`panelPattern${cap}GradientOpacity`] = panelDefaults[`panelPattern${cap}GradientOpacity`];
|
||||
next[`panelPattern${cap}GradientHeight`] = panelDefaults[`panelPattern${cap}GradientHeight`];
|
||||
next[`panelPattern${cap}GradientFade`] = panelDefaults[`panelPattern${cap}GradientFade`];
|
||||
}
|
||||
}
|
||||
|
||||
if (!panelFeatures.allowShowNumbers) {
|
||||
next.showNumbers = panelDefaults.showNumbers;
|
||||
}
|
||||
@ -1370,6 +1472,7 @@ function applyPreferences(nextPreferences) {
|
||||
|
||||
document.documentElement.dataset.theme = activePreferences.theme;
|
||||
document.documentElement.dataset.accent = activePreferences.accent;
|
||||
applyPanelPatternPreferences(activePreferences);
|
||||
document.documentElement.dataset.navLayout =
|
||||
activePreferences.navLayout === "top" ? "top" : "sidebar";
|
||||
document.documentElement.dataset.showTree = String(isTreeVisible(activePreferences));
|
||||
@ -1391,6 +1494,10 @@ function applyPreferences(nextPreferences) {
|
||||
getTopBarMaxHeight(activePreferences),
|
||||
)}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--settings-panel-width",
|
||||
`${clampLayoutPreference("settingsPanelWidth", activePreferences.settingsPanelWidth)}px`,
|
||||
);
|
||||
const treeColumns = getTreeColumns(activePreferences);
|
||||
const sidebarMaxWidth = getSidebarMaxWidth(activePreferences);
|
||||
|
||||
@ -1461,9 +1568,11 @@ function applyPreferences(nextPreferences) {
|
||||
setActiveButton(navLayoutButtons, "navLayout", activePreferences.navLayout);
|
||||
setActiveButton(viewButtons, "view", activePreferences.view);
|
||||
setActiveButton(accentButtons, "accent", activePreferences.accent);
|
||||
syncPatternTargetPanels(activePreferences);
|
||||
setActiveButton(treeMetaLayoutButtons, "treeMetaLayout", activePreferences.treeMetaLayout);
|
||||
syncLayoutLimitInputs(activePreferences);
|
||||
syncCatalogColumnInputs(activePreferences);
|
||||
syncSettingsPanelWidthInput(activePreferences);
|
||||
applyPanelFeatures(activePreferences);
|
||||
}
|
||||
|
||||
@ -1524,7 +1633,11 @@ function isSettingsSectionVisible(sectionName, activePreferences) {
|
||||
case "tree":
|
||||
return panelFeatures.allowTreeOptions && activePreferences.showTree;
|
||||
case "appearance":
|
||||
return panelFeatures.allowThemeChange || panelFeatures.allowAccentChange;
|
||||
return (
|
||||
panelFeatures.allowThemeChange ||
|
||||
panelFeatures.allowAccentChange ||
|
||||
panelFeatures.allowPanelPatternChange
|
||||
);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@ -1776,6 +1889,22 @@ function syncLayoutLimitInputs(activePreferences) {
|
||||
});
|
||||
}
|
||||
|
||||
function syncSettingsPanelWidthInput(activePreferences) {
|
||||
if (!settingsPanelWidthInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limits = getPanelLimitRange("settingsPanelWidth");
|
||||
settingsPanelWidthInput.min = String(limits.min);
|
||||
settingsPanelWidthInput.max = String(limits.max);
|
||||
settingsPanelWidthInput.step = "10";
|
||||
settingsPanelWidthInput.value = String(activePreferences.settingsPanelWidth);
|
||||
|
||||
if (settingsPanelWidthValue) {
|
||||
settingsPanelWidthValue.textContent = `${activePreferences.settingsPanelWidth}px`;
|
||||
}
|
||||
}
|
||||
|
||||
function syncCatalogColumnInputs(activePreferences) {
|
||||
catalogColumnInputs.forEach((input) => {
|
||||
const key = input.dataset.catalogColumns;
|
||||
@ -1802,6 +1931,194 @@ function getPanelLimitRange(key) {
|
||||
return getLayoutLimitRange(key);
|
||||
}
|
||||
|
||||
function clampPanelPatternPercent(value, fallback = 50) {
|
||||
const numeric = Number(value);
|
||||
const base = Number.isFinite(numeric) ? numeric : fallback;
|
||||
return clamp(Math.round(base), 0, 100);
|
||||
}
|
||||
|
||||
function getPanelPatternPreferenceKey(target, field) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
|
||||
switch (field) {
|
||||
case "pattern":
|
||||
return `panelPattern${cap}`;
|
||||
case "opacity":
|
||||
return `panelPattern${cap}Opacity`;
|
||||
case "height":
|
||||
return `panelPattern${cap}Height`;
|
||||
case "fade":
|
||||
return `panelPattern${cap}Fade`;
|
||||
case "gradientOpacity":
|
||||
return `panelPattern${cap}GradientOpacity`;
|
||||
case "gradientHeight":
|
||||
return `panelPattern${cap}GradientHeight`;
|
||||
case "gradientFade":
|
||||
return `panelPattern${cap}GradientFade`;
|
||||
default:
|
||||
return `panelPattern${cap}`;
|
||||
}
|
||||
}
|
||||
|
||||
function migrateLegacyPanelPatternPreferences(next) {
|
||||
if (next.panelPatternNav !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const legacyPattern = availablePanelPatterns.includes(next.panelPattern)
|
||||
? next.panelPattern
|
||||
: panelPatternTargetMeta.nav.defaultPattern;
|
||||
const legacyOpacity = next.panelPatternOpacity ?? panelPatternTuningDefaults.opacity;
|
||||
const legacyHeight = next.panelPatternHeight ?? panelPatternTuningDefaults.height;
|
||||
const legacyGradientOpacity = next.panelGradientOpacity ?? panelPatternTuningDefaults.gradientOpacity;
|
||||
const legacyGradientHeight = next.panelGradientHeight ?? panelPatternTuningDefaults.gradientHeight;
|
||||
const legacyTargets = [
|
||||
["Nav", next.panelPatternOnNav !== false],
|
||||
["Section", next.panelPatternOnSection !== false],
|
||||
["Group", Boolean(next.panelPatternOnGroup)],
|
||||
["Item", Boolean(next.panelPatternOnItem)],
|
||||
];
|
||||
|
||||
for (const [suffix, enabled] of legacyTargets) {
|
||||
next[`panelPattern${suffix}`] = enabled && legacyPattern !== "none" ? legacyPattern : "none";
|
||||
next[`panelPattern${suffix}Opacity`] = legacyOpacity;
|
||||
next[`panelPattern${suffix}Height`] = legacyHeight;
|
||||
next[`panelPattern${suffix}Fade`] = panelPatternTuningDefaults.fade;
|
||||
next[`panelPattern${suffix}GradientOpacity`] = legacyGradientOpacity;
|
||||
next[`panelPattern${suffix}GradientHeight`] = legacyGradientHeight;
|
||||
next[`panelPattern${suffix}GradientFade`] = panelPatternTuningDefaults.gradientFade;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePanelPatternPreferences(next) {
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
const patternKey = `panelPattern${cap}`;
|
||||
|
||||
if (!availablePanelPatterns.includes(next[patternKey])) {
|
||||
next[patternKey] = panelPatternTargetMeta[target].defaultPattern;
|
||||
}
|
||||
|
||||
next[`panelPattern${cap}Opacity`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}Opacity`],
|
||||
panelPatternTuningDefaults.opacity,
|
||||
);
|
||||
next[`panelPattern${cap}Height`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}Height`],
|
||||
panelPatternTuningDefaults.height,
|
||||
);
|
||||
next[`panelPattern${cap}Fade`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}Fade`],
|
||||
panelPatternTuningDefaults.fade,
|
||||
);
|
||||
next[`panelPattern${cap}GradientOpacity`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}GradientOpacity`],
|
||||
panelPatternTuningDefaults.gradientOpacity,
|
||||
);
|
||||
next[`panelPattern${cap}GradientHeight`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}GradientHeight`],
|
||||
panelPatternTuningDefaults.gradientHeight,
|
||||
);
|
||||
next[`panelPattern${cap}GradientFade`] = clampPanelPatternPercent(
|
||||
next[`panelPattern${cap}GradientFade`],
|
||||
panelPatternTuningDefaults.gradientFade,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getPanelPatternFadeStart(heightPercent, fadePercent) {
|
||||
const height = clampPanelPatternPercent(heightPercent, panelPatternTuningDefaults.height);
|
||||
const fade = clampPanelPatternPercent(fadePercent, panelPatternTuningDefaults.fade);
|
||||
return `${Math.max(0, height - fade)}%`;
|
||||
}
|
||||
|
||||
function applyPanelPatternPreferences(activePreferences) {
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const cap = capitalizePanelPatternTarget(target);
|
||||
const pattern = activePreferences[`panelPattern${cap}`] || "none";
|
||||
const height = clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}Height`],
|
||||
panelPatternTuningDefaults.height,
|
||||
);
|
||||
const opacityPercent = clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}Opacity`],
|
||||
panelPatternTuningDefaults.opacity,
|
||||
);
|
||||
const patternOpacity = pattern === "none" || height === 0 ? 0 : opacityPercent / 100;
|
||||
|
||||
document.documentElement.dataset[`panelPattern${cap}`] = pattern;
|
||||
document.documentElement.style.setProperty(`--panel-pattern-${target}-opacity`, String(patternOpacity));
|
||||
document.documentElement.style.setProperty(`--panel-pattern-${target}-height`, `${height}%`);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-pattern-${target}-fade-start`,
|
||||
getPanelPatternFadeStart(height, activePreferences[`panelPattern${cap}Fade`]),
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-gradient-${target}-strength`,
|
||||
String(
|
||||
(clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}GradientOpacity`],
|
||||
panelPatternTuningDefaults.gradientOpacity,
|
||||
) /
|
||||
100) *
|
||||
0.28,
|
||||
),
|
||||
);
|
||||
const gradientHeight = clampPanelPatternPercent(
|
||||
activePreferences[`panelPattern${cap}GradientHeight`],
|
||||
panelPatternTuningDefaults.gradientHeight,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-gradient-${target}-height`,
|
||||
`${gradientHeight}%`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
`--panel-gradient-${target}-fade-start`,
|
||||
getPanelPatternFadeStart(gradientHeight, activePreferences[`panelPattern${cap}GradientFade`]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function syncPatternTargetPanels(activePreferences) {
|
||||
if (!patternTargetsPanel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disabled = !panelFeatures.allowPanelPatternChange;
|
||||
const tuningFields = ["opacity", "height", "fade", "gradientOpacity", "gradientHeight", "gradientFade"];
|
||||
|
||||
for (const target of panelPatternTargetIds) {
|
||||
const pattern = activePreferences[getPanelPatternPreferenceKey(target, "pattern")];
|
||||
patternTargetsPanel
|
||||
.querySelectorAll(`[data-panel-pattern-target="${target}"][data-panel-pattern]`)
|
||||
.forEach((button) => {
|
||||
const isActive = button.dataset.panelPattern === pattern;
|
||||
button.classList.toggle("is-active", isActive);
|
||||
button.setAttribute("aria-pressed", String(isActive));
|
||||
button.disabled = disabled;
|
||||
});
|
||||
|
||||
for (const field of tuningFields) {
|
||||
const key = getPanelPatternPreferenceKey(target, field);
|
||||
const input = patternTargetsPanel.querySelector(
|
||||
`[data-panel-pattern-tuning="${target}"][data-panel-pattern-field="${field}"]`,
|
||||
);
|
||||
const output = patternTargetsPanel.querySelector(
|
||||
`[data-panel-pattern-value="${target}"][data-panel-pattern-field="${field}"]`,
|
||||
);
|
||||
|
||||
if (input) {
|
||||
input.value = String(activePreferences[key]);
|
||||
input.disabled = disabled;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
output.textContent = `${activePreferences[key]}%`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clampCatalogColumn(key, value) {
|
||||
const limits = getPanelLimitRange(key);
|
||||
const fallback = panelDefaults[key] ?? defaultPreferences[key];
|
||||
|
||||
@ -11,6 +11,26 @@
|
||||
"customAccentFrom": "#8b5cf6",
|
||||
"customAccentTo": "#22d3ee",
|
||||
"customAccentAngle": 135,
|
||||
"panelPatternNav": "hex",
|
||||
"panelPatternNavOpacity": 70,
|
||||
"panelPatternNavHeight": 45,
|
||||
"panelPatternNavGradientOpacity": 80,
|
||||
"panelPatternNavGradientHeight": 40,
|
||||
"panelPatternSection": "hex",
|
||||
"panelPatternSectionOpacity": 70,
|
||||
"panelPatternSectionHeight": 45,
|
||||
"panelPatternSectionGradientOpacity": 80,
|
||||
"panelPatternSectionGradientHeight": 40,
|
||||
"panelPatternGroup": "none",
|
||||
"panelPatternGroupOpacity": 70,
|
||||
"panelPatternGroupHeight": 45,
|
||||
"panelPatternGroupGradientOpacity": 80,
|
||||
"panelPatternGroupGradientHeight": 40,
|
||||
"panelPatternItem": "none",
|
||||
"panelPatternItemOpacity": 70,
|
||||
"panelPatternItemHeight": 45,
|
||||
"panelPatternItemGradientOpacity": 80,
|
||||
"panelPatternItemGradientHeight": 40,
|
||||
"showNumbers": false,
|
||||
"showTreeTags": true,
|
||||
"showTreeType": false,
|
||||
@ -31,7 +51,8 @@
|
||||
"catalogListColumns": 1,
|
||||
"catalogCompactCardsColumns": 5,
|
||||
"catalogCompactListColumns": 3,
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер..."
|
||||
"searchPlaceholder": "Grafana, S3, журнал, сервер...",
|
||||
"settingsPanelWidth": 460
|
||||
},
|
||||
"limits": {
|
||||
"shellMaxWidth": { "min": 1200, "max": 3840 },
|
||||
@ -45,7 +66,8 @@
|
||||
"catalogCardsColumns": { "min": 1, "max": 6 },
|
||||
"catalogListColumns": { "min": 1, "max": 4 },
|
||||
"catalogCompactCardsColumns": { "min": 2, "max": 8 },
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 }
|
||||
"catalogCompactListColumns": { "min": 1, "max": 6 },
|
||||
"settingsPanelWidth": { "min": 320, "max": 720 }
|
||||
},
|
||||
"features": {
|
||||
"allowNavLayoutChange": true,
|
||||
@ -58,6 +80,7 @@
|
||||
"allowSidebarResize": true,
|
||||
"allowLayoutLimitsChange": true,
|
||||
"allowThemeChange": true,
|
||||
"allowAccentChange": true
|
||||
"allowAccentChange": true,
|
||||
"allowPanelPatternChange": true
|
||||
}
|
||||
}
|
||||
|
||||
373
styles.css
373
styles.css
@ -31,6 +31,46 @@
|
||||
--catalog-list-columns: 1;
|
||||
--catalog-compact-cards-columns: 5;
|
||||
--catalog-compact-list-columns: 3;
|
||||
--pattern-color: rgba(255, 255, 255, 0.16);
|
||||
--pattern-tile-hex-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.18' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
--pattern-tile-hex-size: 96px 84px;
|
||||
--pattern-tile-dots-image: radial-gradient(circle at center, var(--pattern-color) 0.95px, transparent 1px);
|
||||
--pattern-tile-dots-size: 18px 18px;
|
||||
--pattern-tile-grid-image:
|
||||
linear-gradient(var(--pattern-color) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--pattern-color) 1px, transparent 1px);
|
||||
--pattern-tile-grid-size: 26px 26px;
|
||||
--pattern-tile-diagonal-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 10px,
|
||||
var(--pattern-color) 10px,
|
||||
var(--pattern-color) 11px
|
||||
);
|
||||
--pattern-tile-diagonal-size: auto;
|
||||
--pattern-tile-cross-image:
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 14px,
|
||||
var(--pattern-color) 14px,
|
||||
var(--pattern-color) 15px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 14px,
|
||||
var(--pattern-color) 14px,
|
||||
var(--pattern-color) 15px
|
||||
);
|
||||
--pattern-tile-cross-size: auto;
|
||||
--pattern-tile-mesh-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
rgba(var(--accent-rgb), 0.22) 1px,
|
||||
transparent 0
|
||||
);
|
||||
--pattern-tile-mesh-size: 20px 20px;
|
||||
--settings-panel-width: 460px;
|
||||
}
|
||||
|
||||
/* Светлая тема переопределяет только токены, структура стилей остается общей. */
|
||||
@ -45,8 +85,40 @@ html[data-theme="light"] {
|
||||
--muted: #5f6b7a;
|
||||
--border: rgba(15, 23, 42, 0.12);
|
||||
--shadow: 0 24px 70px rgba(56, 65, 91, 0.18);
|
||||
--pattern-color: rgba(15, 23, 42, 0.13);
|
||||
--pattern-tile-hex-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23101827' stroke-opacity='.14' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
--pattern-tile-mesh-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
rgba(var(--accent-rgb), 0.16) 1px,
|
||||
transparent 0
|
||||
);
|
||||
}
|
||||
|
||||
html[data-panel-pattern-nav="hex"] { --panel-pattern-image-nav: var(--pattern-tile-hex-image); --panel-pattern-size-nav: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-nav="dots"] { --panel-pattern-image-nav: var(--pattern-tile-dots-image); --panel-pattern-size-nav: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-nav="grid"] { --panel-pattern-image-nav: var(--pattern-tile-grid-image); --panel-pattern-size-nav: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-nav="diagonal"] { --panel-pattern-image-nav: var(--pattern-tile-diagonal-image); --panel-pattern-size-nav: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-nav="cross"] { --panel-pattern-image-nav: var(--pattern-tile-cross-image); --panel-pattern-size-nav: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-nav="mesh"] { --panel-pattern-image-nav: var(--pattern-tile-mesh-image); --panel-pattern-size-nav: var(--pattern-tile-mesh-size); }
|
||||
html[data-panel-pattern-section="hex"] { --panel-pattern-image-section: var(--pattern-tile-hex-image); --panel-pattern-size-section: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-section="dots"] { --panel-pattern-image-section: var(--pattern-tile-dots-image); --panel-pattern-size-section: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-section="grid"] { --panel-pattern-image-section: var(--pattern-tile-grid-image); --panel-pattern-size-section: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-section="diagonal"] { --panel-pattern-image-section: var(--pattern-tile-diagonal-image); --panel-pattern-size-section: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-section="cross"] { --panel-pattern-image-section: var(--pattern-tile-cross-image); --panel-pattern-size-section: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-section="mesh"] { --panel-pattern-image-section: var(--pattern-tile-mesh-image); --panel-pattern-size-section: var(--pattern-tile-mesh-size); }
|
||||
html[data-panel-pattern-group="hex"] { --panel-pattern-image-group: var(--pattern-tile-hex-image); --panel-pattern-size-group: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-group="dots"] { --panel-pattern-image-group: var(--pattern-tile-dots-image); --panel-pattern-size-group: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-group="grid"] { --panel-pattern-image-group: var(--pattern-tile-grid-image); --panel-pattern-size-group: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-group="diagonal"] { --panel-pattern-image-group: var(--pattern-tile-diagonal-image); --panel-pattern-size-group: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-group="cross"] { --panel-pattern-image-group: var(--pattern-tile-cross-image); --panel-pattern-size-group: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-group="mesh"] { --panel-pattern-image-group: var(--pattern-tile-mesh-image); --panel-pattern-size-group: var(--pattern-tile-mesh-size); }
|
||||
html[data-panel-pattern-item="hex"] { --panel-pattern-image-item: var(--pattern-tile-hex-image); --panel-pattern-size-item: var(--pattern-tile-hex-size); }
|
||||
html[data-panel-pattern-item="dots"] { --panel-pattern-image-item: var(--pattern-tile-dots-image); --panel-pattern-size-item: var(--pattern-tile-dots-size); }
|
||||
html[data-panel-pattern-item="grid"] { --panel-pattern-image-item: var(--pattern-tile-grid-image); --panel-pattern-size-item: var(--pattern-tile-grid-size); }
|
||||
html[data-panel-pattern-item="diagonal"] { --panel-pattern-image-item: var(--pattern-tile-diagonal-image); --panel-pattern-size-item: var(--pattern-tile-diagonal-size); }
|
||||
html[data-panel-pattern-item="cross"] { --panel-pattern-image-item: var(--pattern-tile-cross-image); --panel-pattern-size-item: var(--pattern-tile-cross-size); }
|
||||
html[data-panel-pattern-item="mesh"] { --panel-pattern-image-item: var(--pattern-tile-mesh-image); --panel-pattern-size-item: var(--pattern-tile-mesh-size); }
|
||||
|
||||
/* Наборы акцентов. --accent-rgb нужен для прозрачных градиентов через rgba(). */
|
||||
html[data-accent="cyan"] {
|
||||
--accent: #06b6d4;
|
||||
@ -324,6 +396,8 @@ body.is-resizing-top-bar {
|
||||
background: var(--panel);
|
||||
box-shadow: var(--shadow);
|
||||
backdrop-filter: blur(22px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
@ -335,7 +409,6 @@ body.is-resizing-top-bar {
|
||||
gap: 8px;
|
||||
padding: clamp(16px, 2.6vw, 24px);
|
||||
border-radius: 22px;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
@ -344,21 +417,117 @@ body.is-resizing-top-bar {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Внутренний фон sidebar: градиент + hexagon pattern. */
|
||||
.hero-content::before {
|
||||
/* Акцентный градиент под узором — отдельно для каждого типа блоков. */
|
||||
html:not([data-panel-pattern-nav="none"]) .hero-content::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background:
|
||||
linear-gradient(120deg, rgba(var(--accent-rgb), 0.25), transparent 34%),
|
||||
url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.10' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
mask-image: linear-gradient(to bottom, black, transparent 88%);
|
||||
background: linear-gradient(120deg, rgba(var(--accent-rgb), 1), transparent 38%);
|
||||
opacity: var(--panel-gradient-nav-strength);
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-nav-fade-start), transparent var(--panel-gradient-nav-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-nav-fade-start), transparent var(--panel-gradient-nav-height));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-section="none"]) .catalog-section::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(120deg, rgba(var(--accent-rgb), 1), transparent 38%);
|
||||
opacity: var(--panel-gradient-section-strength);
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-section-fade-start), transparent var(--panel-gradient-section-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-section-fade-start), transparent var(--panel-gradient-section-height));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-group="none"]) .catalog-group::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: linear-gradient(120deg, rgba(var(--accent-rgb), 1), transparent 38%);
|
||||
opacity: var(--panel-gradient-group-strength);
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-group-fade-start), transparent var(--panel-gradient-group-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-gradient-group-fade-start), transparent var(--panel-gradient-group-height));
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-nav="none"]) .hero-content::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-nav-opacity);
|
||||
background-image: var(--panel-pattern-image-nav);
|
||||
background-size: var(--panel-pattern-size-nav);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-nav-fade-start), transparent var(--panel-pattern-nav-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-nav-fade-start), transparent var(--panel-pattern-nav-height));
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-section="none"]) .catalog-section::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-section-opacity);
|
||||
background-image: var(--panel-pattern-image-section);
|
||||
background-size: var(--panel-pattern-size-section);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-section-fade-start), transparent var(--panel-pattern-section-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-section-fade-start), transparent var(--panel-pattern-section-height));
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-group="none"]) .catalog-group::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-group-opacity);
|
||||
background-image: var(--panel-pattern-image-group);
|
||||
background-size: var(--panel-pattern-size-group);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-group-fade-start), transparent var(--panel-pattern-group-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-group-fade-start), transparent var(--panel-pattern-group-height));
|
||||
}
|
||||
|
||||
html:not([data-panel-pattern-item="none"]) .nav-card::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: var(--panel-pattern-item-opacity);
|
||||
background-image: var(--panel-pattern-image-item);
|
||||
background-size: var(--panel-pattern-size-item);
|
||||
background-repeat: repeat;
|
||||
mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-item-fade-start), transparent var(--panel-pattern-item-height));
|
||||
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 var(--panel-pattern-item-fade-start), transparent var(--panel-pattern-item-height));
|
||||
}
|
||||
|
||||
.catalog-section > summary,
|
||||
.catalog-section > .section-groups,
|
||||
.catalog-group > summary,
|
||||
.catalog-group > .items-grid {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero-content > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Общий стиль маленьких uppercase-меток и номеров. */
|
||||
@ -414,7 +583,7 @@ html[data-show-tree-tags="false"][data-show-tree-type="false"] .tree-file-meta {
|
||||
z-index: 1001;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
width: min(460px, calc(100% - 48px));
|
||||
width: min(var(--settings-panel-width), calc(100% - 48px));
|
||||
max-height: calc(100vh - 48px);
|
||||
overflow: auto;
|
||||
display: grid;
|
||||
@ -512,6 +681,27 @@ html[data-show-tree-tags="false"][data-show-tree-type="false"] .tree-file-meta {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.settings-width-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
.settings-width-row span {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.settings-width-row output {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.settings-width-row input[type="range"] {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
@ -901,6 +1091,166 @@ html[data-theme="light"] .accent-swatch-custom {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.pattern-group {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.pattern-targets-panel {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pattern-target-panel .settings-section-body {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.pattern-swatches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pattern-swatch {
|
||||
min-height: 54px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.pattern-swatch::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.95;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="none"]::before {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.12), transparent 70%);
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="hex"]::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='white' stroke-opacity='.18' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-size: 48px 42px;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .pattern-swatch[data-panel-pattern="hex"]::before {
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='96' height='84' viewBox='0 0 96 84' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' stroke='%23101827' stroke-opacity='.14' stroke-width='1.4'%3E%3Cpath d='M24 2 46 14v25L24 52 2 39V14L24 2Z'/%3E%3Cpath d='M72 32 94 44v25L72 82 50 69V44l22-12Z'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="dots"]::before {
|
||||
background-image: radial-gradient(circle at center, var(--pattern-color) 0.95px, transparent 1px);
|
||||
background-size: 12px 12px;
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="grid"]::before {
|
||||
background-image:
|
||||
linear-gradient(var(--pattern-color) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--pattern-color) 1px, transparent 1px);
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="diagonal"]::before {
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 7px,
|
||||
var(--pattern-color) 7px,
|
||||
var(--pattern-color) 8px
|
||||
);
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="cross"]::before {
|
||||
background-image:
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 9px,
|
||||
var(--pattern-color) 9px,
|
||||
var(--pattern-color) 10px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
transparent 9px,
|
||||
var(--pattern-color) 9px,
|
||||
var(--pattern-color) 10px
|
||||
);
|
||||
}
|
||||
|
||||
.pattern-swatch[data-panel-pattern="mesh"]::before {
|
||||
background-image: radial-gradient(
|
||||
circle at 1px 1px,
|
||||
rgba(var(--accent-rgb), 0.24) 1px,
|
||||
transparent 0
|
||||
);
|
||||
background-size: 12px 12px;
|
||||
}
|
||||
|
||||
.pattern-swatch-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
place-items: end start;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 6px 8px;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.28), transparent 58%);
|
||||
color: #f8fafc;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
html[data-theme="light"] .pattern-swatch-label {
|
||||
background: linear-gradient(to top, rgba(255, 255, 255, 0.82), transparent 58%);
|
||||
color: #101827;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.pattern-swatch.is-active {
|
||||
border-color: rgba(var(--accent-rgb), 0.72);
|
||||
box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18);
|
||||
}
|
||||
|
||||
.pattern-tuning {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-rows: auto auto;
|
||||
gap: 6px 10px;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row span {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row output {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.pattern-tuning .accent-custom-row input[type="range"] {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
/* Панель настройки своего градиента появляется при data-accent="custom". */
|
||||
.accent-custom-panel {
|
||||
display: grid;
|
||||
@ -1329,6 +1679,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
}
|
||||
|
||||
.catalog-group {
|
||||
position: relative;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
@ -1427,6 +1778,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
opacity: 0;
|
||||
transition: opacity 180ms ease;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Hover задается классом .is-hovered из JS, чтобы убрать скачки на границах. */
|
||||
@ -1445,6 +1797,8 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
|
||||
/* Иконка сервиса берется из serviceMeta. */
|
||||
.nav-card img {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
object-fit: contain;
|
||||
@ -1453,6 +1807,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
|
||||
.card-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
}
|
||||
@ -1493,6 +1848,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
bottom: 14px;
|
||||
z-index: 1;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: grid;
|
||||
@ -1515,6 +1871,7 @@ html:not([data-tree-columns="1"]) .tree-node-icon {
|
||||
.shine {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 2;
|
||||
background: linear-gradient(
|
||||
110deg,
|
||||
transparent 0%,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user