@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.
Files changed (135) hide show
  1. package/README.md +130 -16
  2. package/dist/ActionGroup-ChKFW0mt.js +27 -0
  3. package/dist/ActionGroup.css +1 -0
  4. package/dist/ActionGroup.d.ts +42 -0
  5. package/dist/ActionGroup.js +4 -0
  6. package/dist/Avatar-loGgFt72.js +113 -0
  7. package/dist/Avatar.d.ts +11 -2
  8. package/dist/Avatar.js +3 -2
  9. package/dist/{AvatarGroup-BAWas4pU.js → AvatarGroup-D1gnT3Od.js} +1 -1
  10. package/dist/AvatarGroup.d.ts +2 -1
  11. package/dist/AvatarGroup.js +1 -1
  12. package/dist/AvatarName-0BzyfpMq.js +68 -0
  13. package/dist/AvatarName.css +1 -1
  14. package/dist/AvatarName.d.ts +8 -2
  15. package/dist/AvatarName.js +1 -1
  16. package/dist/Badge-jp-9iNNL.js +28 -0
  17. package/dist/Badge.css +1 -1
  18. package/dist/Badge.d.ts +33 -17
  19. package/dist/Badge.js +1 -1
  20. package/dist/{Block-BAXh0PPp.js → Block-Bu7NEA0g.js} +1 -1
  21. package/dist/Block.js +1 -1
  22. package/dist/Card.css +1 -1
  23. package/dist/Card.d.ts +8 -1
  24. package/dist/ChatFrame-BW2TmCkZ.js +37 -0
  25. package/dist/ChatFrame.css +1 -0
  26. package/dist/ChatList.d.ts +9 -0
  27. package/dist/ChatList.js +7 -0
  28. package/dist/ChatMessages-B8rdXiK-.js +19 -0
  29. package/dist/ChatMessages.d.ts +9 -0
  30. package/dist/ChatMessages.js +4 -0
  31. package/dist/ChatSplit-DNnu3TNq.js +21 -0
  32. package/dist/CheckIcon-FdB8fMEF.js +17 -0
  33. package/dist/CheckIcon.d.ts +8 -0
  34. package/dist/{Column-DDaqX-A7.js → Column-BiogBDf8.js} +1 -1
  35. package/dist/Column.js +1 -1
  36. package/dist/Dropdown-4yUB2tk1.js +142 -0
  37. package/dist/Dropdown.css +1 -1
  38. package/dist/Dropdown.d.ts +3 -3
  39. package/dist/Dropdown.js +1 -1
  40. package/dist/Footer.css +1 -1
  41. package/dist/Item-D4okhsfW.js +46 -0
  42. package/dist/Item.css +1 -1
  43. package/dist/Item.d.ts +8 -1
  44. package/dist/Item.js +1 -1
  45. package/dist/ItemColumn-M7a9L8CI.js +41 -0
  46. package/dist/ItemColumn.css +1 -0
  47. package/dist/ItemColumn.d.ts +62 -0
  48. package/dist/ItemColumn.js +4 -0
  49. package/dist/Layout-mi6Ghayv.js +31 -0
  50. package/dist/Layout.css +1 -1
  51. package/dist/Layout.d.ts +28 -6
  52. package/dist/Layout.js +1 -1
  53. package/dist/Loading-DkkG7RcE.js +16 -0
  54. package/dist/Loading.css +1 -1
  55. package/dist/Loading.d.ts +1 -1
  56. package/dist/Loading.js +1 -1
  57. package/dist/Menu-DuXQv-zT.js +66 -0
  58. package/dist/Menu.css +1 -1
  59. package/dist/Menu.d.ts +15 -3
  60. package/dist/Menu.js +1 -1
  61. package/dist/NavSections-BpyZPimZ.js +79 -0
  62. package/dist/NavSections.css +1 -1
  63. package/dist/NavSections.js +1 -1
  64. package/dist/PageHead-DgaJ1mXY.js +31 -0
  65. package/dist/PageHead.css +1 -1
  66. package/dist/PageHead.d.ts +28 -6
  67. package/dist/PageHead.js +1 -1
  68. package/dist/{PageShell-CghThHS3.js → PageShell-D9EZiO8o.js} +13 -11
  69. package/dist/PageShell.css +1 -1
  70. package/dist/PageShell.d.ts +11 -6
  71. package/dist/PageShell.js +1 -1
  72. package/dist/Popover-D0m5JWIt.js +142 -0
  73. package/dist/Popover.css +1 -0
  74. package/dist/Popover.d.ts +54 -0
  75. package/dist/Popover.js +4 -0
  76. package/dist/PricingGroup-Czuhe81Z.js +120 -0
  77. package/dist/PricingGroup.css +1 -1
  78. package/dist/PricingGroup.js +1 -1
  79. package/dist/{PricingTable-DALddSwM.js → PricingTable-BuLC0opK.js} +69 -75
  80. package/dist/PricingTable.css +1 -1
  81. package/dist/PricingTable.js +1 -1
  82. package/dist/RadioGroup-fcXzhvMu.js +129 -0
  83. package/dist/RadioGroup.css +1 -1
  84. package/dist/RadioGroup.js +1 -1
  85. package/dist/{Row--HIe6vb9.js → Row-DsQOVn18.js} +1 -1
  86. package/dist/Row.js +1 -1
  87. package/dist/Sidebar-CBYq1ad6.js +107 -0
  88. package/dist/Sidebar.css +1 -0
  89. package/dist/Sidebar.d.ts +99 -0
  90. package/dist/Sidebar.js +4 -0
  91. package/dist/Stack-XkmPSswj.js +53 -0
  92. package/dist/Stack.css +1 -1
  93. package/dist/Stack.d.ts +16 -4
  94. package/dist/Stack.js +3 -2
  95. package/dist/Stat-C5iZyEoo.js +27 -0
  96. package/dist/Stat.css +1 -0
  97. package/dist/Stat.d.ts +39 -0
  98. package/dist/Stat.js +4 -0
  99. package/dist/TabSections-DN7yVJoF.js +69 -0
  100. package/dist/TabSections.css +1 -1
  101. package/dist/TabSections.js +1 -1
  102. package/dist/TextGroup-DmhuyMR1.js +23 -0
  103. package/dist/TextGroup.css +1 -0
  104. package/dist/TextGroup.d.ts +36 -0
  105. package/dist/TextGroup.js +4 -0
  106. package/dist/badgeText-CRzlIzi3.js +8 -0
  107. package/dist/badgeText.d.ts +4 -0
  108. package/dist/dark.css +13 -0
  109. package/dist/index.css +31 -17
  110. package/dist/index.d.ts +27 -4
  111. package/dist/index.es.d.ts +82 -0
  112. package/dist/index.es.js +83 -59
  113. package/dist/light.css +13 -0
  114. package/dist/overlayZ.d.ts +9 -0
  115. package/dist/rovingKeys-CGlfTtNQ.js +15 -0
  116. package/dist/rovingKeys.d.ts +13 -0
  117. package/dist/tokens.css +44 -4
  118. package/dist/types.d.ts +2 -0
  119. package/dist/useControllableValue-CWqH1kpM.js +10 -0
  120. package/dist/useControllableValue.d.ts +4 -0
  121. package/package.json +6 -2
  122. package/dist/Avatar-CyNXYvsa.js +0 -101
  123. package/dist/AvatarName-DhD3TytF.js +0 -56
  124. package/dist/Badge-l6nUkl-H.js +0 -22
  125. package/dist/Dropdown-DQlDiWCR.js +0 -168
  126. package/dist/Item-CaU4RW2G.js +0 -38
  127. package/dist/Layout-Xb3-xsmA.js +0 -18
  128. package/dist/Loading-ChGq9peU.js +0 -15
  129. package/dist/Menu-DRBHvSdJ.js +0 -40
  130. package/dist/NavSections-NDcIDD6H.js +0 -59
  131. package/dist/PageHead-Baz9BLuA.js +0 -22
  132. package/dist/PricingGroup-DgcdVSq8.js +0 -126
  133. package/dist/RadioGroup-D2UUOTkY.js +0 -138
  134. package/dist/Stack-b0-XWwQ1.js +0 -31
  135. package/dist/TabSections-DL7DufKU.js +0 -50
