@elcrm/components 0.2.13 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/README.md +76 -38
  2. package/dist/ActionGroup-DcApj_Mz.js +27 -0
  3. package/dist/ActionGroup.css +1 -1
  4. package/dist/ActionGroup.d.ts +19 -19
  5. package/dist/ActionGroup.js +1 -1
  6. package/dist/Avatar-eGdLrMCv.js +113 -0
  7. package/dist/Avatar.css +1 -1
  8. package/dist/Avatar.d.ts +4 -4
  9. package/dist/Avatar.js +1 -1
  10. package/dist/AvatarGroup-Dq-UrOaC.js +81 -0
  11. package/dist/AvatarGroup.css +1 -1
  12. package/dist/AvatarGroup.d.ts +8 -28
  13. package/dist/AvatarGroup.js +1 -1
  14. package/dist/AvatarName-3B4yCxHn.js +68 -0
  15. package/dist/AvatarName.css +1 -1
  16. package/dist/AvatarName.js +1 -1
  17. package/dist/Badge-BjScAXGy.js +28 -0
  18. package/dist/Badge.css +1 -1
  19. package/dist/Badge.js +1 -1
  20. package/dist/Banner-DAY18FWO.js +34 -0
  21. package/dist/Banner.css +1 -1
  22. package/dist/Banner.js +1 -1
  23. package/dist/Brand-BfCuiyf7.js +34 -0
  24. package/dist/Brand.css +1 -1
  25. package/dist/Brand.d.ts +8 -10
  26. package/dist/Brand.js +1 -1
  27. package/dist/Breadcrumb-DM_hWw5n.js +98 -0
  28. package/dist/Breadcrumb.css +1 -1
  29. package/dist/Breadcrumb.d.ts +8 -5
  30. package/dist/Breadcrumb.js +1 -1
  31. package/dist/Card-a7N8Uh_H.js +12 -0
  32. package/dist/Card.css +1 -1
  33. package/dist/Card.js +1 -1
  34. package/dist/ChatFrame-BdSYRF-B.js +37 -0
  35. package/dist/ChatFrame.css +1 -1
  36. package/dist/ChatList.js +2 -2
  37. package/dist/{ChatMessages-B8rdXiK-.js → ChatMessages-CLxwRGIa.js} +1 -1
  38. package/dist/ChatMessages.js +1 -1
  39. package/dist/ChatSplit-8iw_1Shp.js +21 -0
  40. package/dist/CodeBlock-B3wvMejv.js +34 -0
  41. package/dist/CodeBlock.css +1 -1
  42. package/dist/CodeBlock.js +1 -1
  43. package/dist/Dropdown-CR_XxNak.js +185 -0
  44. package/dist/Dropdown.css +1 -1
  45. package/dist/Dropdown.d.ts +4 -94
  46. package/dist/Dropdown.js +1 -1
  47. package/dist/EmptyState-B_wWPjUu.js +23 -0
  48. package/dist/EmptyState.css +1 -1
  49. package/dist/EmptyState.d.ts +1 -1
  50. package/dist/EmptyState.js +1 -1
  51. package/dist/{Footer-qxUlnPR9.js → Footer-Bgh_JCUe.js} +4 -4
  52. package/dist/Footer.css +1 -1
  53. package/dist/Footer.js +1 -1
  54. package/dist/Header-BMDIi3u2.js +21 -0
  55. package/dist/Header.css +1 -1
  56. package/dist/Header.js +1 -1
  57. package/dist/Healthmap-Z-LLfGW0.js +359 -0
  58. package/dist/Healthmap.css +1 -1
  59. package/dist/Healthmap.d.ts +25 -3
  60. package/dist/Healthmap.js +1 -1
  61. package/dist/{Item-D4okhsfW.js → Item-CGpkMJu0.js} +13 -13
  62. package/dist/Item.css +1 -1
  63. package/dist/Item.d.ts +7 -7
  64. package/dist/Item.js +1 -1
  65. package/dist/ItemColumn-Ch61LqE0.js +41 -0
  66. package/dist/ItemColumn.css +1 -1
  67. package/dist/ItemColumn.js +1 -1
  68. package/dist/Layout-CzEGKl9s.js +31 -0
  69. package/dist/Layout.css +1 -1
  70. package/dist/Layout.d.ts +1 -1
  71. package/dist/Layout.js +1 -1
  72. package/dist/List-D13pqaUY.js +12 -0
  73. package/dist/List.css +1 -1
  74. package/dist/List.js +1 -1
  75. package/dist/Loading-C_CKD-zt.js +16 -0
  76. package/dist/Loading.css +1 -1
  77. package/dist/Loading.d.ts +1 -1
  78. package/dist/Loading.js +1 -1
  79. package/dist/Menu-D2kf7fN_.js +72 -0
  80. package/dist/Menu.css +1 -1
  81. package/dist/Menu.d.ts +7 -25
  82. package/dist/Menu.js +1 -1
  83. package/dist/NavSections-CNxxBMPT.js +154 -0
  84. package/dist/NavSections.css +1 -1
  85. package/dist/NavSections.d.ts +16 -28
  86. package/dist/NavSections.js +1 -1
  87. package/dist/PageHead-BfClTLrF.js +31 -0
  88. package/dist/PageHead.css +1 -1
  89. package/dist/PageHead.d.ts +1 -1
  90. package/dist/PageHead.js +1 -1
  91. package/dist/PageShell-BAJges-G.js +30 -0
  92. package/dist/PageShell.css +1 -1
  93. package/dist/PageShell.js +1 -1
  94. package/dist/PanelInfo-Db5GCr-c.js +137 -0
  95. package/dist/PanelInfo.css +1 -1
  96. package/dist/PanelInfo.d.ts +16 -9
  97. package/dist/PanelInfo.js +1 -1
  98. package/dist/Popover-xEZoMY-h.js +143 -0
  99. package/dist/Popover.css +1 -1
  100. package/dist/Popover.d.ts +40 -11
  101. package/dist/Popover.js +1 -1
  102. package/dist/PricingGroup-CWy47MlE.js +138 -0
  103. package/dist/PricingGroup.css +1 -1
  104. package/dist/PricingGroup.d.ts +0 -2
  105. package/dist/PricingGroup.js +1 -1
  106. package/dist/PricingTable-BjpGVAhi.js +157 -0
  107. package/dist/PricingTable.css +1 -1
  108. package/dist/PricingTable.d.ts +4 -7
  109. package/dist/PricingTable.js +1 -1
  110. package/dist/RadioGroup-DI4KKUvZ.js +121 -0
  111. package/dist/RadioGroup.css +1 -1
  112. package/dist/RadioGroup.js +1 -1
  113. package/dist/Section-PprWjOvt.js +47 -0
  114. package/dist/Section.css +1 -1
  115. package/dist/Section.d.ts +29 -9
  116. package/dist/Section.js +1 -1
  117. package/dist/Sidebar-Bouhd5SS.js +205 -0
  118. package/dist/Sidebar.css +1 -1
  119. package/dist/Sidebar.d.ts +18 -26
  120. package/dist/Sidebar.js +1 -1
  121. package/dist/SplitPane-DBa-g9td.js +92 -0
  122. package/dist/SplitPane.css +1 -1
  123. package/dist/SplitPane.d.ts +8 -7
  124. package/dist/SplitPane.js +1 -1
  125. package/dist/Stack-BPTehVYc.js +53 -0
  126. package/dist/Stack.css +1 -1
  127. package/dist/Stack.d.ts +2 -1
  128. package/dist/Stack.js +1 -1
  129. package/dist/Stat-p8j4PjrC.js +28 -0
  130. package/dist/Stat.css +1 -1
  131. package/dist/Stat.js +1 -1
  132. package/dist/TabSections-Dppe9wiz.js +84 -0
  133. package/dist/TabSections.css +1 -1
  134. package/dist/TabSections.d.ts +7 -2
  135. package/dist/TabSections.js +1 -1
  136. package/dist/Table-DqrDUUtb.js +27 -0
  137. package/dist/Table.css +1 -1
  138. package/dist/Table.d.ts +10 -2
  139. package/dist/Table.js +1 -1
  140. package/dist/Text-Bo1u7gVC.js +25 -0
  141. package/dist/Text.css +1 -0
  142. package/dist/Text.d.ts +19 -0
  143. package/dist/Text.js +4 -0
  144. package/dist/TextGroup-236jHxdT.js +23 -0
  145. package/dist/TextGroup.css +1 -1
  146. package/dist/TextGroup.js +1 -1
  147. package/dist/Toolbar-Bl9-5Mis.js +35 -0
  148. package/dist/Toolbar.css +1 -1
  149. package/dist/Toolbar.d.ts +7 -12
  150. package/dist/Toolbar.js +1 -2
  151. package/dist/cx.d.ts +1 -1
  152. package/dist/dark.css +12 -0
  153. package/dist/index.css +41 -39
  154. package/dist/index.d.ts +7 -12
  155. package/dist/index.es.d.ts +7 -12
  156. package/dist/index.es.js +110 -107
  157. package/dist/kit-manifest.json +1646 -0
  158. package/dist/lazyRoute.d.ts +12 -6
  159. package/dist/light.css +12 -0
  160. package/dist/navTree.d.ts +13 -1
  161. package/dist/rovingKeys-Di9N1Nct.js +23 -0
  162. package/dist/rovingKeys.d.ts +7 -1
  163. package/dist/stackGap.d.ts +1 -1
  164. package/dist/tokens.css +20 -2
  165. package/dist/useControllableValue-BaE0VR5d.js +13 -0
  166. package/dist/useControllableValue.d.ts +3 -0
  167. package/package.json +48 -4
  168. package/dist/ActionGroup-ChKFW0mt.js +0 -27
  169. package/dist/Avatar-loGgFt72.js +0 -113
  170. package/dist/AvatarGroup-D1gnT3Od.js +0 -81
  171. package/dist/AvatarName-0BzyfpMq.js +0 -68
  172. package/dist/Badge-jp-9iNNL.js +0 -28
  173. package/dist/Banner-B72lcEMo.js +0 -34
  174. package/dist/Block-Bu7NEA0g.js +0 -8
  175. package/dist/Block.d.ts +0 -30
  176. package/dist/Block.js +0 -4
  177. package/dist/Brand-Zq-Idks4.js +0 -34
  178. package/dist/Breadcrumb-CAM_e9LN.js +0 -98
  179. package/dist/Card-hn37Opo3.js +0 -12
  180. package/dist/ChatFrame-BW2TmCkZ.js +0 -37
  181. package/dist/ChatSplit-DNnu3TNq.js +0 -21
  182. package/dist/CodeBlock-CRzVopIv.js +0 -25
  183. package/dist/Column-BiogBDf8.js +0 -8
  184. package/dist/Column.d.ts +0 -24
  185. package/dist/Column.js +0 -4
  186. package/dist/Dropdown-DosvDR13.js +0 -147
  187. package/dist/EmptyState-mBiWtMIM.js +0 -23
  188. package/dist/Header-DZQopVNy.js +0 -21
  189. package/dist/Healthmap-B6cdiLUl.js +0 -243
  190. package/dist/ItemColumn-M7a9L8CI.js +0 -41
  191. package/dist/Layout-mi6Ghayv.js +0 -31
  192. package/dist/List-BgKkYaGO.js +0 -12
  193. package/dist/Loading-DkkG7RcE.js +0 -16
  194. package/dist/Menu-DuXQv-zT.js +0 -66
  195. package/dist/NavSections-D6rDEtT1.js +0 -142
  196. package/dist/PageHead-DgaJ1mXY.js +0 -31
  197. package/dist/PageShell-D9EZiO8o.js +0 -30
  198. package/dist/PanelInfo-A5y5ysH8.js +0 -81
  199. package/dist/Popover-DWdvZTuA.js +0 -135
  200. package/dist/PricingGroup-Czuhe81Z.js +0 -120
  201. package/dist/PricingTable-BuLC0opK.js +0 -152
  202. package/dist/RadioGroup-fcXzhvMu.js +0 -129
  203. package/dist/Row-DsQOVn18.js +0 -8
  204. package/dist/Row.d.ts +0 -24
  205. package/dist/Row.js +0 -4
  206. package/dist/Section-Crxk2J-E.js +0 -34
  207. package/dist/Sidebar-CYFaPqdC.js +0 -178
  208. package/dist/SplitPane-DR7PCZ8f.js +0 -74
  209. package/dist/Stack-XkmPSswj.js +0 -53
  210. package/dist/Stat-sKXJwz5L.js +0 -27
  211. package/dist/TabSections-DN7yVJoF.js +0 -69
  212. package/dist/Table-BoDr3X2d.js +0 -25
  213. package/dist/TextGroup-DmhuyMR1.js +0 -23
  214. package/dist/Toolbar-C1zAk9ak.js +0 -42
  215. package/dist/rovingKeys-CGlfTtNQ.js +0 -15
  216. package/dist/useControllableValue-CWqH1kpM.js +0 -10
