elcrm 1.1.21 → 1.1.23

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/dist/index.js CHANGED
@@ -3899,16 +3899,23 @@ function isMainComponentDoc(doc) {
3899
3899
  return false;
3900
3900
  return doc.length > 72;
3901
3901
  }
3902
- function componentDoc(content, slug) {
3902
+ function componentDocs(content, slug) {
3903
3903
  const fnAnchor = "export declare function " + slug;
3904
3904
  const propsAnchor = "export type " + slug + "Props";
3905
3905
  const fnDoc = leadingDoc(content, fnAnchor);
3906
3906
  const propsDoc = leadingDoc(content, propsAnchor);
3907
- if (isMainComponentDoc(fnDoc))
3908
- return fnDoc;
3909
- if (isMainComponentDoc(propsDoc))
3910
- return propsDoc;
3911
- return fnDoc || propsDoc;
3907
+ const examplesDoc = fnDoc.includes("@example") ? fnDoc : propsDoc.includes("@example") ? propsDoc : fnDoc || propsDoc;
3908
+ let descriptionDoc = fnDoc || propsDoc;
3909
+ if (propsDoc.includes("@example")) {
3910
+ descriptionDoc = propsDoc;
3911
+ } else if (isMainComponentDoc(propsDoc) && propsDoc.length > fnDoc.length) {
3912
+ descriptionDoc = propsDoc;
3913
+ } else if (isMainComponentDoc(fnDoc)) {
3914
+ descriptionDoc = fnDoc;
3915
+ } else if (isMainComponentDoc(propsDoc)) {
3916
+ descriptionDoc = propsDoc;
3917
+ }
3918
+ return { descriptionDoc, examplesDoc };
3912
3919
  }
3913
3920
  function leadingDoc(content, anchor) {
3914
3921
  const idx = content.indexOf(anchor);
@@ -3976,15 +3983,15 @@ function parseComponentDts(content, slug) {
3976
3983
  if (!new RegExp("export declare function " + slug + "\\b").test(content)) {
3977
3984
  return null;
3978
3985
  }
3979
- const doc = componentDoc(content, slug);
3986
+ const { descriptionDoc, examplesDoc } = componentDocs(content, slug);
3980
3987
  const propsMatch = content.match(new RegExp("export type " + slug + "Props\\s*=\\s*([\\s\\S]*?);\\s*(?:export|$)"));
3981
3988
  const propsBlock = propsMatch?.[1] ?? "";
3982
3989
  return {
3983
3990
  slug,
3984
3991
  title: slug,
3985
- description: descriptionFromDoc(doc) || slug,
3992
+ description: descriptionFromDoc(descriptionDoc) || slug,
3986
3993
  importFrom: "@elcrm/components",
3987
- examples: examplesFromDoc(doc),
3994
+ examples: examplesFromDoc(examplesDoc),
3988
3995
  props: propsFromPropsBlock(propsBlock)
3989
3996
  };
3990
3997
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elcrm",
3
- "version": "1.1.21",
3
+ "version": "1.1.23",
4
4
  "description": "CLI @elcrm/*: doctor --fix (порядок проекта), update, css, docs, cursor, kit, migrate",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@ Shell UI: оболочка, шапка, секции, списки, Dropdown / R
14
14
  import {
15
15
  Layout, Header, Footer, Brand, Avatar, AvatarName, AvatarGroup, Badge,
16
16
  Menu, Dropdown, Section, NavSections, TabSections, Breadcrumb, Stack,
17
- Card, Stat, ActionGroup, TextGroup, ItemColumn, List, Item, PageHead, PageShell, ChatList, ChatMessages, ChatSplit, Sidebar, Popover,
17
+ Card, Stat, ActionGroup, Toolbar, TextGroup, ItemColumn, List, Item, PageHead, PageShell, ChatList, ChatMessages, ChatSplit, Sidebar, Popover,
18
18
  Healthmap, PanelInfo,
19
19
  Loading, EmptyState, RadioGroup, PricingGroup, PricingTable,
20
20
  createLazyResolver,
@@ -31,13 +31,15 @@ import type { PricingPlan, HealthmapData } from "@elcrm/components";
31
31
  - **PageHead** — title + actions. `sup` — текст или Badge сразу после title (надстрочно; `0` скрыт). `sticky` — шапка прилипает к верху скролла (фон `--shell-bg`). `children` — фильтры/поиск под заголовком, отступ `--shell-gap`.
32
32
  - **Section** — блок контента / auth.
33
33
  - **Layout / Header** — оболочка приложения. `Layout sidebar={<Sidebar />}` — ряд: меню слева + контент. `fixed={["header", "footer", "sidebar"]}` (по умолчанию) — эти слоты не скроллятся, крутится только `children`. `fixed={[]}` — поток страницы. Высота каркаса: `--layout-height` (`100dvh`).
34
- - **Sidebar** — левое меню. `variant="cards"` (карточки) или `"plain"` (плоский backoffice). `groups` + `value`. Слоты: `brand`, `extra`, `footer`. Меню аккаунта: `footerItems` (как у Dropdown) + `onFooterSelect` клик по `footer` открывает панель, справа ⋮. Пункты: `--sidebar-item-font-size`, `--sidebar-item-padding`, `--sidebar-icon-size`, `--sidebar-group-font-size`. Не наследуют `body` 18px (`Item` `font: inherit` перебит).
34
+ - **Sidebar** — левое меню. `variant="cards"` (карточки) или `"plain"` (плоский backoffice). `groups` + `value`. Подменю: у пункта `items` + `defaultOpen` (открыто/скрыто при старте). Контроль: `open` / `onOpenChange` (массив `value` родителей). Клик по родителю только раскрывает; `onSelect` у листьев. Если выбран потомок — ветка открывается. Слоты: `brand`, `extra`, `footer`. Меню аккаунта: `footerItems` + `onFooterSelect`. Отступ вложенных: `--sidebar-indent`.
35
+ - **NavSections** — сайдбар страницы + lazy-панель. Подменю как у Sidebar: `items` + `defaultOpen`, `open` / `onOpenChange`. Панель грузится по `value` листа (`resolveSection`). Клик по родителю только раскрывает.
35
36
  - **Stack** — ряд/колонка/сетка (`direction="row"|"column"|"grid"`). Не `Block` / `Row` / `Column`. Сетка: `auto-fit` + `--stack-grid-min`, либо `columns={4}` / `columns={["1fr", "100px", "auto"]}`. С `columns` ряды `stretch` (карточки не наезжают); в одну линию — `style={{ alignItems: "center" }}`. Не `height: 100%` у детей сетки.
36
37
  - **Card** — курсор: `--card-pointer` (`auto` / `pointer`). Сетка Stack: `--stack-grid-min`.
37
38
  - **Stat** — метрика: `label` + `value`, `tone` как у Badge, `labelMuted`. Не собирать из `span` в приложении.
38
39
  - **Healthmap** — теплокарта дней (`data.days`). Данные грузит приложение; слоты `brandName` / `brandSub` / `unit` / `hint`. Токены `--healthmap-cell`, `--healthmap-gap`, `--healthmap-l0`…`l4`.
39
40
  - **PanelInfo** — выезд справа (шапка / body / footer). На узком экране — оверлей. Слоты: `children`, `footer`, `headerExtra`, `close`. Токены `--panel-info-width`, `--panel-info-width-min`, `--panel-info-overlay-max`, `--panel-info-backdrop`.
40
- - **ActionGroup** — тулбар: `label` + `items` (узлы) и/или `children`. Кнопки — `@elcrm/button`, не нативный `<button>`.
41
+ - **ActionGroup** — ряд иконок в карточке / строке: `label` + `items` и/или `children`. Кнопки — `@elcrm/button`.
42
+ - **Toolbar** — фильтры над списком. `size` (`s`/`m`/`l`, по умолчанию `s`) наследуют вложенные `*Field`, `Search`, `Button`, `Dropdown` (свой `size` важнее). `align`: `start` / `center` / `end` / `between`. `width`: `l` (100%, по умолчанию) или `s` (по контенту, не растягивать). Токены `--toolbar-gap`. Не `ActionGroup`.
41
43
  - **TextGroup** — две строки: `title` + `description` (без аватара; с аватаром — AvatarName).
42
44
  - **Item** — строка списка. `as` button/a/li, `active`. `variant="card"` — рамка и muted-фон (идеи / тикеты / inbox). Не дублировать `.desk-row` в приложении.
43
45
  - **ItemColumn** — внутри Item: `icon` слева, столбик `title` / `description` / `footer` или `meta`, `extra` — бейджи справа сверху, `aside` — колонка справа. `unread` — жирный title. Title: `--item-column-title-size` / `--item-column-title-line` (в console = `--button-size-s` / `--button-line-height`).
@@ -45,7 +47,7 @@ import type { PricingPlan, HealthmapData } from "@elcrm/components";
45
47
  ## Меню
46
48
 
47
49
  - **Menu** — inline-навигация в шапке. `items`: `value` + `label` + `badge` (`0` не показываем, иначе `Badge` size s). `variant="line"` — вкладки с чертой снизу (не ломать pill селекторами страницы).
48
- - **Dropdown** — выпадающее меню по `items`/`groups` (portal). `placement="auto"|"top"|"bottom"` — куда открывать панель (футер Sidebar — `top`).
50
+ - **Dropdown** — выпадающее меню по `items`/`groups` (portal). `placement="auto"|"top"|"bottom"` — куда открывать панель (футер Sidebar — `top`). `size` — высота триггера; в `Toolbar` берётся сам.
49
51
  - **Popover** — свой попап в portal (якорь + `children`). z-index: `acquireOverlayZIndex` (тот же стек, что у form). Не `@elcrm/overlay` `Menu`. `placement` как у Dropdown: позиция по **реальной** высоте панели, не по `maxHeight` 420.
50
52
 
51
53
  ## Avatar
@@ -65,6 +67,7 @@ import type { PricingPlan, HealthmapData } from "@elcrm/components";
65
67
  - `--layout-padding` / `--layout-margin` / `--layout-max-width` — отступы и макс. ширина корня Layout (`0` / `none` = на весь вьюпорт).
66
68
  - `--header-border` — рамка Header (shorthand: все стороны и толщина). Только снизу: `none` + линия через `--header-shadow`.
67
69
  - `--header-nav-justify` — выравнивание `nav` в шапке (`flex-start` / `center` / `flex-end`).
70
+ - `<Toolbar size="s" width="l" align="start">` — один размер для Search / `*Field` / Button / Dropdown. `width="s"` — ряд по контенту. Свой `size` у ребёнка важнее.
68
71
  - Узкие токены (`--chat-*`, `--sidebar-*`, `--item-column-*`) по умолчанию = `--shell-*`. Переопределяй только отличие.
69
72
  - Задать `--shell-color` / `--shell-padding` / `--shell-radius` один раз — большинство компонентов подхватят.
70
73
  - `--popup-shadow` общий с `@elcrm/form`.
@@ -23,7 +23,8 @@ const form = useForm({ login: "", password: "" });
23
23
 
24
24
  Канонические поля: `StringField`, `PasswordField`, `TextareaField`, `NumberField`, `PercentField`, `MoneyField`, `MaskField`, `PhoneField`, `EmailField`, `UrlField`, `DateField`, `TimeField`, `SelectField`, `OptionsField`, `ModalField`, `RangeField`, `CheckField`, `RadioField`, `TagsField`, `RatingField`, `CodeField`, `FileField`, `DragDropField`, `RichTextField`, `ColorField`, `HiddenField`, `DisplayField`, `SearchField`, `TabsField`.
25
25
 
26
- `disabled`, `size` (`"s"` | `"m"` | `"l"`) — у всех видимых `*Field` (кроме `HiddenField`).
26
+ `disabled`, `size` (`"s"` | `"m"` | `"l"`) — у всех видимых `*Field` (кроме `HiddenField`).
27
+ В ряду фильтров — `<Toolbar size="s">` из `@elcrm/components`: вложенные поля/Search/Button берут этот `size`.
27
28
 
28
29
  `PasswordField`: `generate` — кнопка случайного пароля. `true` → `Az09#` (A–Z, a–z, 0–9, символы). `generate="A"` только заглавные, `"59"` цифры 5–9, `"#"` только символы. Длина — `maxLength` или 12.
29
30
 
@@ -10,7 +10,8 @@
10
10
  function EditModal({ id, onClose }: { id: number; onClose?: () => void }) {
11
11
  return (
12
12
  <Modal.Main title="Редактирование" onClose={onClose} size="m">
13
- <Modal.Scroll>…</Modal.Scroll>
13
+ {/* scroll + padding по умолчанию — без Modal.Scroll */}
14
+ <p>…</p>
14
15
  </Modal.Main>
15
16
  );
16
17
  }
@@ -25,12 +26,30 @@ Open({ id: 123 });
25
26
 
26
27
  | API | Зачем |
27
28
  | --- | --- |
28
- | `Modal.Main` | окно + оверлей: `title`, `size`, `button`, `tabs`, `menu`, `shake`, `escape` |
29
- | `Modal.Panel` | без оверлея |
30
- | `Modal.Scroll` / `Center` / `Form` / `Column` / `List` | раскладка |
29
+ | `Modal.Main` | окно + оверлей: `title`, `size`, `button`, `tabs`, `menu`, `shake`, `escape`, **`scroll`**, **`padding`** |
30
+ | `Modal.Panel` | без оверлея (те же props) |
31
+ | `Modal.Scroll` | редко: сырой layout или `padding={false}` внутри |
32
+ | `Center` / `Form` / `Column` / `List` | раскладка |
31
33
  | `Modal.Close(name?)` | одно или все |
32
34
  | `Modal.Shake` | тряска |
33
35
 
36
+ ### Отступы (по умолчанию уже есть)
37
+
38
+ Один токен **`--modal-padding`** (default `30px`) — шапка, тело, футер, Form, List.
39
+
40
+ | Как | Пример |
41
+ | --- | --- |
42
+ | Тема на всё приложение | `--modal-padding: 24px` в `theme-*.css` |
43
+ | Одно окно | `<Modal.Main padding="20px">` |
44
+ | Без отступов тела | `padding={false}` |
45
+ | Выключить авто-Scroll | `scroll={false}` |
46
+
47
+ `Modal.Main` / `Panel` сами оборачивают children в `Modal.Scroll` с отступами.
48
+
49
+ - Обычный контент — **не** пишите `Modal.Scroll`.
50
+ - Единственный `Modal.List` или `Modal.Form` — без внешнего Scroll (свои отступы через тот же токен).
51
+ - Если единственный ребёнок уже `Modal.Scroll` — второй раз не оборачиваем.
52
+
34
53
  `size`: `a` auto · `s` 400 · `m` 500 · `l` 800 · `x` 1000 · `f` full · `p` page (весь экран поверх страницы).
35
54
  `escape` закрывает верхнее; клик по оверлею при `shake` (default true) трясёт, не закрывает; крестик закрывает всегда. Для `size="p"` обычно `shake={false}`.
36
55
 
@@ -58,3 +77,4 @@ Card.Open({ id: 1 }, { origin: event.currentTarget });
58
77
  - `key={Date.now()}` на модалке.
59
78
  - Без `Modal.Init`.
60
79
  - Нативные input внутри модалки — `@elcrm/form`.
80
+ - Ручные `padding: 0` / пустой layout «в край» без нужды — дефолт уже с отступами.
@@ -134,6 +134,7 @@
134
134
  --item-column-title-line: 1.3;
135
135
  --stat-value-size: 1.75rem;
136
136
  --action-group-gap: var(--shell-gap-sm);
137
+ --toolbar-gap: var(--shell-gap-sm);
137
138
  --sidebar-width: 260px;
138
139
  --sidebar-pad-x: var(--shell-gap);
139
140
  --sidebar-pad-y: var(--shell-gap-lg);
@@ -142,6 +143,7 @@
142
143
  --sidebar-item-padding: 6px 10px;
143
144
  --sidebar-icon-size: 16px;
144
145
  --sidebar-group-font-size: 11px;
146
+ --sidebar-indent: 14px;
145
147
  --pricing-card-min-width: 16rem;
146
148
  --pricing-table-label-width: 10rem;
147
149
  --chat-list-width: 340px;
@@ -145,6 +145,7 @@
145
145
  --item-column-title-line: 1.3;
146
146
  --stat-value-size: 1.75rem;
147
147
  --action-group-gap: var(--shell-gap-sm);
148
+ --toolbar-gap: var(--shell-gap-sm);
148
149
  --sidebar-width: 248px;
149
150
  --sidebar-pad-x: var(--shell-gap);
150
151
  --sidebar-pad-y: var(--shell-gap-lg);
@@ -153,6 +154,7 @@
153
154
  --sidebar-item-padding: 6px 10px;
154
155
  --sidebar-icon-size: 16px;
155
156
  --sidebar-group-font-size: 11px;
157
+ --sidebar-indent: 14px;
156
158
  --pricing-card-min-width: 16rem;
157
159
  --pricing-table-label-width: 10rem;
158
160
  --chat-list-width: 340px;