# PeriodChart Component

## Описание

Столбчатый график с переключателем уровней детализации (например Год / Месяц / Сутки / Час) —
универсальный компонент общего назначения, не привязан к конкретному домену данных. Принимает
набор уровней (`levels`), каждый со своим массивом значений и подписей столбиков; переключение
между уровнями меняет только то, какой массив рисуется — сам компонент ничего не запрашивает,
данные всех уровней передаются через пропсы целиком (как `historyData`/`streamingData` у `Graph`).

## Пропсы

| Название            | Тип                                                 | По умолчанию                        | Описание                                                                                                                                                                  |
| ------------------- | --------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                | `string`                                            | `crypto.randomUUID()`               | Уникальный идентификатор компонента                                                                                                                                       |
| `wrapperClass`      | `string`                                            | `"bg-blue"`                         | CSS-классы обёртки — через `bg-*` задаёт цвет переключателя и столбиков                                                                                                   |
| `label`             | `{ name?: string; class?: string }`                 | `{ name: "", class: "" }`           | Заголовок над графиком                                                                                                                                                    |
| `levels`            | `IPeriodChartLevel[]`                               | 4 демо-уровня со случайными данными | Уровни детализации, порядок = порядок вкладок переключателя; открывается всегда на первом (индекс 0)                                                                      |
| `levels[].name`     | `string`                                            | `-`                                 | Подпись вкладки, напр. `"Час"`                                                                                                                                            |
| `levels[].variable` | `string`                                            | `undefined`                         | Имя переменной устройства, откуда берутся `data` этого уровня, напр. `"PWR.EHour"` — используется конструктором для подписки, сам компонент по нему ничего не запрашивает |
| `levels[].data`     | `number[]`                                          | `-`                                 | Значения — длина массива = число столбиков, ничем не ограничена                                                                                                           |
| `levels[].labels`   | `string[]`                                          | `undefined`                         | Подписи столбиков по X; если не заданы — используется индекс+1                                                                                                            |
| `unit`              | `string`                                            | `""`                                | Единицы измерения, добавляются к значению в `aria-label`, напр. `"Вт·ч"`                                                                                                  |
| `onLevelChange`     | `(index: number, level: IPeriodChartLevel) => void` | `() => {}`                          | Вызывается при переключении вкладки — только уведомление, без фетча внутри                                                                                                |

## События

Компонент не генерирует DOM-события — используйте колбэк `onLevelChange` для реакции на смену
уровня (например чтобы догрузить свежие данные для только что выбранного уровня).

## Примеры

### Базовое использование (демо-данные)

```svelte
<script>
  import * as UI from "poe-svelte-ui-lib"
</script>

<UI.PeriodChart label={{ name: "Расход энергии" }} />
```

### Реальные данные с устройства (например BL0910: PWR.EYear/EMonth/EDay/EHour)

`variable` в каждом уровне — это имя Cfg-ключа устройства (`PWR.EHour` и т.п.), по которому
конструктор облака подписывается через `eventHandler.Variables` и подставляет актуальный `data`;
сам компонент значение `variable` не читает, только рисует уже готовый `data`.

```svelte
<script>
  import * as UI from "poe-svelte-ui-lib"

  let levels = [
    {
      name: "Год",
      variable: "PWR.EYear",
      data: deviceData.EYear,
      labels: ["янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"],
    },
    { name: "Месяц", variable: "PWR.EMonth", data: deviceData.EMonth },
    { name: "Сутки", variable: "PWR.EDay", data: deviceData.EDay },
    { name: "Час", variable: "PWR.EHour", data: deviceData.EHour, labels: ["0-10", "10-20", "20-30", "30-40", "40-50", "50-60"] },
  ]
</script>

<UI.PeriodChart {levels} unit=" Вт·ч" onLevelChange={(index, level) => console.log("Переключились на", level.name)} />
```

## Внутренняя архитектура

### Реактивность

- `currentLevelIndex` (`$state`) — индекс активной вкладки, всегда инициализируется `0` (первый уровень)
- `currentLevel`/`values` (`$derived`) — текущий уровень и его массив значений
- `maxValue` (`$derived`) — максимум текущего массива с отступом ×1.1 для автомасштаба высоты столбиков

### Сторы и зависимости

- Используется `twMerge` для объединения Tailwind CSS классов

### Директивы

- `#each` для отображения вкладок переключателя, столбиков графика и подписей по X
- `#if` для пустого состояния (уровень без данных)

### Слоты

- Компонент не использует внешних слотов