@@ -0,0 +1,62 @@
1
+ import { default as React } from 'react';
2
+ import { UiSize } from './types';
3
+ /**
4
+ * Колонка содержимого строки списка: иконка слева, текст столбиком, слоты справа.
5
+ *
6
+ * Роли: внутри {@link Item} / {@link Card} (идеи, тикеты, inbox).
7
+ * Не заменяет Item (клик / active) и не AvatarName (там фото).
8
+ *
9
+ * @param icon кружок слева (иконка приложения)
10
+ * @param title первая строка
11
+ * @param extra бейджи в правом верхнем углу строки title
12
+ * @param aside колонка справа на всю высоту (дата, действия)
13
+ * @param description превью / вторая строка
14
+ * @param footer третья строка (мета)
15
+ * @param meta куски футера через «·» (если нет footer)
16
+ * @param size `s` | `m` | `l`
17
+ * @param unread жирный title (непрочитанное)
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <Item as="button" onClick={open}>
22
+ * <ItemColumn
23
+ * icon={<Icon />}
24
+ * title="Тема"
25
+ * extra={<Badge size="s" tone="accent">Новые</Badge>}
26
+ * description="Превью текста…"
27
+ * meta={["★ 2", "3 комм.", "Иван"]}
28
+ * />
29
+ * </Item>
30
+ * ```
31
+ *
32
+ * Токены: `--item-column-gap`, `--item-column-icon-size`, `--item-column-icon-radius`,
33
+ * `--item-column-title-size`, `--item-column-title-line`.
34
+ */
35
+ export type ItemColumnProps = Omit<React.HTMLAttributes<HTMLDivElement>, "title"> & {
36
+ /** Иконка слева */
37
+ icon?: React.ReactNode;
38
+ /** Заголовок */
39
+ title?: React.ReactNode;
40
+ /** Справа сверху у title (Badge, счётчик) */
41
+ extra?: React.ReactNode;
42
+ /** Правая колонка (верхний край) */
43
+ aside?: React.ReactNode;
44
+ /** Описание / превью */
45
+ description?: React.ReactNode;
46
+ /** Подвал целиком */
47
+ footer?: React.ReactNode;
48
+ /** Куски подвала через точку — если нет `footer` */
49
+ meta?: React.ReactNode[];
50
+ /** Размер текста */
51
+ size?: UiSize;
52
+ /** Жирный title (непрочитанное) */
53
+ unread?: boolean;
54
+ /** Доп. строки под footer */
55
+ children?: React.ReactNode;
56
+ };
57
+ /**
58
+ * Иконка + колонка title / description / footer (+ extra / aside).
59
+ *
60
+ * @see Item, TextGroup, AvatarName, Badge
61
+ */
62
+ export declare function ItemColumn({ icon, title, extra, aside, description, footer, meta, size, unread, children, className, ...rest }: ItemColumnProps): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { I as e } from "./ItemColumn-M7a9L8CI.js";
2
+ export {
3
+ e as ItemColumn
4
+ };
@@ -0,0 +1,31 @@
1
+ import l from "react";
2
+ import { c as a } from "./cx-CcykAxZN.js";
3
+ import './Layout.css';const f = "clr", h = "clx", k = "clm", x = "cls", F = "clb", L = "cla", g = "clk", y = "clh", D = "clf", n = {
4
+ r: f,
5
+ x: h,
6
+ m: k,
7
+ s: x,
8
+ b: F,
9
+ a: L,
10
+ k: g,
11
+ h: y,
12
+ f: D
13
+ }, p = ["header", "footer", "sidebar"];
14
+ function o(e, c) {
15
+ return (e ?? p).includes(c);
16
+ }
17
+ function I({
18
+ sidebar: e,
19
+ header: c,
20
+ footer: t,
21
+ fixed: s,
22
+ children: b,
23
+ className: m,
24
+ ...r
25
+ }) {
26
+ const u = c != null && o(s, "header"), i = t != null && o(s, "footer"), v = e != null && o(s, "sidebar"), E = (s ?? p).length > 0, N = /* @__PURE__ */ l.createElement("main", { className: n.m }, c != null && !u ? c : null, b, t != null && !i ? t : null), d = /* @__PURE__ */ l.createElement(l.Fragment, null, u ? /* @__PURE__ */ l.createElement("div", { className: n.h }, c) : null, N, i ? /* @__PURE__ */ l.createElement("div", { className: n.f }, t) : null);
27
+ return e != null ? /* @__PURE__ */ l.createElement("div", { className: a(n.r, n.s, E && n.x, m), ...r }, /* @__PURE__ */ l.createElement("div", { className: a(n.a, v && n.k) }, e), /* @__PURE__ */ l.createElement("div", { className: n.b }, d)) : /* @__PURE__ */ l.createElement("div", { className: a(n.r, E && n.x, m), ...r }, d);
28
+ }
29
+ export {
30
+ I as L
31
+ };
package/dist/Layout.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.clr{display:flex;flex-direction:column;flex:1 1 auto;height:100%;min-height:0;max-height:100%;overflow:hidden;background:var(--shell-background-muted);gap:0}.clm{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}}
1
+ @layer elcrm-components{.clr{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;box-sizing:border-box;background:var(--shell-background-muted);gap:0}.clx{height:var(--layout-height);max-height:var(--layout-height);overflow:hidden}.clm{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:auto;overscroll-behavior:contain}.clr:not(.clx){height:auto;max-height:none;overflow:visible}.clr:not(.clx) .clm{overflow:visible;flex:0 1 auto;min-height:auto}.cls{flex-direction:row;align-items:stretch}.clb{display:flex;flex-direction:column;flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.clr:not(.clx) .clb{overflow:visible}.cla{display:flex;flex-direction:column;flex:0 0 auto;min-height:0;align-self:stretch}.clk{overflow:hidden}.cla:not(.clk){overflow:auto}.cla>*{flex:1 1 auto;min-height:0;height:auto;max-height:100%}.clh,.clf{flex:0 0 auto;min-width:0}}
package/dist/Layout.d.ts CHANGED
@@ -1,30 +1,52 @@
1
1
  import { default as React } from 'react';
2
+ /** Слот каркаса, который можно закрепить вне скролла `children`. */
3
+ export type LayoutFixedSlot = "header" | "footer" | "sidebar";
2
4
  /**
3
5
  * Оболочка приложения: шапка, основной контент, подвал.
4
6
  *
5
7
  * Роли: Layout = каркас страницы; внутри — Header / Section / PageShell / Footer.
6
8
  * Используйте как корневой каркас маршрута или приложения.
7
9
  *
10
+ * `fixed` — какие слоты не крутятся вместе со страницей.
11
+ * По умолчанию `["header", "footer", "sidebar"]`: скроллится только `children` в `<main>`.
12
+ * Пустой массив — обычный поток, скролл у предка.
13
+ *
14
+ * Высота каркаса: `--layout-height` (по умолчанию `100dvh`).
15
+ * Вложенный стенд: `--layout-height: 100%` у родителя с явной высотой.
16
+ *
8
17
  * @example
9
18
  * ```tsx
10
- * <Layout header={<Header brand="App" />} footer={<Footer>© 2026</Footer>}>
11
- * <Section title="Главная">…</Section>
19
+ * <Layout
20
+ * fixed={["header", "footer", "sidebar"]}
21
+ * sidebar={<Sidebar groups={groups} value={page} onSelect={setPage} />}
22
+ * header={<Header brand="App" />}
23
+ * footer={<Footer>© elCRM</Footer>}
24
+ * >
25
+ * <PageShell>…</PageShell>
12
26
  * </Layout>
13
27
  * ```
14
28
  *
15
- * Токены: `--shell-background-muted` (фон main), прочие `--shell-*`.
29
+ * Токены: `--layout-height`, `--shell-background-muted`, прочие `--shell-*`.
16
30
  */
17
31
  export type LayoutProps = React.HTMLAttributes<HTMLDivElement> & {
32
+ /** Левая колонка (обычно `<Sidebar />`) — ряд: сайдбар + контент */
33
+ sidebar?: React.ReactNode;
18
34
  /** Слот шапки (обычно `<Header />`) */
19
35
  header?: React.ReactNode;
20
36
  /** Слот подвала (обычно `<Footer />`) */
21
37
  footer?: React.ReactNode;
38
+ /**
39
+ * Какие слоты зафиксированы (не прокручиваются).
40
+ * По умолчанию все: `header`, `footer`, `sidebar`.
41
+ * `[]` — ничего не фиксировать.
42
+ */
43
+ fixed?: LayoutFixedSlot[];
22
44
  /** Основной контент страницы (рендерится в `<main>`) */
23
45
  children?: React.ReactNode;
24
46
  };
25
47
  /**
26
- * Оболочка страницы: header + main + footer.
48
+ * Оболочка: опционально `sidebar` слева, затем header + main + footer.
27
49
  *
28
- * @see Header, Footer, Section, PageShell
50
+ * @see Sidebar, Header, Footer, Section, PageShell
29
51
  */
30
- export declare function Layout({ header, footer, children, className, ...rest }: LayoutProps): React.JSX.Element;
52
+ export declare function Layout({ sidebar, header, footer, fixed, children, className, ...rest }: LayoutProps): React.JSX.Element;
package/dist/Layout.js CHANGED
@@ -1,4 +1,4 @@
1
- import { L as r } from "./Layout-Xb3-xsmA.js";
1
+ import { L as r } from "./Layout-mi6Ghayv.js";
2
2
  export {
3
3
  r as Layout
4
4
  };
@@ -0,0 +1,16 @@
1
+ import e from "react";
2
+ import { c as s } from "./cx-CcykAxZN.js";
3
+ import './Loading.css';const i = "cgr", n = "cgi", r = {
4
+ r: i,
5
+ i: n
6
+ };
7
+ function l({
8
+ children: t = "Загрузка…",
9
+ className: a,
10
+ ...c
11
+ }) {
12
+ return /* @__PURE__ */ e.createElement("div", { className: s(r.r, a), role: "status", ...c }, /* @__PURE__ */ e.createElement("span", { className: r.i, "aria-hidden": !0 }), t);
13
+ }
14
+ export {
15
+ l as L
16
+ };
package/dist/Loading.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.cgr{display:flex;align-items:center;justify-content:center;padding:var(--shell-gap-lg);color:var(--shell-color-muted)}}
1
+ @layer elcrm-components{.cgr{display:flex;align-items:center;justify-content:center;gap:var(--shell-gap-sm);padding:var(--shell-gap-lg);color:var(--shell-color-muted)}.cgi{box-sizing:border-box;width:var(--shell-icon-size);height:var(--shell-icon-size);border:2px solid var(--shell-border);border-top-color:var(--shell-color-accent);border-radius:var(--avatar-radius);animation:cgspin .7s linear infinite}}@keyframes cgspin{to{transform:rotate(360deg)}}
package/dist/Loading.d.ts CHANGED
@@ -17,7 +17,7 @@ export type LoadingProps = React.HTMLAttributes<HTMLDivElement> & {
17
17
  children?: React.ReactNode;
18
18
  };