package/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  Shell UI для elCRM: `Layout`, `Header`, `Section`, `Stack`, `NavSections`, `TabSections` и др.
4
4
 
5
- Стили — CSS Modules с короткими уникальными классами; значения токенов задаёт приложение (`theme-dark.css` / `theme-light.css`). Fallback в пакете нет.
5
+ Стили — CSS Modules. В DOM className всегда **3 буквы** `{семья}{компонент}{первая буква локального}` (короткий код вроде c8s, только a–z): List `.root` → `llr`, Layout `.root` → `slr`, Sidebar `.root` → `nsr`. В исходнике — слова (`.root`, `.icon`). Семьи: `l` lists, `n` nav, `s` shell, `k` kit, `t` type, `a` avatar, `o` overlay, `g` pricing, `h` healthmap, `d` data. Значения токенов задаёт приложение (`theme-dark.css` / `theme-light.css`). Fallback в пакете нет.
6
+
7
+ **Локаль:** дефолтные подписи UI — на русском (`navLabel`, `label`, `copyLabel`, `aria-label` и т.п. можно переопределить). Пакет не тянет i18n-runtime.
6
8
 
7
9
  ## Визуальный стенд
8
10
 
@@ -33,6 +35,7 @@ import {
33
35
  ActionGroup,
34
36
  Toolbar,
35
37
  TextGroup,
38
+ Text,
36
39
  Stat,
37
40
  Stack,
38
41
  Menu,
@@ -63,13 +66,18 @@ import {
63
66
  ChatSplit,
64
67
  Sidebar,
65
68
  Popover,
69
+ Healthmap,
70
+ PanelInfo,
66
71
  createLazyResolver,
72
+ acquireOverlayZIndex,
67
73
  } from "@elcrm/components";
68
- import type { PricingPlan } from "@elcrm/components";
74
+ import type { PricingPlan, HealthmapData, NavTreeItem } from "@elcrm/components";
69
75
  // CSS подтягивается с чанками; при необходимости единый файл:
70
76
  // import "@elcrm/components/style.css";
71
77
  ```
72
78
 
79
+ Слоты `Header` заполняет приложение: поиск — `@elcrm/form` (`Search`), кнопки — `@elcrm/button`.
80
+
73
81
  Точечный импорт:
74
82
 
75
83
  ```ts
@@ -88,44 +96,65 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
88
96
  | **Brand** | Название / логотип | `as`, `children`, `onClick` |
89
97
  | **Avatar** | Фото или инициалы | `src`, `name`, `size`, `radius`, `status`, `as` |
90
98
  | **AvatarName** | Аватар + title + description | `title`, `description`, `radius`, `--avatar-name-*-color` |
91
- | **AvatarGroup** | Стопка аватаров + «+N» | `items` (`value`/`title`/`name`), `max`, `add` |
99
+ | **AvatarGroup** | Стопка аватаров + «+N» | `items` (`value` обязателен), `max`, `add`, `onValueChange` |
92
100
  | **Badge** | Метка / статус | `tone`, `size`, `dot`, `children` |
93
- | **RadioGroup** | Выбор одной опции-карточки | `items`, `value`, `indicator`, `indicatorSide` |
94
- | **PricingGroup** | Тарифные карточки в ряд | `items` (`PricingPlan`), `selectable`, `value` |
101
+ | **RadioGroup** | Выбор одной опции-карточки | `items`, `value`, `onValueChange`, `indicator`, `indicatorSide` |
102
+ | **PricingGroup** | Тарифные карточки в ряд | `items` (`PricingPlan`), `selectable`, `value`, `onValueChange` |
95
103
  | **PricingTable** | Сравнение тарифов (таблица) | те же `items` + `sections` |
96
- | **Table** | Таблица данных со скроллом | `layout` (`auto`/`fixed`), стандартные props `<table>` |
104
+ | **Table** | Таблица данных со скроллом | `layout`, `stickyHeader`, `<caption>` |
97
105
  | **CodeBlock** | Блок кода с bar и копированием | `title`, `code` / `children`, `onCopy`, `actions` |
98
- | **SplitPane** | Две панели с разделителем | `direction`, `ratio`, `onRatioChange`, `first`, `second`, `minFirst` / `minSecond` |
106
+ | **SplitPane** | Две панели с разделителем | `direction`, `ratio` / `defaultRatio`, `onRatioChange`, `first`, `second` |
99
107
  | **Banner** | Статусная полоса | `tone` (`info`/`warning`/`danger`/`success`), не toast |
100
108
  | **IconButton** | Иконка + tooltip | `name`, обязательный `label`, `tooltip` (`false` — без подсказки) |
101
- | **Menu** | Навигация в шапке (не popover) | `items` (`value`, `badge`: `0` скрыт), `value`, `onSelect`, `variant` (`plain`/`line`) |
102
- | **Dropdown** | Выпадающее меню (items, portal) | `label`/`trigger`, `items`/`groups`, `onSelect` |
103
- | **Popover** | Свой попап в portal | `trigger`, `children`, `align`, `open` |
104
- | **Breadcrumb** | Хлебные крошки | `items`, `separator`, `collapseAfter` |
105
- | **Sidebar** | Левое меню приложения | `groups`, `value`, `item.items` + `defaultOpen`, `open` / `onOpenChange`, `footerItems` |
109
+ | **Menu** | Навигация в шапке (не popover) | `items`, `value` / `defaultValue`, `onValueChange`, `variant` (`plain`/`line`) |
110
+ | **Dropdown** | Выпадающее меню (items, portal) | `label`/`trigger`, `items`/`groups`, `onValueChange` |
111
+ | **Popover** | Свой попап в portal | `trigger` (render-prop `anchorProps` или узел), `children`, `align`, `open` |
112
+ | **Breadcrumb** | Хлебные крошки | `items`, `separator`, `collapseAfter`, `onValueChange` |
113
+ | **Sidebar** | Левое меню приложения | `groups`, `value` / `defaultValue`, `onValueChange`, `footerItems` + `onFooterValueChange` |
106
114
  | **PageShell** | Каркас модуля (скролл + footer) | `wide`, `scroll`, `footer`, `children` |
107
115
  | **ChatList** | Колонка диалогов | `header`, `footer`, `children` (`List`/`Item`), `bodyRef` |
108
116
  | **ChatMessages** | Колонка переписки | `header`, `footer`, `children`, `bodyRef`, `onBodyScroll` |
109
117
  | **ChatSplit** | Список + лента | `list`, `messages` |
110
118
  | **PageHead** | Заголовок страницы + actions | `title`, `sup` (текст/Badge), `subtitle`, `actions`, `sticky`, `children` |
111
- | **Section** | Блок контента / auth / подраздел | `title`, `description`, `icon`, `actions`, `variant` |
112
- | **NavSections** | Сайдбар + lazy-панель | `groups`, `item.items` + `defaultOpen`, `open` / `onOpenChange`, `resolveSection` |
113
- | **TabSections** | Горизонтальные вкладки + lazy | `items` (`value` + `badge`), `resolveSection` |
119
+ | **Section** | Блок контента / auth / подраздел | `title`, `description`, `icon`, `actions`, `variant`, `direction`/`gap`/`columns` (тело = Stack) |
120
+ | **NavSections** | Сайдбар + lazy-панель | `groups` (`value`), `resolveSection?`, `children` (готовая панель) |
121
+ | **TabSections** | Горизонтальные вкладки + lazy | `items`, `resolveSection?`, `children` |
114
122
  | **Card** | Карточка | `title`, `--card-pointer` |
115
- | **ActionGroup** | Ряд иконок в карточке | `label`, `items` и/или `children` |
116
- | **Toolbar** | Фильтры одной высоты | `size`, `width` (`l` = 100%, `s` = по контенту), `align`, `label`, `children` |
123
+ | **ActionGroup** | Ряд иконок в карточке | `label`, `actions` и/или `children` (не декларативные `items`) |
124
+ | **Toolbar** | Фильтры одной высоты | `size` `useToolbar` + `data-size`, `width`, `align`, `label`, `children` |
117
125
  | **TextGroup** | Title + description | `title`, `description`, `size` |
126
+ | **Text** | Короткий текст | `tone` (`default`/`muted`), `as` |
118
127
  | **ItemColumn** | Иконка + столбик в Item | `icon`, `title`, `extra`, `aside`, `description`, `footer` / `meta`, `unread` |
119
128
  | **Stat** | Карточка-метрика | `label`, `value`, `tone`, `labelMuted` |
120
- | **List** / **Item** | Список и строка | List: `row`; Item: `as`, `active`, `variant` (`plain`/`card`) |
129
+ | **List** / **Item** | Список и строка | List: `row`; Item: по умолчанию `li`, кликабельный `as="button"` |
121
130
  | **Stack** | Ряд / колонка / сетка | `direction`, `columns` (N или `["1fr", "100px"]`), `gap` |
122
- | **Loading** | Плейсхолдер загрузки | `children` |
123
- | **EmptyState** | Заглушка «нет данных» | `title`, `description`, `icon`, `actions` |
124
- | **createLazyResolver** | Кэш `import()` секций | `(key) => import(…)` |
125
-
126
- Слоты `Header` заполняет приложение: поиск — `@elcrm/search`, кнопки — `@elcrm/button`, и т.д.
127
-
128
- `Block` / `Row` / `Column` — deprecated-обёртки над `Stack`. В проектах: `elcrm migrate components`.
131
+ | **Loading** | Плейсхолдер загрузки | `children`; `--loading-min-height` |
132
+ | **EmptyState** | Пустой / ошибка | `title`, `description`, `icon`, `actions`; `--empty-*` |
133
+ | **PanelInfo** | Выезд справа | `open` / `defaultOpen`, `onOpenChange` |
134
+ | **createLazyResolver** | Кэш `import()` секций | `(key) => import(…)`; `.clear(segment?)` при ошибке / retry |
135
+
136
+ Раскладка — только `Stack` (или `Section` с `direction`/`gap`/`columns`). `Block` / `Row` / `Column` **удалены** из пакета; в проектах: `elcrm migrate components`.
137
+
138
+ ### Кто когда (роли)
139
+
140
+ Не сливаем компоненты — выбирай по слою:
141
+
142
+ | Слой | Компоненты | Не путать с |
143
+ | --- | --- | --- |
144
+ | Оболочка app | `Layout` (+ Header / Footer / Sidebar) | PageShell |
145
+ | Каркас модуля | `PageShell` | Section |
146
+ | Шапка страницы | `PageHead` | Section title |
147
+ | Блок контента | `Section` (`direction` / `gap` / `columns` как у Stack) | PageHead |
148
+ | Нав app | `Sidebar` | NavSections (страница + lazy-панель) |
149
+ | Вкладки | `Menu variant="line"` = только табы; `TabSections` = табы + lazy-панель | |
150
+ | Две колонки | `SplitPane` = resize; `ChatSplit` = чат list+messages без resize | |
151
+ | Чат | `ChatList` / `ChatMessages` / `ChatSplit`; `ChatFrame` — deep `@elcrm/components/ChatFrame` | |
152
+ | Таблица | `Table` (`stickyHeader`) | PricingTable |
153
+ | Выбор планов | `PricingGroup` / `PricingTable`; `RadioGroup variant="split"` — общий radio-UI | |
154
+ | Toolbar | `Toolbar` = фильтры одной высоты; `ActionGroup` = иконки в карточке | |
155
+ | Текст | `Text` / `TextGroup` / `AvatarName` (+аватар) | |
156
+ | Статус | `Badge` метка; `Banner` полоса; `EmptyState` пустой экран | |
157
+ | Раскладка | `Stack` | Block / Row / Column |
129
158
 
130
159
  ### Sidebar
131
160
 
@@ -135,8 +164,8 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
135
164
  <Sidebar
136
165
  variant="cards"
137
166
  value={page}
138
- onSelect={setPage}
139
- brand={<Brand as="strong">App</Brand>}
167
+ onValueChange={setPage}
168
+ brand={<Brand>App</Brand>}
140
169
  extra={<Notifications.Bell />}
141
170
  groups={[
142
171
  {
@@ -165,7 +194,7 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
165
194
  { value: "theme", label: "Тема", submenu: true },
166
195
  { value: "logout", label: "Выйти", danger: true },
167
196
  ]}
168
- onFooterSelect={(value) => console.log(value)}
197
+ onFooterValueChange={(value) => console.log(value)}
169
198
  version="v 0.2.26"
170
199
  />
171
200
  ```
@@ -357,18 +386,24 @@ const sections = [
357
386
  | ------------- | -------------------------------------------------------------------------------------------------------- |
358
387
  | **PageShell** | Каркас модуля: скролл в body (`scroll`, по умолчанию да), опциональный footer снизу |
359
388
  | **PageHead** | Строка title (+ `sup` надстрочно, subtitle) слева и actions справа; `sticky` — прилипает к верху скролла |
360
- | **Section** | Блок контента / auth-карточка / подраздел с title+description |
389
+ | **Section** | Блок контента / auth / подраздел. Корень по умолчанию `<section>` (не `<main>` — его даёт Layout). Тело: `direction` / `gap` / `columns` как у Stack |
361
390
 
362
391
  ```tsx
363
- <PageShell scroll footer={<button type="button">Сохранить</button>}>
392
+ <PageShell scroll footer={<Button>Сохранить</Button>}>
364
393
  <PageHead
365
394
  sticky
366
395
  title="Товары"
367
396
  sup={12}
368
397
  subtitle="Каталог"
369
- actions={<button>Добавить</button>}
398
+ actions={<Button>Добавить</Button>}
370
399
  />
371
- <Section title="Фильтры">{/* */}</Section>
400
+ <Section title="Фильтры" direction="row" gap={12}>
401
+ <SearchField name="q" />
402
+ <Button>Найти</Button>
403
+ </Section>
404
+ <Section title="Метрики" direction="grid" columns={3} gap={16}>
405
+ <Stat label="Всего" value={10} />
406
+ </Section>
372
407
  </PageShell>
373
408
  ```
374
409
 
@@ -431,11 +466,11 @@ const people = [
431
466
  max={5}
432
467
  add
433
468
  onAdd={() => {}}
434
- onItemClick={(item) => console.log(item)}
469
+ onValueChange={(value) => console.log(value)}
435
470
  />
436
471
  ```
437
472
 
438
- `max` — сколько показать; остаток → «+N». `add` — кнопка «+» справа.
473
+ `max` — сколько показать; остаток → «+N». `add` — кнопка «+» справа. У каждого пункта обязателен `value`.
439
474
 
440
475
  ### Avatar / AvatarName
441
476
 
@@ -491,7 +526,7 @@ const people = [
491
526
  items: [{ value: "details", label: "View details", submenu: true }],
492
527
  },
493
528
  ]}