## Заметки

### Адаптивность

- Столбики растягиваются на всю ширину контейнера (`flex-1` на каждый), высота — в процентах от
  зоны столбиков фиксированной высоты (`h-40`); значение (округлено до целого, `Math.round`)
  выводится прямо внутри столбика, по центру — видно всегда, без наведения. Для маленьких значений
  столбик слишком низкий, чтобы число влезло внутрь (`overflow-hidden` его обрежет) — если высота
  столбика меньше `MIN_LABEL_PX` (18px из `BAR_ZONE_PX`=160px = `h-40`), число рисуется НАД
  столбиком вместо "внутри". Подписи (`labels[i]`/индекс+1) выводятся отдельной строкой под этой
  зоной, тоже без наведения
- Никакого тултипа — компонент не реагирует на hover вообще
- Полностью на CSS-переменных темы (`--back-color`, `--border-color`, `--shadow-color`,
  `--bg-color` через `wrapperClass`) — тёмная/светлая тема не требует отдельной настройки

### Ограничения

- Компонент презентационный — не хранит историю, не опрашивает устройство сам; при смене
  `levels` извне (например после ответа на `onLevelChange`) график перерисовывается автоматически
- Значение внутри столбика округляется до целого только для отображения (`Math.round`) — исходные
  дробные данные в `levels[].data` не изменяются; `unit` в самом столбике не показывается (мало
  места), только в `aria-label` для доступности

### Производительность

- Никакого `<canvas>` — чистый HTML/CSS, подходит для умеренного числа столбиков (проверено на
  вплоть до 31); для сотен/тысяч точек стоит использовать `Graph`

## Конструктор свойств (PeriodChartProps.svelte)

### Описание

Компонент `PeriodChartProps.svelte` предоставляет визуальный интерфейс для редактирования свойств
`PeriodChart`. Поддерживает два режима отображения: для конструктора и для редактирования —
идентично остальным компонентам библиотеки (`ProgressBarProps`, `TabsProps`).

### Пропсы конструктора

| Название           | Тип                                                                                                                                 | По умолчанию | Описание                                                |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------- |
| `component`        | `UIComponent & { properties: Partial<IPeriodChartProps> }`                                                                          | `-`          | Объект компонента с его свойствами                      |
| `onPropertyChange` | `(updates: Partial<{ properties?: string \| object; name?: string; access?: string; eventHandler?: IUIComponentHandler }>) => void` | `-`          | Коллбэк для обновления свойств компонента               |
| `forConstructor`   | `boolean`                                                                                                                           | `true`       | Режим отображения (для конструктора или редактирования) |

### Особенности конструктора

- Общие поля через `CommonSnippets`: `Access` (без `viewOnly` — компонент чисто отображающий, как
  `Graph`/`Tabs`/`ProgressBar`), `Colors` (пишет в `wrapperClass`, тот же цвет уходит на
  переключатель и столбики), `Label`; в режиме редактирования дополнительно `Identificator`,
  `WrapperClass`
- `unit` — отдельное текстовое поле ввода
- Редактор уровней (`levels[]`) — тот же паттерн повторяющегося списка, что у `TabsSettings`/
  `ProgressBarOptions`: перетаскивание (`UI.Dragging`) для порядка вкладок, добавление/удаление
  через `ButtonAdd`/`ButtonDelete`, на каждый уровень — поля `name`, `variable`, `data` и
  `labels`. `variable` — тот же `Select` по списку переменных устройства (`DeviceVariables` из
  контекста), что и у `Graph`/`CommonSnippets.Variable`, только по одному на каждый уровень; при
  любом изменении списка уровней редактор пересобирает `eventHandler.Variables` как объединение
  всех непустых `variable` со всех уровней — так рантайм подписывается сразу на все нужные ключи.
  `data`/`labels` остаются полями для литеральных демо-данных — массивы чисел/строк вводятся одной
  строкой через запятую (`"10, 20, 30"`) и парсятся на лету

### Использование

```svelte
<script>
  import * as UI from "poe-svelte-ui-lib"

  let component = $state({
    id: crypto.randomUUID(),
    type: "PeriodChart",
    access: "full",
    properties: { levels: [{ name: "Час", data: [1, 2, 3, 4, 5, 6] }] },
    position: { row: 0, col: 0, width: 0, height: 0 },
    parentId: "",
  })
</script>

<UI.PeriodChartProps {component} onPropertyChange={(updates) => (component = { ...component, ...updates })} forConstructor={true} />
```