19
19
  /**
20
- * Плейсхолдер загрузки.
20
+ * Плейсхолдер загрузки со спиннером.
21
21
  *
22
22
  * @see EmptyState, PageShell
23
23
  */
package/dist/Loading.js CHANGED
@@ -1,4 +1,4 @@
1
- import { L as r } from "./Loading-ChGq9peU.js";
1
+ import { L as r } from "./Loading-DkkG7RcE.js";
2
2
  export {
3
3
  r as Loading
4
4
  };
@@ -0,0 +1,66 @@
1
+ import d from "react";
2
+ import { B as g } from "./Badge-jp-9iNNL.js";
3
+ import { b as x } from "./badgeText-CRzlIzi3.js";
4
+ import { c as i } from "./cx-CcykAxZN.js";
5
+ import { h as y } from "./rovingKeys-CGlfTtNQ.js";
6
+ import './Menu.css';const h = "cmr", k = "cmv", E = "cmi", N = "cmd", w = "cma", B = "cmu", n = {
7
+ r: h,
8
+ v: k,
9
+ i: E,
10
+ d: N,
11
+ a: w,
12
+ u: B
13
+ };
14
+ function D({
15
+ items: s,
16
+ vertical: l,
17
+ variant: u = "plain",
18
+ value: e,
19
+ onSelect: c,
20
+ children: b,
21
+ className: p,
22
+ ...m
23
+ }) {
24
+ const f = (s ?? []).filter((a) => !a.disabled).map((a) => a.value), o = u === "line";
25
+ return /* @__PURE__ */ d.createElement(
26
+ "nav",
27
+ {
28
+ className: i(n.r, l && n.v, o && n.u, p),
29
+ role: o ? "tablist" : void 0,
30
+ onKeyDown: c ? (a) => y(a, {
31
+ values: f,
32
+ value: e,
33
+ onSelect: c,
34
+ vertical: l
35
+ }) : void 0,
36
+ ...m
37
+ },
38
+ s == null ? void 0 : s.map((a) => {
39
+ const r = a.active ?? (e != null && a.value === e), t = x(a.badge);
40
+ return /* @__PURE__ */ d.createElement(
41
+ "button",
42
+ {
43
+ key: a.value,
44
+ type: "button",
45
+ role: o ? "tab" : void 0,
46
+ className: i(n.i, r && n.a),
47
+ "data-active": r ? "true" : void 0,
48
+ "aria-current": o ? void 0 : r ? "page" : void 0,
49
+ "aria-selected": o ? r : void 0,
50
+ tabIndex: o ? r ? 0 : -1 : void 0,
51
+ disabled: a.disabled,
52
+ onClick: () => {
53
+ var v;
54
+ (v = a.onClick) == null || v.call(a), c == null || c(a.value);
55
+ }
56
+ },
57
+ a.label,
58
+ t != null ? /* @__PURE__ */ d.createElement(g, { className: n.d, tone: "accent", size: "s" }, t) : null
59
+ );
60
+ }),
61
+ b
62
+ );
63
+ }
64
+ export {
65
+ D as M
66
+ };
package/dist/Menu.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.cmr{display:flex;align-items:center;gap:var(--shell-gap-sm);margin:0;padding:0;list-style:none}.cmv{flex-direction:column;align-items:stretch}.cmi{-webkit-appearance:none;-moz-appearance:none;appearance:none;display:inline-flex;align-items:center;justify-content:center;border:0;background:transparent;color:var(--shell-color-muted);padding:var(--shell-padding);border-radius:var(--shell-radius);font:inherit;line-height:1;cursor:pointer;text-align:left}.cmi>svg{display:block;flex-shrink:0}.cmi:hover{background:var(--shell-background-hover);color:var(--shell-color)}.cma,.cmi[aria-current=page],.cmi[data-active=true]{background:var(--shell-background-selected);color:var(--shell-color)}}
1
+ @layer elcrm-components{.cmr{display:flex;align-items:center;gap:var(--shell-gap-sm);margin:0;padding:0;list-style:none}.cmv{flex-direction:column;align-items:stretch}.cmi{-webkit-appearance:none;-moz-appearance:none;appearance:none;display:inline-flex;align-items:center;justify-content:center;border:0;background:transparent;color:var(--shell-color-muted);padding:var(--shell-padding);border-radius:var(--shell-radius);font:inherit;line-height:1;cursor:pointer;text-align:left;gap:var(--shell-gap-sm)}.cmd{flex-shrink:0;min-width:16px;height:16px;padding:0 4px;font-size:10px}.cmi>svg{display:block;flex-shrink:0}.cmi:hover{background:var(--shell-background-hover);color:var(--shell-color)}.cma,.cmi[aria-current=page],.cmi[data-active=true]{background:var(--shell-background-selected);color:var(--shell-color)}.cmu{width:100%;align-items:stretch;gap:1.25rem;padding:0;border-bottom:1px solid var(--shell-border)}.cmu .cmi{border-radius:0;background:transparent;padding:.45rem .1rem .55rem;border-bottom:2px solid transparent;margin-bottom:-1px}.cmu .cmi:hover{background:transparent;color:var(--shell-color)}.cmu .cma,.cmu .cmi[aria-current=page],.cmu .cmi[data-active=true]{background:transparent;color:var(--shell-color);border-bottom-color:var(--shell-color-accent)}}
package/dist/Menu.d.ts CHANGED
@@ -19,6 +19,11 @@ export type MenuItem = {
19
19
  onClick?: () => void;
20
20
  /** Недоступный пункт. */
21
21
  disabled?: boolean;
22
+ /**
23
+ * Счётчик справа (`Badge`).
24
+ * `0`, `null`, `""` — не рендерим.
25
+ */
26
+ badge?: number | string | null;
22
27
  };
