@elcrm/components 0.1.1 → 0.2.0
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.
- package/README.md +130 -16
- package/dist/ActionGroup-ChKFW0mt.js +27 -0
- package/dist/ActionGroup.css +1 -0
- package/dist/ActionGroup.d.ts +42 -0
- package/dist/ActionGroup.js +4 -0
- package/dist/Avatar-loGgFt72.js +113 -0
- package/dist/Avatar.d.ts +11 -2
- package/dist/Avatar.js +3 -2
- package/dist/{AvatarGroup-BAWas4pU.js → AvatarGroup-D1gnT3Od.js} +1 -1
- package/dist/AvatarGroup.d.ts +2 -1
- package/dist/AvatarGroup.js +1 -1
- package/dist/AvatarName-0BzyfpMq.js +68 -0
- package/dist/AvatarName.css +1 -1
- package/dist/AvatarName.d.ts +8 -2
- package/dist/AvatarName.js +1 -1
- package/dist/Badge-jp-9iNNL.js +28 -0
- package/dist/Badge.css +1 -1
- package/dist/Badge.d.ts +33 -17
- package/dist/Badge.js +1 -1
- package/dist/{Block-BAXh0PPp.js → Block-Bu7NEA0g.js} +1 -1
- package/dist/Block.js +1 -1
- package/dist/Card.css +1 -1
- package/dist/Card.d.ts +8 -1
- package/dist/ChatFrame-BW2TmCkZ.js +37 -0
- package/dist/ChatFrame.css +1 -0
- package/dist/ChatList.d.ts +9 -0
- package/dist/ChatList.js +7 -0
- package/dist/ChatMessages-B8rdXiK-.js +19 -0
- package/dist/ChatMessages.d.ts +9 -0
- package/dist/ChatMessages.js +4 -0
- package/dist/ChatSplit-DNnu3TNq.js +21 -0
- package/dist/CheckIcon-FdB8fMEF.js +17 -0
- package/dist/CheckIcon.d.ts +8 -0
- package/dist/{Column-DDaqX-A7.js → Column-BiogBDf8.js} +1 -1
- package/dist/Column.js +1 -1
- package/dist/Dropdown-4yUB2tk1.js +142 -0
- package/dist/Dropdown.css +1 -1
- package/dist/Dropdown.d.ts +3 -3
- package/dist/Dropdown.js +1 -1
- package/dist/Footer.css +1 -1
- package/dist/Item-D4okhsfW.js +46 -0
- package/dist/Item.css +1 -1
- package/dist/Item.d.ts +8 -1
- package/dist/Item.js +1 -1
- package/dist/ItemColumn-M7a9L8CI.js +41 -0
- package/dist/ItemColumn.css +1 -0
- package/dist/ItemColumn.d.ts +62 -0
- package/dist/ItemColumn.js +4 -0
- package/dist/Layout-mi6Ghayv.js +31 -0
- package/dist/Layout.css +1 -1
- package/dist/Layout.d.ts +28 -6
- package/dist/Layout.js +1 -1
- package/dist/Loading-DkkG7RcE.js +16 -0
- package/dist/Loading.css +1 -1
- package/dist/Loading.d.ts +1 -1
- package/dist/Loading.js +1 -1
- package/dist/Menu-DuXQv-zT.js +66 -0
- package/dist/Menu.css +1 -1
- package/dist/Menu.d.ts +15 -3
- package/dist/Menu.js +1 -1
- package/dist/NavSections-BpyZPimZ.js +79 -0
- package/dist/NavSections.css +1 -1
- package/dist/NavSections.js +1 -1
- package/dist/PageHead-DgaJ1mXY.js +31 -0
- package/dist/PageHead.css +1 -1
- package/dist/PageHead.d.ts +28 -6
- package/dist/PageHead.js +1 -1
- package/dist/{PageShell-CghThHS3.js → PageShell-D9EZiO8o.js} +13 -11
- package/dist/PageShell.css +1 -1
- package/dist/PageShell.d.ts +11 -6
- package/dist/PageShell.js +1 -1
- package/dist/Popover-D0m5JWIt.js +142 -0
- package/dist/Popover.css +1 -0
- package/dist/Popover.d.ts +54 -0
- package/dist/Popover.js +4 -0
- package/dist/PricingGroup-Czuhe81Z.js +120 -0
- package/dist/PricingGroup.css +1 -1
- package/dist/PricingGroup.js +1 -1
- package/dist/{PricingTable-DALddSwM.js → PricingTable-BuLC0opK.js} +69 -75
- package/dist/PricingTable.css +1 -1
- package/dist/PricingTable.js +1 -1
- package/dist/RadioGroup-fcXzhvMu.js +129 -0
- package/dist/RadioGroup.css +1 -1
- package/dist/RadioGroup.js +1 -1
- package/dist/{Row--HIe6vb9.js → Row-DsQOVn18.js} +1 -1
- package/dist/Row.js +1 -1
- package/dist/Sidebar-CBYq1ad6.js +107 -0
- package/dist/Sidebar.css +1 -0
- package/dist/Sidebar.d.ts +99 -0
- package/dist/Sidebar.js +4 -0
- package/dist/Stack-XkmPSswj.js +53 -0
- package/dist/Stack.css +1 -1
- package/dist/Stack.d.ts +16 -4
- package/dist/Stack.js +3 -2
- package/dist/Stat-C5iZyEoo.js +27 -0
- package/dist/Stat.css +1 -0
- package/dist/Stat.d.ts +39 -0
- package/dist/Stat.js +4 -0
- package/dist/TabSections-DN7yVJoF.js +69 -0
- package/dist/TabSections.css +1 -1
- package/dist/TabSections.js +1 -1
- package/dist/TextGroup-DmhuyMR1.js +23 -0
- package/dist/TextGroup.css +1 -0
- package/dist/TextGroup.d.ts +36 -0
- package/dist/TextGroup.js +4 -0
- package/dist/badgeText-CRzlIzi3.js +8 -0
- package/dist/badgeText.d.ts +4 -0
- package/dist/dark.css +13 -0
- package/dist/index.css +31 -17
- package/dist/index.d.ts +27 -4
- package/dist/index.es.d.ts +82 -0
- package/dist/index.es.js +83 -59
- package/dist/light.css +13 -0
- package/dist/overlayZ.d.ts +9 -0
- package/dist/rovingKeys-CGlfTtNQ.js +15 -0
- package/dist/rovingKeys.d.ts +13 -0
- package/dist/tokens.css +44 -4
- package/dist/types.d.ts +2 -0
- package/dist/useControllableValue-CWqH1kpM.js +10 -0
- package/dist/useControllableValue.d.ts +4 -0
- package/package.json +6 -2
- package/dist/Avatar-CyNXYvsa.js +0 -101
- package/dist/AvatarName-DhD3TytF.js +0 -56
- package/dist/Badge-l6nUkl-H.js +0 -22
- package/dist/Dropdown-DQlDiWCR.js +0 -168
- package/dist/Item-CaU4RW2G.js +0 -38
- package/dist/Layout-Xb3-xsmA.js +0 -18
- package/dist/Loading-ChGq9peU.js +0 -15
- package/dist/Menu-DRBHvSdJ.js +0 -40
- package/dist/NavSections-NDcIDD6H.js +0 -59
- package/dist/PageHead-Baz9BLuA.js +0 -22
- package/dist/PricingGroup-DgcdVSq8.js +0 -126
- package/dist/RadioGroup-D2UUOTkY.js +0 -138
- package/dist/Stack-b0-XWwQ1.js +0 -31
- package/dist/TabSections-DL7DufKU.js +0 -50
package/README.md
CHANGED
|
@@ -4,6 +4,17 @@ Shell UI для elCRM: `Layout`, `Header`, `Section`, `Stack`, `NavSections`, `T
|
|
|
4
4
|
|
|
5
5
|
Стили — CSS Modules с короткими уникальными классами; значения токенов задаёт приложение (`theme-dark.css` / `theme-light.css`). Fallback в пакете нет.
|
|
6
6
|
|
|
7
|
+
## Визуальный стенд
|
|
8
|
+
|
|
9
|
+
Как у `@elcrm/form` — страница с галереей компонентов (`@elcrm/storybook`):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun i
|
|
13
|
+
bun run dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Откроется `http://localhost:3001`. В сайдбаре стенда: `section` (компонент / `all`), `size`, тон Badge, вариант Menu.
|
|
17
|
+
|
|
7
18
|
## Установка
|
|
8
19
|
|
|
9
20
|
```bash
|
|
@@ -17,7 +28,11 @@ import {
|
|
|
17
28
|
Footer,
|
|
18
29
|
List,
|
|
19
30
|
Item,
|
|
31
|
+
ItemColumn,
|
|
20
32
|
Card,
|
|
33
|
+
ActionGroup,
|
|
34
|
+
TextGroup,
|
|
35
|
+
Stat,
|
|
21
36
|
Stack,
|
|
22
37
|
Menu,
|
|
23
38
|
Loading,
|
|
@@ -37,6 +52,11 @@ import {
|
|
|
37
52
|
Breadcrumb,
|
|
38
53
|
PageHead,
|
|
39
54
|
PageShell,
|
|
55
|
+
ChatList,
|
|
56
|
+
ChatMessages,
|
|
57
|
+
ChatSplit,
|
|
58
|
+
Sidebar,
|
|
59
|
+
Popover,
|
|
40
60
|
createLazyResolver,
|
|
41
61
|
} from "@elcrm/components";
|
|
42
62
|
import type { PricingPlan } from "@elcrm/components";
|
|
@@ -56,28 +76,37 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
|
|
|
56
76
|
|
|
57
77
|
| | Назначение | Ключевые props |
|
|
58
78
|
| -------------------------------- | -------------------------------- | ---------------------------------------------------- |
|
|
59
|
-
| **Layout** | Оболочка страницы | `header`, `footer`, `children`
|
|
79
|
+
| **Layout** | Оболочка страницы | `sidebar`, `header`, `footer`, `fixed`, `children` |
|
|
60
80
|
| **Header** | Шапка со слотами | `brand`, `nav`, `search`, `actions` |
|
|
61
81
|
| **Footer** | Подвал | `children` |
|
|
62
82
|
| **Brand** | Название / логотип | `as`, `children`, `onClick` |
|
|
63
|
-
| **Avatar** | Фото или инициалы | `src`, `name
|
|
64
|
-
| **AvatarName** | Аватар + title + description | `title`, `description`, `name
|
|
83
|
+
| **Avatar** | Фото или инициалы | `src`, `name`, `size`, `radius`, `status`, `as` |
|
|
84
|
+
| **AvatarName** | Аватар + title + description | `title`, `description`, `radius`, `--avatar-name-*-color` |
|
|
65
85
|
| **AvatarGroup** | Стопка аватаров + «+N» | `items` (`value`/`title`/`name`), `max`, `add` |
|
|
66
|
-
| **Badge** | Метка / статус | `tone`, `children`
|
|
86
|
+
| **Badge** | Метка / статус | `tone`, `size`, `dot`, `children` |
|
|
67
87
|
| **RadioGroup** | Выбор одной опции-карточки | `items`, `value`, `indicator`, `indicatorSide` |
|
|
68
88
|
| **PricingGroup** | Тарифные карточки в ряд | `items` (`PricingPlan`), `selectable`, `value` |
|
|
69
89
|
| **PricingTable** | Сравнение тарифов (таблица) | те же `items` + `sections` |
|
|
70
|
-
| **Menu** | Навигация в шапке (не popover) | `items` (`value`), `value`, `onSelect`
|
|
71
|
-
| **Dropdown** | Выпадающее меню (items)
|
|
90
|
+
| **Menu** | Навигация в шапке (не popover) | `items` (`value`, `badge`: `0` скрыт), `value`, `onSelect`, `variant` (`plain`/`line`) |
|
|
91
|
+
| **Dropdown** | Выпадающее меню (items, portal) | `label`/`trigger`, `items`/`groups`, `onSelect` |
|
|
92
|
+
| **Popover** | Свой попап в portal | `trigger`, `children`, `align`, `open` |
|
|
72
93
|
| **Breadcrumb** | Хлебные крошки | `items`, `separator`, `collapseAfter` |
|
|
73
|
-
| **
|
|
74
|
-
| **
|
|
94
|
+
| **Sidebar** | Левое меню приложения | `groups`, `value`, `footer` + `footerItems`, `variant` |
|
|
95
|
+
| **PageShell** | Каркас модуля (скролл + footer) | `wide`, `scroll`, `footer`, `children` |
|
|
96
|
+
| **ChatList** | Колонка диалогов | `header`, `footer`, `children` (`List`/`Item`), `bodyRef` |
|
|
97
|
+
| **ChatMessages** | Колонка переписки | `header`, `footer`, `children`, `bodyRef`, `onBodyScroll` |
|
|
98
|
+
| **ChatSplit** | Список + лента | `list`, `messages` |
|
|
99
|
+
| **PageHead** | Заголовок страницы + actions | `title`, `sup` (текст/Badge), `subtitle`, `actions`, `sticky`, `children` |
|
|
75
100
|
| **Section** | Блок контента / auth / подраздел | `title`, `description`, `icon`, `actions`, `variant` |
|
|
76
101
|
| **NavSections** | Сайдбар + lazy-панель | `groups` (`value`), `resolveSection`, `defaultValue` |
|
|
77
102
|
| **TabSections** | Горизонтальные вкладки + lazy | `items` (`value` + `badge`), `resolveSection` |
|
|
78
|
-
| **Card** | Карточка | `title`, `
|
|
79
|
-
| **
|
|
80
|
-
| **
|
|
103
|
+
| **Card** | Карточка | `title`, `--card-pointer` |
|
|
104
|
+
| **ActionGroup** | Тулбар действий | `label`, `items` и/или `children` |
|
|
105
|
+
| **TextGroup** | Title + description | `title`, `description`, `size` |
|
|
106
|
+
| **ItemColumn** | Иконка + столбик в Item | `icon`, `title`, `extra`, `aside`, `description`, `footer` / `meta`, `unread` |
|
|
107
|
+
| **Stat** | Карточка-метрика | `label`, `value`, `tone`, `labelMuted` |
|
|
108
|
+
| **List** / **Item** | Список и строка | List: `row`; Item: `as`, `active`, `variant` (`plain`/`card`) |
|
|
109
|
+
| **Stack** | Ряд / колонка / сетка | `direction`, `columns` (N или `["1fr", "100px"]`), `gap` |
|
|
81
110
|
| **Loading** | Плейсхолдер загрузки | `children` |
|
|
82
111
|
| **EmptyState** | Заглушка «нет данных» | `title`, `description`, `icon`, `actions` |
|
|
83
112
|
| **createLazyResolver** | Кэш `import()` секций | `(key) => import(…)` |
|
|
@@ -86,11 +115,51 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
|
|
|
86
115
|
|
|
87
116
|
`Block` / `Row` / `Column` — deprecated-обёртки над `Stack`. В проектах: `elcrm migrate components`.
|
|
88
117
|
|
|
118
|
+
### Sidebar
|
|
119
|
+
|
|
120
|
+
Левое меню приложения. `variant="cards"` — секции-карточки; `variant="plain"` — плоский список (backoffice). Иконки, колокольчик, тема, карточка пользователя — слоты приложения. Роутер не подключаем.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<Sidebar
|
|
124
|
+
variant="cards"
|
|
125
|
+
value={page}
|
|
126
|
+
onSelect={setPage}
|
|
127
|
+
brand={<Brand as="strong">App</Brand>}
|
|
128
|
+
extra={<Notifications.Bell />}
|
|
129
|
+
groups={[
|
|
130
|
+
{ value: "main", items: [{ value: "home", label: "Обзор", icon: <Icon /> }] },
|
|
131
|
+
{ value: "admin", title: "Админ", items: [{ value: "ops", label: "Операции" }] },
|
|
132
|
+
]}
|
|
133
|
+
footer={<AvatarName title="Паша" description="mail@…" />}
|
|
134
|
+
footerItems={[
|
|
135
|
+
{ value: "docs", label: "Документация", icon: <Icon /> },
|
|
136
|
+
{ value: "theme", label: "Тема", submenu: true },
|
|
137
|
+
{ value: "logout", label: "Выйти", danger: true },
|
|
138
|
+
]}
|
|
139
|
+
onFooterSelect={(value) => console.log(value)}
|
|
140
|
+
version="v 0.2.26"
|
|
141
|
+
/>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Токены: `--sidebar-width`, `--sidebar-item-font-size`, `--sidebar-item-padding`, `--sidebar-icon-size`, `--sidebar-group-font-size`, `--sidebar-item-active`. Для orange active в `plain` переопределите `--sidebar-item-active` в теме приложения.
|
|
145
|
+
|
|
89
146
|
### Stack
|
|
90
147
|
|
|
91
148
|
```tsx
|
|
92
149
|
<Stack gap="0.75rem">{/* колонка */}</Stack>
|
|
93
150
|
<Stack direction="row" gap={8}>{/* ряд */}</Stack>
|
|
151
|
+
<Stack direction="grid" gap={16}>{/* auto-fit, мин. ширина --stack-grid-min */}</Stack>
|
|
152
|
+
<Stack direction="grid" columns={3} gap={12}>{/* stretch; не height:100% у детей */}</Stack>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Card
|
|
156
|
+
|
|
157
|
+
Курсор задаёт приложение токеном `--card-pointer` (`auto` по умолчанию):
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
<Card title="Тенанты" style={{ ["--card-pointer" as string]: "pointer" }}>
|
|
161
|
+
12
|
|
162
|
+
</Card>
|
|
94
163
|
```
|
|
95
164
|
|
|
96
165
|
### RadioGroup
|
|
@@ -255,17 +324,53 @@ const sections = [
|
|
|
255
324
|
|
|
256
325
|
| | Роль |
|
|
257
326
|
| --- | --- |
|
|
258
|
-
| **PageShell** | Каркас модуля: скролл в body, опциональный footer снизу |
|
|
259
|
-
| **PageHead** | Строка title (+ subtitle) слева и actions
|
|
327
|
+
| **PageShell** | Каркас модуля: скролл в body (`scroll`, по умолчанию да), опциональный footer снизу |
|
|
328
|
+
| **PageHead** | Строка title (+ `sup` надстрочно, subtitle) слева и actions справа; `sticky` — прилипает к верху скролла |
|
|
260
329
|
| **Section** | Блок контента / auth-карточка / подраздел с title+description |
|
|
261
330
|
|
|
262
331
|
```tsx
|
|
263
|
-
<PageShell footer={<button type="button">Сохранить</button>}>
|
|
264
|
-
<PageHead title="Товары" subtitle="Каталог" actions={<button>Добавить</button>} />
|
|
332
|
+
<PageShell scroll footer={<button type="button">Сохранить</button>}>
|
|
333
|
+
<PageHead sticky title="Товары" sup={12} subtitle="Каталог" actions={<button>Добавить</button>} />
|
|
265
334
|
<Section title="Фильтры">{/* … */}</Section>
|
|
266
335
|
</PageShell>
|
|
267
336
|
```
|
|
268
337
|
|
|
338
|
+
### ChatList / ChatMessages / ChatSplit
|
|
339
|
+
|
|
340
|
+
Каркас мессенджера, не пузыри и не композер. Строки диалогов — `List` + `Item` (+ `ItemColumn` / `Avatar`). Поиск и ввод — слоты `header` / `footer` приложения.
|
|
341
|
+
|
|
342
|
+
| | Роль |
|
|
343
|
+
| --- | --- |
|
|
344
|
+
| **ChatList** | Колонка диалогов: `header` (поиск), `children` (список), `footer` |
|
|
345
|
+
| **ChatMessages** | Колонка переписки: `header` (собеседник), `children` (лента), `footer` (ввод) |
|
|
346
|
+
| **ChatSplit** | Ряд `list` + `messages` (`--chat-split-gap`) |
|
|
347
|
+
|
|
348
|
+
Токены: `--chat-list-width`, `--chat-split-gap`, `--chat-radius`, `--chat-background`, `--chat-border` (рамка и разделители header/footer), `--chat-header-padding` / `--chat-body-padding` / `--chat-footer-padding`, узкие `--chat-messages-*-padding`. Узкие `--chat-*` по умолчанию = `--shell-*`.
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
<ChatSplit
|
|
352
|
+
list={
|
|
353
|
+
<ChatList header={<SearchField name="q" placeholder="Найти" />}>
|
|
354
|
+
<List>
|
|
355
|
+
<li>
|
|
356
|
+
<Item as="button" active>
|
|
357
|
+
<ItemColumn icon={<Avatar name="Иван" />} title="Иван" description="Привет" />
|
|
358
|
+
</Item>
|
|
359
|
+
</li>
|
|
360
|
+
</List>
|
|
361
|
+
</ChatList>
|
|
362
|
+
}
|
|
363
|
+
messages={
|
|
364
|
+
<ChatMessages
|
|
365
|
+
header={<AvatarName title="Иван" description="в сети" />}
|
|
366
|
+
footer={<TextareaField name="body" placeholder="Сообщение" />}
|
|
367
|
+
>
|
|
368
|
+
{bubbles}
|
|
369
|
+
</ChatMessages>
|
|
370
|
+
}
|
|
371
|
+
/>
|
|
372
|
+
```
|
|
373
|
+
|
|
269
374
|
### AvatarGroup
|
|
270
375
|
|
|
271
376
|
Стопка аватаров через `items`:
|
|
@@ -407,7 +512,7 @@ const people = [
|
|
|
407
512
|
|
|
408
513
|
`Menu` — навигация в шапке (inline, не popover).
|
|
409
514
|
|
|
410
|
-
**Dropdown vs
|
|
515
|
+
**Dropdown vs Popover:** `Dropdown` — пункты `items`/`groups`. `Popover` — свой `children` в portal (тот же z-стек, что у `@elcrm/form`). `@elcrm/overlay` `Menu` — устаревший алиас `Popover`. Select — `@elcrm/form`, не overlay.
|
|
411
516
|
|
|
412
517
|
### Breadcrumb
|
|
413
518
|
|
|
@@ -449,6 +554,8 @@ const people = [
|
|
|
449
554
|
|
|
450
555
|
```tsx
|
|
451
556
|
<Layout
|
|
557
|
+
fixed={["header", "footer", "sidebar"]}
|
|
558
|
+
sidebar={<Sidebar groups={groups} value={page} onSelect={setPage} />}
|
|
452
559
|
header={
|
|
453
560
|
<Header
|
|
454
561
|
brand={<Brand onClick={() => {}}>App</Brand>}
|
|
@@ -473,6 +580,10 @@ const people = [
|
|
|
473
580
|
</Section>
|
|
474
581
|
</Layout>;
|
|
475
582
|
|
|
583
|
+
{/* `fixed` по умолчанию = header + footer + sidebar. Скролл только у children.
|
|
584
|
+
Высота каркаса: `--layout-height` (100dvh). Вложенно: 100% + явная высота родителя.
|
|
585
|
+
`fixed={[]}` — обычный поток страницы. */}
|
|
586
|
+
|
|
476
587
|
{
|
|
477
588
|
/* Экран входа */
|
|
478
589
|
}
|
|
@@ -563,6 +674,7 @@ const resolve = createLazyResolver((value) => import(`./sections/${value}.tsx`))
|
|
|
563
674
|
--shell-color-warning
|
|
564
675
|
--shell-border (= --line, только цвет)
|
|
565
676
|
--shell-background (= --panel)
|
|
677
|
+
--shell-bg фон страницы (sticky PageHead)
|
|
566
678
|
--shell-background-muted / -hover / -selected
|
|
567
679
|
--shell-shadow
|
|
568
680
|
```
|
|
@@ -576,6 +688,8 @@ const resolve = createLazyResolver((value) => import(`./sections/${value}.tsx`))
|
|
|
576
688
|
--avatar-size-s|m|l / --avatar-group-overlap
|
|
577
689
|
--dropdown-min-width / --dropdown-max-width / --dropdown-max-height
|
|
578
690
|
--pricing-card-min-width / --pricing-table-label-width
|
|
691
|
+
--card-pointer
|
|
692
|
+
--stack-grid-min
|
|
579
693
|
--row-gap / --column-gap
|
|
580
694
|
--popup-shadow
|
|
581
695
|
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import e from "react";
|
|
2
|
+
import { c as n } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './ActionGroup.css';const s = "c5r", i = {
|
|
4
|
+
r: s
|
|
5
|
+
};
|
|
6
|
+
function p({
|
|
7
|
+
label: r,
|
|
8
|
+
items: o,
|
|
9
|
+
children: t,
|
|
10
|
+
className: a,
|
|
11
|
+
...c
|
|
12
|
+
}) {
|
|
13
|
+
return /* @__PURE__ */ e.createElement(
|
|
14
|
+
"div",
|
|
15
|
+
{
|
|
16
|
+
className: n(i.r, a),
|
|
17
|
+
role: "toolbar",
|
|
18
|
+
"aria-label": r,
|
|
19
|
+
...c
|
|
20
|
+
},
|
|
21
|
+
o,
|
|
22
|
+
t
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
p as A
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer elcrm-components{.c5r{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--action-group-gap);flex:0 0 auto}}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Ряд действий в строке списка / карточке (`role="toolbar"`).
|
|
4
|
+
*
|
|
5
|
+
* Роли: тулбар иконок справа в Card. Кнопки — `@elcrm/button` / свои слоты,
|
|
6
|
+
* не нативный `<button>` в приложении.
|
|
7
|
+
*
|
|
8
|
+
* @param label подпись для `aria-label`
|
|
9
|
+
* @param items массив узлов (если нет — только `children`)
|
|
10
|
+
* @param children слот кнопок; можно вместе с `items` (сначала items)
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <ActionGroup
|
|
15
|
+
* label="Действия: Иван"
|
|
16
|
+
* items={[
|
|
17
|
+
* <Button key="edit" variant="ghost" onClick={edit}>Изменить</Button>,
|
|
18
|
+
* <Button key="del" variant="ghost" onClick={remove}>Удалить</Button>,
|
|
19
|
+
* ]}
|
|
20
|
+
* />
|
|
21
|
+
*
|
|
22
|
+
* <ActionGroup label="Ещё">
|
|
23
|
+
* <Button variant="ghost">Копировать</Button>
|
|
24
|
+
* </ActionGroup>
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Токены: `--action-group-gap` (иначе `--shell-gap-sm`).
|
|
28
|
+
*/
|
|
29
|
+
export type ActionGroupProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
30
|
+
/** Подпись тулбара (`aria-label`) */
|
|
31
|
+
label: string;
|
|
32
|
+
/** Кнопки массивом узлов */
|
|
33
|
+
items?: React.ReactNode[];
|
|
34
|
+
/** Свои кнопки / слоты */
|
|
35
|
+
children?: React.ReactNode;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Группа действий: `items` и/или `children`.
|
|
39
|
+
*
|
|
40
|
+
* @see Card, Stack, Item
|
|
41
|
+
*/
|
|
42
|
+
export declare function ActionGroup({ label, items, children, className, ...rest }: ActionGroupProps): React.JSX.Element;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import a from "react";
|
|
2
|
+
import { c as p } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './Avatar.css';const h = "car", x = "cav", A = "cas", C = "cam", S = "cal", z = "caa", B = "cai", F = "cab", R = "cao", U = "caf", e = {
|
|
4
|
+
r: h,
|
|
5
|
+
v: x,
|
|
6
|
+
s: A,
|
|
7
|
+
m: C,
|
|
8
|
+
l: S,
|
|
9
|
+
a: z,
|
|
10
|
+
i: B,
|
|
11
|
+
b: F,
|
|
12
|
+
o: R,
|
|
13
|
+
f: U
|
|
14
|
+
};
|
|
15
|
+
function $(t) {
|
|
16
|
+
const n = t.trim();
|
|
17
|
+
if (!n) return "?";
|
|
18
|
+
const s = n.split(/\s+/).filter(Boolean);
|
|
19
|
+
return s.length >= 2 ? (s[0][0] + s[1][0]).toUpperCase() : n.slice(0, 2).toUpperCase();
|
|
20
|
+
}
|
|
21
|
+
function D(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.b, t === "online" ? e.o : e.f),
|
|
32
|
+
"data-status": t,
|
|
33
|
+
"aria-hidden": !0
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function k({
|
|
38
|
+
src: t,
|
|
39
|
+
label: n,
|
|
40
|
+
children: s,
|
|
41
|
+
name: c,
|
|
42
|
+
status: o
|
|
43
|
+
}) {
|
|
44
|
+
const l = t != null && t !== "" ? /* @__PURE__ */ a.createElement("img", { className: e.i, src: t, alt: n ?? "", draggable: !1 }) : s ?? (c != null ? $(c) : "?");
|
|
45
|
+
return /* @__PURE__ */ a.createElement(a.Fragment, null, /* @__PURE__ */ a.createElement("span", { className: e.v }, l), o != null ? /* @__PURE__ */ a.createElement(j, { status: o }) : null);
|
|
46
|
+
}
|
|
47
|
+
function G(t) {
|
|
48
|
+
const {
|
|
49
|
+
src: n,
|
|
50
|
+
alt: s,
|
|
51
|
+
name: c,
|
|
52
|
+
size: o = "m",
|
|
53
|
+
status: l,
|
|
54
|
+
active: i,
|
|
55
|
+
children: b,
|
|
56
|
+
className: y,
|
|
57
|
+
radius: E,
|
|
58
|
+
as: f = "span",
|
|
59
|
+
style: N,
|
|
60
|
+
...m
|
|
61
|
+
} = t, g = o === "s" ? e.s : o === "l" ? e.l : e.m, u = p(e.r, g, i && e.a, y), r = s ?? c, d = { ...D(E), ...N }, v = /* @__PURE__ */ a.createElement(
|
|
62
|
+
k,
|
|
63
|
+
{
|
|
64
|
+
src: n,
|
|
65
|
+
label: r,
|
|
66
|
+
name: c,
|
|
67
|
+
status: l,
|
|
68
|
+
children: b
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
return f === "button" ? /* @__PURE__ */ a.createElement(
|
|
72
|
+
"button",
|
|
73
|
+
{
|
|
74
|
+
type: "button",
|
|
75
|
+
className: u,
|
|
76
|
+
"aria-label": r,
|
|
77
|
+
"data-active": i ? "true" : void 0,
|
|
78
|
+
"data-status": l,
|
|
79
|
+
style: d,
|
|
80
|
+
...m
|
|
81
|
+
},
|
|
82
|
+
v
|
|
83
|
+
) : f === "div" ? /* @__PURE__ */ a.createElement(
|
|
84
|
+
"div",
|
|
85
|
+
{
|
|
86
|
+
className: u,
|
|
87
|
+
role: r ? "img" : void 0,
|
|
88
|
+
"aria-label": r,
|
|
89
|
+
"data-active": i ? "true" : void 0,
|
|
90
|
+
"data-status": l,
|
|
91
|
+
style: d,
|
|
92
|
+
...m
|
|
93
|
+
},
|
|
94
|
+
v
|
|
95
|
+
) : /* @__PURE__ */ a.createElement(
|
|
96
|
+
"span",
|
|
97
|
+
{
|
|
98
|
+
className: u,
|
|
99
|
+
role: r ? "img" : void 0,
|
|
100
|
+
"aria-label": r,
|
|
101
|
+
"data-active": i ? "true" : void 0,
|
|
102
|
+
"data-status": l,
|
|
103
|
+
style: d,
|
|
104
|
+
...m
|
|
105
|
+
},
|
|
106
|
+
v
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
G as A,
|
|
111
|
+
D as a,
|
|
112
|
+
$ as i
|
|
113
|
+
};
|
package/dist/Avatar.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { UiSize } from './types';
|
|
2
3
|
/**
|
|
3
4
|
* Инициалы из имени: «Иван Петров» → «ИП», «admin» → «AD».
|
|
4
5
|
*
|
|
@@ -25,16 +26,23 @@ type Common = {
|
|
|
25
26
|
*/
|
|
26
27
|
name?: string;
|
|
27
28
|
/** Размер: `s` | `m` | `l`. */
|
|
28
|
-
size?:
|
|
29
|
+
size?: UiSize;
|
|
29
30
|
/** Точка статуса (online / offline). */
|
|
30
31
|
status?: AvatarStatus;
|
|
31
32
|
/** Подсветка (например активный пункт шапки). */
|
|
32
33
|
active?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Скругление. Число — пиксели (`10` → `10px`), строка — любой CSS.
|
|
36
|
+
* Пишет `--avatar-radius` на корне (дефолт токена — круг, `999px`).
|
|
37
|
+
*/
|
|
38
|
+
radius?: number | string;
|
|
33
39
|
/** Кастомное содержимое вместо инициалов (если нет `src`). */
|
|
34
40
|
children?: React.ReactNode;
|
|
35
41
|
/** CSS-класс корня. */
|
|
36
42
|
className?: string;
|
|
37
43
|
};
|
|
44
|
+
/** Стиль `--avatar-radius` из пропа `radius`. */
|
|
45
|
+
export declare function avatarRadiusStyle(radius?: number | string): React.CSSProperties | undefined;
|
|
38
46
|
/**
|
|
39
47
|
* Props {@link Avatar}: корень как `button` | `span` | `div`.
|
|
40
48
|
*
|
|
@@ -59,7 +67,8 @@ export type AvatarProps = (Common & React.ButtonHTMLAttributes<HTMLButtonElement
|
|
|
59
67
|
* (`name` у Avatar остаётся только для инициалов);
|
|
60
68
|
* - {@link AvatarGroup} — стопка аватаров из `items`.
|
|
61
69
|
*
|
|
62
|
-
* CSS-токены: `--avatar-*` (в т.ч. `--avatar-size-s|m|l`).
|
|
70
|
+
* CSS-токены: `--avatar-*` (в т.ч. `--avatar-size-s|m|l`, `--avatar-radius`).
|
|
71
|
+
* Скругление: `radius={10}` или `style={{ ["--avatar-radius"]: "10px" }}`.
|
|
63
72
|
*
|
|
64
73
|
* @example
|
|
65
74
|
* ```tsx
|
package/dist/Avatar.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import n from "react";
|
|
2
|
-
import { A as f } from "./Avatar-
|
|
2
|
+
import { A as f } from "./Avatar-loGgFt72.js";
|
|
3
3
|
import { c as u } from "./cx-CcykAxZN.js";
|
|
4
4
|
import './AvatarGroup.css';const G = "cyr", I = "cyi", M = "cyo", R = "cyd", S = "cys", j = "cym", q = "cyl", l = {
|
|
5
5
|
r: G,
|
package/dist/AvatarGroup.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { UiSize } from './types';
|
|
2
3
|
/**
|
|
3
4
|
* Элемент стопки {@link AvatarGroup}.
|
|
4
5
|
*
|
|
@@ -30,7 +31,7 @@ export type AvatarGroupProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
30
31
|
/** Сколько аватаров показать до «+N». По умолчанию все. */
|
|
31
32
|
max?: number;
|
|
32
33
|
/** Размер аватаров: `s` | `m` | `l`. */
|
|
33
|
-
size?:
|
|
34
|
+
size?: UiSize;
|
|
34
35
|
/** Кнопка «+» справа. */
|
|
35
36
|
add?: boolean;
|
|
36
37
|
/** Подпись для кнопки добавления (`aria-label`). */
|
package/dist/AvatarGroup.js
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import t from "react";
|
|
2
|
+
import { A as b } from "./Avatar-loGgFt72.js";
|
|
3
|
+
import { c as g } from "./cx-CcykAxZN.js";
|
|
4
|
+
import './AvatarName.css';const y = "czr", A = "czc", h = "czx", C = "czt", F = "czd", I = "czs", R = "czm", S = "czl", e = {
|
|
5
|
+
r: y,
|
|
6
|
+
c: A,
|
|
7
|
+
x: h,
|
|
8
|
+
t: C,
|
|
9
|
+
d: F,
|
|
10
|
+
s: I,
|
|
11
|
+
m: R,
|
|
12
|
+
l: S
|
|
13
|
+
};
|
|
14
|
+
function $(s, n) {
|
|
15
|
+
if (s != null && s !== "") return s;
|
|
16
|
+
if (typeof n == "string") return n;
|
|
17
|
+
}
|
|
18
|
+
function w(s) {
|
|
19
|
+
const {
|
|
20
|
+
src: n,
|
|
21
|
+
name: c,
|
|
22
|
+
title: m,
|
|
23
|
+
description: i,
|
|
24
|
+
size: a = "m",
|
|
25
|
+
radius: p,
|
|
26
|
+
status: z,
|
|
27
|
+
className: E,
|
|
28
|
+
children: N,
|
|
29
|
+
extra: u,
|
|
30
|
+
as: d = "div",
|
|
31
|
+
...r
|
|
32
|
+
} = s, x = a === "s" ? e.s : a === "l" ? e.l : e.m, l = g(e.r, x, E), f = m ?? (typeof c == "string" ? c : void 0), v = $(c, m), o = /* @__PURE__ */ t.createElement(t.Fragment, null, /* @__PURE__ */ t.createElement(
|
|
33
|
+
b,
|
|
34
|
+
{
|
|
35
|
+
src: n,
|
|
36
|
+
name: v,
|
|
37
|
+
size: a,
|
|
38
|
+
radius: p,
|
|
39
|
+
status: z
|
|
40
|
+
}
|
|
41
|
+
), /* @__PURE__ */ t.createElement("span", { className: e.c }, f != null ? /* @__PURE__ */ t.createElement("span", { className: e.t }, f) : null, i != null ? /* @__PURE__ */ t.createElement("span", { className: e.d }, i) : null, N), u != null ? /* @__PURE__ */ t.createElement("span", { className: e.x }, u) : null);
|
|
42
|
+
return d === "button" ? /* @__PURE__ */ t.createElement(
|
|
43
|
+
"button",
|
|
44
|
+
{
|
|
45
|
+
type: "button",
|
|
46
|
+
className: l,
|
|
47
|
+
...r
|
|
48
|
+
},
|
|
49
|
+
o
|
|
50
|
+
) : d === "a" ? /* @__PURE__ */ t.createElement(
|
|
51
|
+
"a",
|
|
52
|
+
{
|
|
53
|
+
className: l,
|
|
54
|
+
...r
|
|
55
|
+
},
|
|
56
|
+
o
|
|
57
|
+
) : /* @__PURE__ */ t.createElement(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
className: l,
|
|
61
|
+
...r
|
|
62
|
+
},
|
|
63
|
+
o
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
w as A
|
|
68
|
+
};
|
package/dist/AvatarName.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer elcrm-components{.czr{display:inline-flex;align-items:center;gap:var(--shell-gap);max-width:100%;text-align:left;vertical-align:middle}.czr:is(button,a){-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding:0;border:none;background:transparent;font:inherit;color:inherit;cursor:pointer;text-decoration:none}.czc{display:flex;flex-direction:column;gap:2px;min-width:0}.czt{margin:0;font-size:var(--shell-font-size);font-weight:var(--shell-font-weight);color:var(--
|
|
1
|
+
@layer elcrm-components{.czr{display:inline-flex;align-items:center;gap:var(--shell-gap);max-width:100%;text-align:left;vertical-align:middle}.czr:is(button,a){-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding:0;border:none;background:transparent;font:inherit;color:inherit;cursor:pointer;text-decoration:none}.czc{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}.czx{display:inline-flex;flex-shrink:0;align-items:center;margin-left:auto;color:var(--shell-color-muted);line-height:0}.czt{margin:0;font-size:var(--shell-font-size);font-weight:var(--shell-font-weight);color:var(--avatar-name-title-color);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.czd{margin:0;font-size:var(--shell-font-size-sm);font-weight:400;color:var(--avatar-name-description-color);line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.czs{gap:var(--shell-gap-sm)}.czs .czt{font-size:var(--shell-font-size-sm)}.czm{gap:var(--shell-gap)}.czl{gap:var(--shell-gap-lg)}.czl .czt{font-size:var(--shell-font-size-lg)}.czl .czd{font-size:var(--shell-font-size)}}
|
package/dist/AvatarName.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { AvatarStatus } from './Avatar';
|
|
3
|
+
import { UiSize } from './types';
|
|
3
4
|
/**
|
|
4
5
|
* Общие поля блока «аватар + текст».
|
|
5
6
|
*
|
|
@@ -19,13 +20,17 @@ type Common = {
|
|
|
19
20
|
/** Подпись: email, роль… */
|
|
20
21
|
description?: React.ReactNode;
|
|
21
22
|
/** Размер: `s` | `m` | `l`. */
|
|
22
|
-
size?:
|
|
23
|
+
size?: UiSize;
|
|
24
|
+
/** Скругление аватара (`10` → `10px`). Токен `--avatar-radius`. */
|
|
25
|
+
radius?: number | string;
|
|
23
26
|
/** Статус у аватара. */
|
|
24
27
|
status?: AvatarStatus;
|
|
25
28
|
/** CSS-класс корня. */
|
|
26
29
|
className?: string;
|
|
27
30
|
/** Доп. контент в текстовой колонке. */
|
|
28
31
|
children?: React.ReactNode;
|
|
32
|
+
/** Справа: ⋮, шеврон, бейдж. Нет — не рисуем. */
|
|
33
|
+
extra?: React.ReactNode;
|
|
29
34
|
};
|
|
30
35
|
/**
|
|
31
36
|
* Props {@link AvatarName}: корень как `button` | `a` | `div`.
|
|
@@ -50,7 +55,8 @@ export type AvatarNameProps = (Common & React.ButtonHTMLAttributes<HTMLButtonEle
|
|
|
50
55
|
*
|
|
51
56
|
* Канон: `title` / `description` для UI-текста; `name` — только инициалы.
|
|
52
57
|
*
|
|
53
|
-
* CSS-токены: `--
|
|
58
|
+
* CSS-токены: `--avatar-name-title-color`, `--avatar-name-description-color`,
|
|
59
|
+
* аватар — `--avatar-*`.
|
|
54
60
|
*
|
|
55
61
|
* @example
|
|
56
62
|
* ```tsx
|
package/dist/AvatarName.js
CHANGED