494
- onSelect={(value) => console.log(value)}
529
+ onValueChange={(value) => console.log(value)}
495
530
  />;
496
531
 
497
532
  {
@@ -566,13 +601,13 @@ const people = [
566
601
  },
567
602
  { value: "brand", label: "Бренд", checkbox: true, checked: false },
568
603
  ]}
569
- onSelect={(value) => toggleColumn(value)}
604
+ onValueChange={(value) => toggleColumn(value)}
570
605
  />;
571
606
  ```
572
607
 
573
608
  `Menu` — навигация в шапке (inline, не popover).
574
609
 
575
- **Dropdown vs Popover:** `Dropdown` — пункты `items`/`groups`. `Popover` — свой `children` в portal (тот же z-стек, что у `@elcrm/form`). `@elcrm/overlay` `Menu` — устаревший алиас `Popover`. Select — `@elcrm/form`, не overlay.
610
+ **Dropdown vs Popover:** `Dropdown` — пункты `items`/`groups`. `Popover` — свой `children` в portal; канон `trigger={(api) => <button {...api.anchorProps}>…</button>}` (без cloneElement). `@elcrm/overlay` `Menu` — устаревший алиас `Popover`. Select — `@elcrm/form`, не overlay.
576
611
 
577
612
  ### Breadcrumb
578
613
 
@@ -619,7 +654,7 @@ const people = [
619
654
  ```tsx