23
28
  /**
24
29
  * Props компонента {@link Menu}.
@@ -34,6 +39,11 @@ export type MenuProps = Omit<React.HTMLAttributes<HTMLElement>, "onSelect"> & {
34
39
  items?: MenuItem[];
35
40
  /** Вертикальный ряд пунктов. */
36
41
  vertical?: boolean;
42
+ /**
43
+ * `plain` — пилюли.
44
+ * `line` — вкладки с нижней чертой (фильтры страницы).
45
+ */
46
+ variant?: "plain" | "line";
37
47
  /**
38
48
  * Активный `value` пункта (если у item не задан `active`).
39
49
  * Идентификатор — `value`, не `key`.
@@ -53,18 +63,20 @@ export type MenuProps = Omit<React.HTMLAttributes<HTMLElement>, "onSelect"> & {
53
63
  *
54
64
  * Связанные компоненты:
55
65
  * - {@link Dropdown} — выпадающая панель рядом с триггером;
56
- * - `@elcrm/overlay`портальный popover/меню поверх страницы;
66
+ * - {@link Popover} / {@link Dropdown} панель в portal;
57
67
  * - {@link NavSections} / {@link TabSections} — разделы с lazy-контентом.
58
68
  *
59
69
  * API: декларативные `items` (идентификатор — `value`). Compound-слоты не используются.
60
70
  *
71
+ * @param variant `plain` (пилюли) или `line` (черта снизу)
72
+ *
61
73
  * CSS-токены: `--shell-*` (цвета/отступы shell-навигации).
62
74
  *
63
75
  * @example
64
76
  * ```tsx
65
77
  * const items = [
66
78
  * { value: "home", label: "Главная" },
67
- * { value: "team", label: "Команда", disabled: true },
79
+ * { value: "team", label: "Команда", badge: 3 },
68
80
  * ];
69
81
  *
70
82
  * <Menu
@@ -75,4 +87,4 @@ export type MenuProps = Omit<React.HTMLAttributes<HTMLElement>, "onSelect"> & {
75
87
  * />
76
88
  * ```
77
89
  */
78
- export declare function Menu({ items, vertical, value, onSelect, children, className, ...rest }: MenuProps): React.JSX.Element;
90
+ export declare function Menu({ items, vertical, variant, value, onSelect, children, className, ...rest }: MenuProps): React.JSX.Element;
package/dist/Menu.js CHANGED
@@ -1,4 +1,4 @@
1
- import { M as r } from "./Menu-DRBHvSdJ.js";
1
+ import { M as r } from "./Menu-DuXQv-zT.js";
2
2
  export {
3
3
  r as Menu
4
4
  };
@@ -0,0 +1,79 @@
1
+ import a, { useMemo as h, Suspense as S } from "react";
2
+ import { c as d } from "./cx-CcykAxZN.js";
3
+ import { I as g } from "./Item-D4okhsfW.js";
4
+ import { h as x } from "./rovingKeys-CGlfTtNQ.js";
5
+ import { u as C } from "./useControllableValue-CWqH1kpM.js";
6
+ import './NavSections.css';const I = "cvr", K = "cvn", M = "cvg", R = "cvl", V = "cvi", w = "cva", D = "cvc", P = "cvt", j = "cvf", q = "cvm", l = {
7
+ r: I,
8
+ n: K,
9
+ g: M,
10
+ l: R,
11
+ i: V,
12
+ a: w,
13
+ c: D,
14
+ t: P,
15
+ f: j,
16
+ m: q
17
+ };
18
+ function z(n) {
19
+ for (const m of n) {
20
+ const c = m.items.find((s) => !s.disabled);
21
+ if (c) return c.value;
22
+ }
23
+ return "";
24
+ }
25
+ function J({
26
+ groups: n,
27
+ resolveSection: m,
28
+ value: c,
29
+ defaultValue: s,
30
+ onValueChange: f,
31
+ loading: b = null,
32
+ footer: i,
33
+ className: E,
34
+ navLabel: N = "Разделы"
35
+ }) {
36
+ const p = h(
37
+ () => s || z(n),
38
+ [s, n]
39
+ ), [r, v] = C(
40
+ c,
41
+ p,
42
+ f
43
+ ), k = n.flatMap((e) => e.items).filter((e) => !e.disabled).map((e) => e.value), u = r ? m(r) : null;
44
+ return /* @__PURE__ */ a.createElement("div", { className: d(l.r, E) }, /* @__PURE__ */ a.createElement(
45
+ "nav",
46
+ {
47
+ className: l.n,
48
+ "aria-label": N,
49
+ onKeyDown: (e) => x(e, {
50
+ values: k,
51
+ value: r,
52
+ onSelect: v,
53
+ vertical: !0
54
+ })
55
+ },
56
+ n.map((e, y) => /* @__PURE__ */ a.createElement("div", { key: y, className: l.g }, e.label != null ? /* @__PURE__ */ a.createElement("div", { className: l.l }, e.label) : null, e.items.map((t) => {
57
+ const o = t.value === r;
58
+ return /* @__PURE__ */ a.createElement(
59
+ g,
60
+ {
61
+ key: t.value,
62
+ as: "button",
63
+ active: o,
64
+ className: d(l.i, o && l.a),
65
+ "data-active": o ? "true" : void 0,
66
+ "aria-current": o ? "page" : void 0,
67
+ disabled: t.disabled,
68
+ onClick: () => v(t.value)
69
+ },
70
+ t.icon != null ? /* @__PURE__ */ a.createElement("span", { className: l.c, "aria-hidden": !0 }, t.icon) : null,
71
+ /* @__PURE__ */ a.createElement("span", { className: l.t }, t.label)
72
+ );
73
+ }))),
74
+ i != null ? /* @__PURE__ */ a.createElement("div", { className: l.f }, i) : null
75
+ ), /* @__PURE__ */ a.createElement("div", { className: l.m }, u ? /* @__PURE__ */ a.createElement(S, { fallback: b }, a.createElement(u)) : null));
76
+ }
77
+ export {
78
+ J as N
79
+ };
@@ -1 +1 @@
1
- @layer elcrm-components{.cvr{display:flex;align-items:stretch;gap:var(--shell-gap-lg);width:100%;flex:1 1 auto;min-height:0;box-sizing:border-box;padding:var(--nav-sections-padding)}.cvn{display:flex;flex-direction:column;gap:var(--shell-gap);flex:0 0 var(--nav-sections-nav-width);width:var(--nav-sections-nav-width);min-width:var(--nav-sections-nav-width);box-sizing:border-box}.cvg{display:flex;flex-direction:column;gap:2px;padding-bottom:var(--shell-gap);border-bottom:1px solid var(--shell-border)}.cvg:last-of-type{border-bottom:none;padding-bottom:0}.cvl{font-size:11px;font-weight:var(--shell-font-weight);letter-spacing:.04em;text-transform:uppercase;color:var(--shell-color-muted);padding:0 var(--shell-padding-inline) var(--shell-gap-sm)}.cvi{-webkit-appearance:none;-moz-appearance:none;appearance:none;display:flex;align-items:center;gap:var(--shell-gap-sm);width:100%;text-align:left;border:none;border-radius:var(--shell-radius);background:transparent;color:var(--shell-color);font:inherit;font-size:var(--shell-font-size-sm);padding:var(--shell-padding);cursor:pointer;position:relative}.cvi:hover:not(:disabled){background:var(--shell-background-hover)}.cva{background:var(--shell-background-selected)}.cva:before{content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;border-radius:0 2px 2px 0;background:var(--shell-color-accent)}.cvi:disabled{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cvc{display:inline-flex;flex:0 0 auto;color:var(--shell-color-muted);line-height:0}.cva .cvc{color:var(--shell-color)}.cvt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cvf{margin-top:auto;padding-top:var(--shell-gap);border-top:1px solid var(--shell-border)}.cvm{flex:1 1 auto;min-width:0;max-width:var(--nav-sections-main-max-width);width:100%;min-height:0;overflow:auto}@media (max-width: 720px){.cvr{flex-direction:column}.cvn{flex:none;width:100%;min-width:0;max-width:none}}}
1
+ @layer elcrm-components{.cvr{display:flex;align-items:stretch;gap:var(--shell-gap-lg);width:100%;flex:1 1 auto;min-height:0;box-sizing:border-box;padding:var(--nav-sections-padding)}.cvn{display:flex;flex-direction:column;gap:var(--shell-gap);flex:0 0 var(--nav-sections-nav-width);width:var(--nav-sections-nav-width);min-width:var(--nav-sections-nav-width);box-sizing:border-box}.cvg{display:flex;flex-direction:column;gap:2px;padding-bottom:var(--shell-gap);border-bottom:1px solid var(--shell-border)}.cvg:last-of-type{border-bottom:none;padding-bottom:0}.cvl{font-size:var(--sidebar-group-font-size);font-weight:var(--shell-font-weight);letter-spacing:.04em;text-transform:uppercase;color:var(--shell-color-muted);padding:0 var(--shell-padding-inline) var(--shell-gap-sm)}.cvi:is(button){position:relative;font-size:var(--shell-font-size-sm)}.cva:before{content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;border-radius:0 2px 2px 0;background:var(--shell-color-accent)}.cvi:disabled{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cvc{display:inline-flex;flex:0 0 auto;color:var(--shell-color-muted);line-height:0}.cva .cvc{color:var(--shell-color)}.cvt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cvf{margin-top:auto;padding-top:var(--shell-gap);border-top:1px solid var(--shell-border)}.cvm{flex:1 1 auto;min-width:0;max-width:var(--nav-sections-main-max-width);width:100%;min-height:0;overflow:auto}@media (max-width: 720px){.cvr{flex-direction:column}.cvn{flex:none;width:100%;min-width:0;max-width:none}}}
@@ -1,4 +1,4 @@
1
- import { N as e } from "./NavSections-NDcIDD6H.js";
1
+ import { N as e } from "./NavSections-BpyZPimZ.js";
2
2
  export {
3
3
  e as NavSections
4
4
  };
@@ -0,0 +1,31 @@
1
+ import c from "react";
2
+ import { c as o } from "./cx-CcykAxZN.js";
3
+ import './PageHead.css';const u = "ccr", f = "cck", i = "ccw", E = "cct", N = "cch", d = "ccn", h = "ccu", v = "ccs", w = "cca", k = "ccc", e = {
4
+ r: u,
5
+ k: f,
6
+ w: i,
7
+ t: E,
8
+ h: N,
9
+ n: d,
10
+ u: h,
11
+ s: v,
12
+ a: w,
13
+ c: k
14
+ };
15
+ function p(t) {
16
+ return !(t == null || t === !1 || t === "" || typeof t == "number" && t === 0 || t === "0");
17
+ }
18
+ function g({
19
+ title: t,
20
+ sup: n,
21
+ subtitle: a,
22
+ actions: l,
23
+ sticky: r,
24
+ className: m,
25
+ children: s
26
+ }) {
27
+ return /* @__PURE__ */ c.createElement("div", { className: o(e.r, r && e.k, m) }, /* @__PURE__ */ c.createElement("div", { className: e.w }, /* @__PURE__ */ c.createElement("div", { className: e.t }, /* @__PURE__ */ c.createElement("h1", { className: e.h }, /* @__PURE__ */ c.createElement("span", { className: e.n }, t), p(n) ? /* @__PURE__ */ c.createElement("span", { className: e.u }, n) : null), a != null ? /* @__PURE__ */ c.createElement("p", { className: e.s }, a) : null), l != null ? /* @__PURE__ */ c.createElement("div", { className: e.a }, l) : null), s != null ? /* @__PURE__ */ c.createElement("div", { className: e.c }, s) : null);
28
+ }
29
+ export {
30
+ g as P
31
+ };
package/dist/PageHead.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.ccr{margin:0 0 var(--shell-gap-lg)}.ccw{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:space-between;gap:var(--shell-gap)}.cct{min-width:0;max-width:36rem}.cch{margin:0;font-size:var(--section-title-size);font-weight:var(--shell-font-weight);color:var(--shell-color);line-height:1.25}.ccs{margin:var(--shell-gap-sm) 0 0;font-size:var(--shell-font-size);color:var(--shell-color-muted);line-height:1.45}.cca{display:flex;flex-wrap:wrap;align-items:center;gap:var(--shell-gap-sm)}}
1
+ @layer elcrm-components{.ccr{display:flex;flex-direction:column;gap:var(--shell-gap);margin:0 0 var(--shell-gap)}.cck{position:sticky;top:0;z-index:var(--shell-z);background:var(--shell-bg)}.ccw{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--shell-gap-sm)}.cct{min-width:0;max-width:36rem}.cch{margin:0;display:flex;align-items:flex-start;gap:.4em;font-size:var(--section-title-size);font-weight:var(--shell-font-weight);color:var(--shell-color);line-height:1.15}.ccn{min-width:0}.ccu{display:inline-flex;align-items:center;margin-top:.12em;font-size:.7rem;font-weight:600;line-height:1;color:var(--shell-color-muted)}.ccs{margin:var(--shell-gap-sm) 0 0;font-size:var(--shell-font-size);color:var(--shell-color-muted);line-height:1.45}.cca{display:flex;flex-wrap:wrap;align-items:center;gap:var(--shell-gap-sm)}.ccc{display:flex;flex-direction:column;gap:var(--shell-gap);min-width:0}}
@@ -8,8 +8,10 @@ import { default as React } from 'react';
8
8
  *