620
655
  <Layout
621
656
  fixed={["header", "footer", "sidebar"]}
622
- sidebar={<Sidebar groups={groups} value={page} onSelect={setPage} />}
657
+ sidebar={<Sidebar groups={groups} value={page} onValueChange={setPage} />}
623
658
  header={
624
659
  <Header
625
660
  brand={<Brand onClick={() => {}}>App</Brand>}
@@ -673,6 +708,7 @@ const resolve = createLazyResolver(
673
708
  resolveSection={resolve}
674
709
  groups={[
675
710
  {
711
+ value: "personal",
676
712
  label: "Личное",
677
713
  items: [
678
714
  { value: "Profile", label: "Профиль" },
@@ -774,6 +810,8 @@ const resolve = createLazyResolver(
774
810
  --code-block-bg / --code-block-padding / --code-block-font-size
775
811
  --split-handle-size / --split-handle-bg / --split-handle-bg-hover
776
812
  --banner-padding / --banner-bg / --banner-border
813
+ --loading-min-height
814
+ --empty-max-width / --empty-padding
777
815
  --panel-info-width / --panel-info-width-min / --panel-info-overlay-max / --panel-info-backdrop
778
816
  --row-gap / --column-gap
779
817
  --popup-shadow
@@ -0,0 +1,27 @@
1
+ import c from "react";
2
+ import { c as l } from "./cx-CcykAxZN.js";
3
+ import './ActionGroup.css';const n = "lgr", s = {
4
+ root: n
5
+ };
6
+ function p({
7
+ label: o,
8
+ actions: r,
9
+ children: t,
10
+ className: a,
11
+ ...e
12
+ }) {
13
+ return /* @__PURE__ */ c.createElement(
14
+ "div",
15
+ {
16
+ className: l(s.root, a),
17
+ role: "toolbar",
18
+ "aria-label": o,
19
+ ...e
20
+ },
21
+ r,
22
+ t
23
+ );
24
+ }
25
+ export {
26
+ p as A
27
+ };
@@ -1 +1 @@
1
- @layer elcrm-components{.c5r{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--action-group-gap);flex:0 0 auto}}
1
+ @layer elcrm-components{.lgr{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--action-group-gap);flex:0 0 auto}}
@@ -2,26 +2,23 @@ import { default as React } from 'react';
2
2
  /**
3
3
  * Ряд действий в строке списка / карточке (`role="toolbar"`).
4
4
  *
5
- * Роли: тулбар иконок справа в Card. Кнопки — `@elcrm/button` / свои слоты,
6
- * не нативный `<button>` в приложении.
7
- *
8
- * @param label подпись для `aria-label`
9
- * @param items массив узлов (если нет — только `children`)
10
- * @param children слот кнопок; можно вместе с `items` (сначала items)
5
+ * Роли: тулбар иконок справа в Card. Кнопки — `@elcrm/button` / свои слоты.
6
+ * Слоты `children` опционально `actions` массивом узлов).
7
+ * Не путать с декларативными `items` у Menu / RadioGroup.
11
8
  *
12
9
  * @example
13
10
  * ```tsx
11
+ * <ActionGroup label="Действия: Иван">
12
+ * <Button variant="ghost" onClick={edit}>Изменить</Button>
13
+ * <Button variant="ghost" onClick={remove}>Удалить</Button>
14
+ * </ActionGroup>
15
+ *
14
16
  * <ActionGroup
15
- * label="Действия: Иван"
16
- * items={[
17
- * <Button key="edit" variant="ghost" onClick={edit}>Изменить</Button>,
18
- * <Button key="del" variant="ghost" onClick={remove}>Удалить</Button>,
17
+ * label="Ещё"
18
+ * actions={[
19
+ * <Button key="copy" variant="ghost">Копировать</Button>,
19
20
  * ]}
20
21
  * />
21
- *
22
- * <ActionGroup label="Ещё">
23
- * <Button variant="ghost">Копировать</Button>
24
- * </ActionGroup>
25
22
  * ```
26
23
  *
27
24
  * Токены: `--action-group-gap` (иначе `--shell-gap-sm`).
@@ -29,14 +26,17 @@ import { default as React } from 'react';
29
26
  export type ActionGroupProps = React.HTMLAttributes<HTMLDivElement> & {
30
27
  /** Подпись тулбара (`aria-label`) */
31
28
  label: string;
32
- /** Кнопки массивом узлов */
33
- items?: React.ReactNode[];
29
+ /**
30
+ * Кнопки массивом узлов (не декларативные `items` групп).
31
+ * Можно вместе с `children` (сначала `actions`).
32
+ */
33
+ actions?: React.ReactNode[];
34
34
  /** Свои кнопки / слоты */
35
35
  children?: React.ReactNode;
36
36
  };
37
37
  /**
38
- * Группа действий: `items` и/или `children`.
38
+ * Группа действий: `actions` и/или `children`.
39
39
  *
40
- * @see Card, Stack, Item
40
+ * @see Card, Stack, Item, Toolbar
41
41
  */
42
- export declare function ActionGroup({ label, items, children, className, ...rest }: ActionGroupProps): React.JSX.Element;
42
+ export declare function ActionGroup({ label, actions, children, className, ...rest }: ActionGroupProps): React.JSX.Element;
@@ -1,4 +1,4 @@
1
- import { A as p } from "./ActionGroup-ChKFW0mt.js";
1
+ import { A as p } from "./ActionGroup-DcApj_Mz.js";
2
2
  export {
3
3
  p as ActionGroup
4
4
  };
@@ -0,0 +1,113 @@
1
+ import a from "react";
2
+ import { c as p } from "./cx-CcykAxZN.js";
3
+ import './Avatar.css';const x = "aar", h = "aav", A = "aas", C = "aam", S = "aal", z = "aaa", B = "aai", F = "aab", R = "aao", U = "aaf", e = {
4
+ root: x,
5
+ vtext: h,
6
+ small: A,
7
+ middle: C,
8
+ large: S,
9
+ active: z,
10
+ img: B,
11
+ bead: F,
12
+ online: R,
13
+ foff: U
14
+ };
15
+ function D(t) {
16
+ const n = t.trim();
17
+ if (!n) return "?";
18
+ const l = n.split(/\s+/).filter(Boolean);
19
+ return l.length >= 2 ? (l[0][0] + l[1][0]).toUpperCase() : n.slice(0, 2).toUpperCase();
20
+ }
21
+ function $(t) {
22
+ if (!(t == null || t === ""))
23
+ return {
24
+ "--avatar-radius": typeof t == "number" ? `${t}px` : t
25
+ };
26
+ }
27
+ function j({ status: t }) {
28
+ return /* @__PURE__ */ a.createElement(
29
+ "span",
30
+ {
31
+ className: p(e.bead, t === "online" ? e.online : e.foff),
32
+ "data-status": t,
33
+ "aria-hidden": !0
34
+ }
35
+ );
36
+ }
37
+ function k({
38
+ src: t,
39
+ label: n,
40
+ children: l,
41
+ name: r,
42
+ status: i
43
+ }) {
44
+ const s = t != null && t !== "" ? /* @__PURE__ */ a.createElement("img", { className: e.img, src: t, alt: n ?? "", draggable: !1 }) : l ?? (r != null ? D(r) : "?");
45
+ return /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement("span", { className: e.vtext }, s), i != null ? /* @__PURE__ */ a.createElement(j, { status: i }) : null);
46
+ }
47
+ function G(t) {
48
+ const {
49
+ src: n,
50
+ alt: l,
51
+ name: r,
52
+ size: i = "m",
53
+ status: s,
54
+ active: c,
55
+ children: b,
56
+ className: g,
57
+ radius: y,
58
+ as: v = "span",
59
+ style: E,
60
+ ...m
61
+ } = t, N = i === "s" ? e.small : i === "l" ? e.large : e.middle, u = p(e.root, N, c && e.active, g), o = l ?? r, d = { ...$(y), ...E }, f = /* @__PURE__ */ a.createElement(
62
+ k,
63
+ {
64
+ src: n,
65
+ label: o,
66
+ name: r,
67
+ status: s,
68
+ children: b
69
+ }
70
+ );
71
+ return v === "button" ? /* @__PURE__ */ a.createElement(
72
+ "button",
73
+ {
74
+ type: "button",
75
+ className: u,
76
+ "aria-label": o,
77
+ "data-active": c ? "true" : void 0,
78
+ "data-status": s,
79
+ style: d,
80
+ ...m
81
+ },
82
+ f
83
+ ) : v === "div" ? /* @__PURE__ */ a.createElement(
84
+ "div",
85
+ {
86
+ className: u,
87
+ role: o ? "img" : void 0,
88
+ "aria-label": o,
89
+ "data-active": c ? "true" : void 0,
90
+ "data-status": s,
91
+ style: d,
92
+ ...m
93
+ },
94
+ f
95
+ ) : /* @__PURE__ */ a.createElement(
96
+ "span",
97
+ {
98
+ className: u,
99
+ role: o ? "img" : void 0,
100
+ "aria-label": o,
101
+ "data-active": c ? "true" : void 0,
102
+ "data-status": s,
103
+ style: d,
104
+ ...m
105
+ },
106
+ f
107
+ );
108
+ }
109
+ export {
110
+ G as A,
111
+ $ as a,
112
+ D as i
113
+ };
package/dist/Avatar.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.car{position:relative;box-sizing:border-box;display:inline-flex;flex-shrink:0;width:fit-content;height:fit-content;border-radius:var(--avatar-radius);-webkit-user-select:none;user-select:none;vertical-align:middle}.car:is(button){-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding:0;font:inherit;cursor:pointer;background:transparent;border:none}.cav{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;width:100%;height:100%;border-radius:inherit;border:var(--avatar-border);background:var(--avatar-background);color:var(--avatar-color);font-weight:var(--avatar-font-weight);line-height:1}.cas{width:var(--avatar-size-s);height:var(--avatar-size-s);font-size:var(--avatar-font-size-s)}.cas .cav{font-size:inherit}.cam{width:var(--avatar-size-m);height:var(--avatar-size-m);font-size:var(--avatar-font-size-m)}.cam .cav{font-size:inherit}.cal{width:var(--avatar-size-l);height:var(--avatar-size-l);font-size:var(--avatar-font-size-l)}.cal .cav{font-size:inherit}.caa .cav,.car[data-active=true] .cav{outline:var(--avatar-outline-active);outline-offset:var(--avatar-outline-offset)}.cai{width:100%;height:100%;object-fit:cover;display:block}.cab{position:absolute;right:0;bottom:0;box-sizing:border-box;width:var(--shell-status-size);height:var(--shell-status-size);border-radius:50%;border:2px solid var(--shell-background);pointer-events:none}.cas .cab{width:8px;height:8px}.cam .cab{width:var(--shell-status-size);height:var(--shell-status-size)}.cal .cab{width:12px;height:12px}.cao{background:var(--shell-color-success)}.caf{background:var(--shell-color-muted)}}
1
+ @layer elcrm-components{.aar{position:relative;box-sizing:border-box;display:inline-flex;flex-shrink:0;width:fit-content;height:fit-content;border-radius:var(--avatar-radius);-webkit-user-select:none;user-select:none;vertical-align:middle}.aar:is(button){-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding:0;font:inherit;cursor:pointer;background:transparent;border:none}.aav{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;overflow:hidden;width:100%;height:100%;border-radius:inherit;border:var(--avatar-border);background:var(--avatar-background);color:var(--avatar-color);font-weight:var(--avatar-font-weight);line-height:1}.aas{width:var(--avatar-size-s);height:var(--avatar-size-s);font-size:var(--avatar-font-size-s)}.aas .aav{font-size:inherit}.aam{width:var(--avatar-size-m);height:var(--avatar-size-m);font-size:var(--avatar-font-size-m)}.aam .aav{font-size:inherit}.aal{width:var(--avatar-size-l);height:var(--avatar-size-l);font-size:var(--avatar-font-size-l)}.aal .aav{font-size:inherit}.aaa .aav,.aar[data-active=true] .aav{outline:var(--avatar-outline-active);outline-offset:var(--avatar-outline-offset)}.aai{width:100%;height:100%;object-fit:cover;display:block}.aab{position:absolute;right:0;bottom:0;box-sizing:border-box;width:var(--shell-status-size);height:var(--shell-status-size);border-radius:50%;border:2px solid var(--shell-background);pointer-events:none}.aas .aab{width:8px;height:8px}.aam .aab{width:var(--shell-status-size);height:var(--shell-status-size)}.aal .aab{width:12px;height:12px}.aao{background:var(--shell-color-success)}.aaf{background:var(--shell-color-muted)}}
package/dist/Avatar.d.ts CHANGED
@@ -48,10 +48,10 @@ export declare function avatarRadiusStyle(radius?: number | string): React.CSSPr
48
48
  *
49
49
  * По умолчанию `as="span"`. Для кликабельного аватара — `as="button"`.
50
50
  */
51
- export type AvatarProps = (Common & React.ButtonHTMLAttributes<HTMLButtonElement> & {
52
- as?: "button";
53
- }) | (Common & React.HTMLAttributes<HTMLSpanElement> & {
54
- as: "span";
51
+ export type AvatarProps = (Common & React.HTMLAttributes<HTMLSpanElement> & {
52
+ as?: "span";
53
+ }) | (Common & React.ButtonHTMLAttributes<HTMLButtonElement> & {
54
+ as: "button";
55
55
  }) | (Common & React.HTMLAttributes<HTMLDivElement> & {
56
56
  as: "div";
57
57
  });
package/dist/Avatar.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as r, a as s, i as t } from "./Avatar-loGgFt72.js";
1
+ import { A as r, a as s, i as t } from "./Avatar-eGdLrMCv.js";
2
2
  export {
3
3
  r as Avatar,
4
4
  s as avatarRadiusStyle,
@@ -0,0 +1,81 @@
1
+ import e from "react";
2
+ import { A as f } from "./Avatar-eGdLrMCv.js";
3
+ import { c } from "./cx-CcykAxZN.js";
4
+ import './AvatarGroup.css';const C = "lar", I = "lai", P = "lao", x = "lad", G = "las", M = "lam", R = "lal", t = {
5
+ root: C,
6
+ item: I,
7
+ overflow: P,
8
+ dadd: x,
9
+ small: G,
10
+ middle: M,
11
+ large: R
12
+ };
13
+ function $(a) {
14
+ if (a.name != null && a.name !== "") return a.name;
15
+ if (a.title != null && a.title !== "") return a.title;
16
+ }
17
+ function B({
18
+ items: a,
19
+ max: s,
20
+ size: n = "m",
21
+ add: v = !1,
22
+ addLabel: b = "Добавить",
23
+ onAdd: r,
24
+ onValueChange: u,
25
+ className: g,
26
+ "aria-label": E = "Аватары",
27
+ ...h
28
+ }) {
29
+ const i = n === "s" ? t.small : n === "l" ? t.large : t.middle, k = s != null && s >= 0 ? Math.min(s, a.length) : a.length, m = a.slice(0, k), o = a.length - m.length;
30
+ return /* @__PURE__ */ e.createElement(
31
+ "div",
32
+ {
33
+ className: c(t.root, g),
34
+ role: "group",
35
+ "aria-label": E,
36
+ ...h
37
+ },
38
+ m.map((l, N) => {
39
+ const p = $(l), d = l.alt ?? l.title ?? l.name, w = u != null;
40
+ return /* @__PURE__ */ e.createElement("span", { key: l.value, className: t.item, style: { zIndex: N + 1 } }, w ? /* @__PURE__ */ e.createElement(
41
+ f,
42
+ {
43
+ as: "button",
44
+ src: l.src,
45
+ name: p,
46
+ alt: d,
47
+ size: n,
48
+ status: l.status,
49
+ onClick: (y) => {
50
+ y.stopPropagation(), u(l.value);
51
+ }
52
+ }
53
+ ) : /* @__PURE__ */ e.createElement(
54
+ f,
55
+ {
56
+ src: l.src,
57
+ name: p,
58
+ alt: d,
59
+ size: n,
60
+ status: l.status
61
+ }
62
+ ));
63
+ }),
64
+ o > 0 ? /* @__PURE__ */ e.createElement("span", { className: c(t.overflow, i), "aria-label": `Ещё ${o}` }, "+", o) : null,
65
+ v ? /* @__PURE__ */ e.createElement(
66
+ "button",
67
+ {
68
+ type: "button",
69
+ className: c(t.dadd, i),
70
+ "aria-label": b,
71
+ onClick: (l) => {
72
+ l.stopPropagation(), r == null || r();
73
+ }
74
+ },
75
+ "+"
76
+ ) : null
77
+ );
78
+ }
79
+ export {
80
+ B as A
81
+ };
@@ -1 +1 @@
1
- @layer elcrm-components{.cyr{display:inline-flex;align-items:center;vertical-align:middle}.cyi{position:relative;margin-left:var(--avatar-group-overlap);box-shadow:0 0 0 var(--avatar-group-ring) var(--shell-background);border-radius:var(--avatar-radius)}.cyi:first-child{margin-left:0}.cyo,.cyd{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:var(--avatar-group-overlap);border-radius:var(--avatar-radius);font-weight:var(--avatar-font-weight);line-height:1;-webkit-user-select:none;user-select:none}.cyo{z-index:20;color:var(--shell-color-muted);background:var(--shell-background-muted);border:1px solid var(--shell-border);box-shadow:0 0 0 var(--avatar-group-ring) var(--shell-background)}.cyd{z-index:21;margin-left:var(--shell-gap-sm);color:var(--shell-color-muted);background:var(--shell-background);border:1px dashed var(--shell-border);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;font:inherit;font-weight:var(--avatar-font-weight)}.cyd:hover{background:var(--shell-background-hover)}.cys{width:var(--avatar-size-s);height:var(--avatar-size-s);font-size:var(--avatar-font-size-s)}.cym{width:var(--avatar-size-m);height:var(--avatar-size-m);font-size:var(--avatar-font-size-m)}.cyl{width:var(--avatar-size-l);height:var(--avatar-size-l);font-size:var(--avatar-font-size-l)}}
1
+ @layer elcrm-components{.lar{display:inline-flex;align-items:center;vertical-align:middle}.lai{position:relative;margin-left:var(--avatar-group-overlap);box-shadow:0 0 0 var(--avatar-group-ring) var(--shell-background);border-radius:var(--avatar-radius)}.lai:first-child{margin-left:0}.lao,.lad{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:var(--avatar-group-overlap);border-radius:var(--avatar-radius);font-weight:var(--avatar-font-weight);line-height:1;-webkit-user-select:none;user-select:none}.lao{z-index:20;color:var(--shell-color-muted);background:var(--shell-background-muted);border:1px solid var(--shell-border);box-shadow:0 0 0 var(--avatar-group-ring) var(--shell-background)}.lad{z-index:21;margin-left:var(--shell-gap-sm);color:var(--shell-color-muted);background:var(--shell-background);border:1px dashed var(--shell-border);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;font:inherit;font-weight:var(--avatar-font-weight)}.lad:hover{background:var(--shell-background-hover)}.las{width:var(--avatar-size-s);height:var(--avatar-size-s);font-size:var(--avatar-font-size-s)}.lam{width:var(--avatar-size-m);height:var(--avatar-size-m);font-size:var(--avatar-font-size-m)}.lal{width:var(--avatar-size-l);height:var(--avatar-size-l);font-size:var(--avatar-font-size-l)}}
@@ -3,12 +3,12 @@ import { UiSize } from './types';
3
3
  /**
4
4
  * Элемент стопки {@link AvatarGroup}.
5
5
  *
6
- * Предпочтительный идентификатор — `value` (как у остальных group-UI).
7
- * `name` — только для инициалов; видимую подпись в списках даёт {@link AvatarName}.
6
+ * Идентификатор — `value` (как у остальных group-UI).
7
+ * `name` — только для инициалов; видимую подпись {@link AvatarName}.
8
8
  */
9
9
  export type AvatarGroupItem = {
10
- /** Уникальный id (предпочтительно; не React `key`). */
11
- value?: string;
10
+ /** Уникальный id (не React `key`). */
11
+ value: string;
12
12
  /** URL фото. */
13
13
  src?: string;
14
14
  /** Только для инициалов (если нет `src`). */
@@ -38,41 +38,21 @@ export type AvatarGroupProps = React.HTMLAttributes<HTMLDivElement> & {
38
38
  addLabel?: string;
39
39
  /** Клик по кнопке добавления. */
40
40
  onAdd?: () => void;
41
- /** Клик по аватару. */
42
- onItemClick?: (item: AvatarGroupItem, index: number) => void;
41
+ /** Клик по аватару (`item.value`). */
42
+ onValueChange?: (value: string) => void;
43
43
  };
44
44
  /**
45
45
  * Стопка аватаров с оверлапом, счётчиком «+N» и опциональной кнопкой добавления.
46
46
  *
47
- * Когда использовать:
48
- * - участники задачи / канала / сделки в одну строку;
49
- * - нужно ограничить видимое число (`max`) и добавить «+».
50
- *
51
- * Связанные компоненты:
52
- * - {@link Avatar} — одиночный аватар;
53
- * - {@link AvatarName} — аватар с текстовой подписью (`title` / `description`).
54
- *
55
- * API: декларативные `items`. Идентификатор предпочтительно `value` (не `key`).
56
- * Compound-слоты не используются.
57
- *
58
- * CSS-токены: `--avatar-group-*`, размеры из `--avatar-size-*`.
59
- *
60
47
  * @example
61
48
  * ```tsx
62
- * const items = [
63
- * { value: "u1", name: "Иван Петров", src: "/u/1.jpg", status: "online" },
64
- * { value: "u2", name: "Мария Смирнова" },
65
- * { value: "u3", name: "Алексей" },
66
- * ];
67
- *
68
49
  * <AvatarGroup
69
50
  * items={items}
70
51
  * max={5}
71
- * size="m"
72
52
  * add
73
53
  * onAdd={() => openInvite()}
74
- * onItemClick={(item) => openProfile(item.value)}
54
+ * onValueChange={(value) => openProfile(value)}
75
55
  * />
76
56
  * ```
77
57
  */
78
- export declare function AvatarGroup({ items, max, size, add, addLabel, onAdd, onItemClick, className, "aria-label": ariaLabel, ...rest }: AvatarGroupProps): React.JSX.Element;
58
+ export declare function AvatarGroup({ items, max, size, add, addLabel, onAdd, onValueChange, className, "aria-label": ariaLabel, ...rest }: AvatarGroupProps): React.JSX.Element;