9
9
  * @example
10
10
  * ```tsx
11
- * <PageShell>
12
- * <PageHead title="Клиенты" subtitle="Все компании" actions={<Button>Создать</Button>} />
11
+ * <PageShell scroll>
12
+ * <PageHead title="Идеи" sup={<Badge size="s" tone="accent">4</Badge>} sticky>
13
+ * <Menu items={tabs} value={tab} onSelect={setTab} />
14
+ * </PageHead>
13
15
  * …
14
16
  * </PageShell>
15
17
  * ```
@@ -19,18 +21,38 @@ import { default as React } from 'react';
19
21
  export type PageHeadProps = {
20
22
  /** Заголовок страницы */
21
23
  title: React.ReactNode;
24
+ /**
25
+ * Надстрочный слот сразу после title: текст или {@link Badge}.
26
+ * Пусто / `0` / `false` — не рендерим.
27
+ */
28
+ sup?: React.ReactNode;
22
29
  /** Подзаголовок */
23
30
  subtitle?: React.ReactNode;
24
31
  /** Кнопки / поиск справа в одной строке с заголовком */
25
32
  actions?: React.ReactNode;
33
+ /**
34
+ * Прилипает к верху скролла предка (`PageShell` с `scroll`).
35
+ * По умолчанию `false`.
36
+ */
37
+ sticky?: boolean;
26
38
  /** Доп. CSS-класс */
27
39
  className?: string;
28
- /** Контент под строкой заголовка (фильтры, Breadcrumb) */
40
+ /**
41
+ * Контент под строкой заголовка (фильтры, поиск, вкладки).
42
+ * Между title и children — `--shell-gap`.
43
+ */
29
44
  children?: React.ReactNode;
30
45
  };
31
46
  /**
32
- * Заголовок: title слева, actions справа (одна линия).
47
+ * Заголовок: title слева (+ `sup` сверху), actions справа.
33
48
  *
34
- * @see PageShell, Section, Header
49
+ * @param props.title заголовок
50
+ * @param props.sup — текст или Badge после title (надстрочно)
51
+ * @param props.subtitle — строка под заголовком
52
+ * @param props.actions — кнопки справа
53
+ * @param props.sticky — прилипает к скроллу
54
+ * @param props.children — фильтры / вкладки под шапкой
55
+ *
56
+ * @see PageShell, Section, Header, Badge
35
57
  */
36
- export declare function PageHead({ title, subtitle, actions, className, children, }: PageHeadProps): React.JSX.Element;
58
+ export declare function PageHead({ title, sup, subtitle, actions, sticky, className, children, }: PageHeadProps): React.JSX.Element;
package/dist/PageHead.js CHANGED
@@ -1,4 +1,4 @@
1
- import { P as o } from "./PageHead-Baz9BLuA.js";
1
+ import { P as o } from "./PageHead-DgaJ1mXY.js";
2
2
  export {
3
3
  o as PageHead
4
4
  };
@@ -1,28 +1,30 @@
1
1
  import e from "react";
2
2
  import { c as l } from "./cx-CcykAxZN.js";
3
- import './PageShell.css';const m = "ckr", o = "ckw", f = "ckb", i = "ckf", c = {
3
+ import './PageShell.css';const m = "ckr", f = "ckw", i = "ckb", k = "ckf", u = "cko", c = {
4
4
  r: m,
5
- w: o,
6
- b: f,
7
- f: i
5
+ w: f,
6
+ b: i,
7
+ f: k,
8
+ o: u
8
9
  };
9
- function u({
10
+ function d({
10
11
  children: s,
11
- className: a,
12
- wide: n,
12
+ className: n,
13
+ wide: o,
14
+ scroll: r = !0,
13
15
  footer: t,
14
- ...r
16
+ ...a
15
17
  }) {
16
18
  return /* @__PURE__ */ e.createElement(
17
19
  "div",
18
20
  {
19
- ...r,
20
- className: l(c.r, n && c.w, a)
21
+ ...a,
22
+ className: l(c.r, o && c.w, !r && c.o, n)
21
23
  },
22
24
  /* @__PURE__ */ e.createElement("div", { className: c.b }, s),
23
25
  t != null ? /* @__PURE__ */ e.createElement("div", { className: c.f }, t) : null
24
26
  );
25
27
  }
26
28
  export {
27
- u as P
29
+ d as P
28
30
  };
@@ -1 +1 @@
1
- @layer elcrm-components{.ckr{flex:1 1 auto;min-height:0;height:100%;width:100%;display:flex;flex-direction:column;box-sizing:border-box;padding:var(--shell-padding);overflow:hidden}.ckw{max-width:none}.ckb{flex:1 1 auto;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:var(--shell-gap)}.ckf{flex:0 0 auto;padding-top:var(--shell-gap-sm)}}
1
+ @layer elcrm-components{.ckr{flex:1 1 auto;min-height:0;height:100%;width:100%;display:flex;flex-direction:column;box-sizing:border-box;padding:var(--shell-padding);overflow:hidden}.ckw{max-width:none}.ckb{flex:1 1 auto;min-height:0;overflow:auto;display:flex;flex-direction:column;gap:var(--shell-gap)}.ckf{flex:0 0 auto;padding-top:var(--shell-gap-sm)}.cko{overflow:visible;height:auto}.cko .ckb{overflow:visible;flex:0 1 auto;min-height:auto}}
@@ -1,14 +1,14 @@
1
1
  import { default as React } from 'react';
2
2
  /**
3
- * Каркас модуля: скролл в body, опциональный footer прибит к низу.
3
+ * Каркас модуля: опциональный скролл в body, footer прибит к низу.
4
4
  *
5
5
  * Роли: PageShell = внутренний каркас контента в Layout;
6
6
  * сверху часто PageHead / Section, снизу — панель действий.
7
7
  *
8
8
  * @example
9
9
  * ```tsx
10
- * <PageShell wide footer={<Footer><Button>Сохранить</Button></Footer>}>
11
- * <PageHead title="Редактор" />
10
+ * <PageShell wide scroll footer={<Footer><Button>Сохранить</Button></Footer>}>
11
+ * <PageHead title="Редактор" sticky />
12
12
  * …
13
13
  * </PageShell>
14
14
  * ```
@@ -16,18 +16,23 @@ import { default as React } from 'react';
16
16
  * Токены: `--shell-*`.
17
17
  */
18
18
  export type PageShellProps = {
19
- /** Контент модуля (скроллится) */
19
+ /** Контент модуля */
20
20
  children: React.ReactNode;
21
21
  /** Доп. CSS-класс */
22
22
  className?: string;
23
23
  /** Шире контент (редакторы, таблицы) */
24
24
  wide?: boolean;
25
+ /**
26
+ * Скролл внутри body. По умолчанию `true`.
27
+ * `false` — шелл растёт с контентом, крутит предок.
28
+ */
29
+ scroll?: boolean;
25
30
  /** Нижняя панель вне скролла */
26
31
  footer?: React.ReactNode;
27
32
  } & Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "className">;
28
33
  /**
29
- * Каркас модуля: скролл в body, footer прибит к низу.
34
+ * Каркас модуля: скролл в body (если `scroll`), footer прибит к низу.
30
35
  *
31
36
  * @see PageHead, Layout, Section, Footer
32
37
  */
33
- export declare function PageShell({ children, className, wide, footer, ...rootProps }: PageShellProps): React.JSX.Element;
38
+ export declare function PageShell({ children, className, wide, scroll, footer, ...rootProps }: PageShellProps): React.JSX.Element;
package/dist/PageShell.js CHANGED
@@ -1,4 +1,4 @@
1
- import { P as l } from "./PageShell-CghThHS3.js";
1
+ import { P as l } from "./PageShell-D9EZiO8o.js";
2
2
  export {
3
3
  l as PageShell
4
4
  };