@elcrm/components 0.0.3 → 0.1.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 (108) hide show
  1. package/README.md +450 -202
  2. package/dist/Avatar-Jzan87XD.js +101 -0
  3. package/dist/Avatar.css +1 -1
  4. package/dist/Avatar.d.ts +56 -5
  5. package/dist/Avatar.js +1 -1
  6. package/dist/AvatarGroup-DUe7-T3B.js +81 -0
  7. package/dist/AvatarGroup.css +1 -0
  8. package/dist/AvatarGroup.d.ts +77 -0
  9. package/dist/AvatarGroup.js +4 -0
  10. package/dist/AvatarName-KBbQrVta.js +56 -0
  11. package/dist/AvatarName.css +1 -0
  12. package/dist/AvatarName.d.ts +76 -0
  13. package/dist/AvatarName.js +4 -0
  14. package/dist/Badge.css +1 -1
  15. package/dist/Badge.d.ts +16 -1
  16. package/dist/Block-BAXh0PPp.js +8 -0
  17. package/dist/Block.d.ts +27 -6
  18. package/dist/Block.js +1 -1
  19. package/dist/Brand.css +1 -1
  20. package/dist/Brand.d.ts +20 -1
  21. package/dist/Breadcrumb-D0wXkE38.js +98 -0
  22. package/dist/Breadcrumb.css +1 -0
  23. package/dist/Breadcrumb.d.ts +80 -0
  24. package/dist/Breadcrumb.js +4 -0
  25. package/dist/Card.css +1 -1
  26. package/dist/Card.d.ts +22 -1
  27. package/dist/Column-DDaqX-A7.js +8 -0
  28. package/dist/Column.d.ts +23 -7
  29. package/dist/Column.js +1 -1
  30. package/dist/Dropdown-Cw5LKpcT.js +168 -0
  31. package/dist/Dropdown.css +1 -0
  32. package/dist/Dropdown.d.ts +124 -0
  33. package/dist/Dropdown.js +4 -0
  34. package/dist/EmptyState.css +1 -1
  35. package/dist/EmptyState.d.ts +24 -1
  36. package/dist/Footer.css +1 -1
  37. package/dist/Footer.d.ts +20 -1
  38. package/dist/Header.css +1 -1
  39. package/dist/Header.d.ts +25 -2
  40. package/dist/Item.css +1 -1
  41. package/dist/Item.d.ts +24 -1
  42. package/dist/Layout.css +1 -1
  43. package/dist/Layout.d.ts +23 -3
  44. package/dist/List.css +1 -1
  45. package/dist/List.d.ts +22 -1
  46. package/dist/Loading.css +1 -1
  47. package/dist/Loading.d.ts +19 -1
  48. package/dist/Menu-DRBHvSdJ.js +40 -0
  49. package/dist/Menu.css +1 -1
  50. package/dist/Menu.d.ts +64 -8
  51. package/dist/Menu.js +1 -1
  52. package/dist/{NavSections-CM-VPai2.js → NavSections-NDcIDD6H.js} +26 -26
  53. package/dist/NavSections.css +1 -1
  54. package/dist/NavSections.d.ts +79 -15
  55. package/dist/NavSections.js +1 -1
  56. package/dist/PageHead-Baz9BLuA.js +22 -0
  57. package/dist/PageHead.css +1 -0
  58. package/dist/PageHead.d.ts +36 -0
  59. package/dist/PageHead.js +4 -0
  60. package/dist/PageShell-CghThHS3.js +28 -0
  61. package/dist/PageShell.css +1 -0
  62. package/dist/PageShell.d.ts +33 -0
  63. package/dist/PageShell.js +4 -0
  64. package/dist/PricingGroup-CiqQtzjd.js +126 -0
  65. package/dist/PricingGroup.css +1 -0
  66. package/dist/PricingGroup.d.ts +130 -0
  67. package/dist/PricingGroup.js +4 -0
  68. package/dist/PricingTable-DYTJG4AU.js +158 -0
  69. package/dist/PricingTable.css +1 -0
  70. package/dist/PricingTable.d.ts +108 -0
  71. package/dist/PricingTable.js +4 -0
  72. package/dist/RadioGroup-DxoPHPIF.js +138 -0
  73. package/dist/RadioGroup.css +1 -0
  74. package/dist/RadioGroup.d.ts +111 -0
  75. package/dist/RadioGroup.js +4 -0
  76. package/dist/Row--HIe6vb9.js +8 -0
  77. package/dist/Row.d.ts +23 -7
  78. package/dist/Row.js +1 -1
  79. package/dist/Section.css +1 -1
  80. package/dist/Section.d.ts +23 -3
  81. package/dist/Stack-b0-XWwQ1.js +31 -0
  82. package/dist/Stack.css +1 -0
  83. package/dist/Stack.d.ts +34 -0
  84. package/dist/Stack.js +4 -0
  85. package/dist/TabSections-DL7DufKU.js +50 -0
  86. package/dist/TabSections.css +1 -1
  87. package/dist/TabSections.d.ts +69 -13
  88. package/dist/TabSections.js +1 -1
  89. package/dist/cx.d.ts +8 -1
  90. package/dist/dark.css +24 -0
  91. package/dist/index.css +32 -18
  92. package/dist/index.d.ts +21 -1
  93. package/dist/index.es.js +58 -38
  94. package/dist/lazyRoute.d.ts +19 -3
  95. package/dist/light.css +25 -0
  96. package/dist/stackGap.d.ts +13 -1
  97. package/dist/tokens.css +57 -0
  98. package/package.json +20 -4
  99. package/dist/Avatar-DHNPaIjZ.js +0 -64
  100. package/dist/Block-CVKukh1e.js +0 -28
  101. package/dist/Block.css +0 -1
  102. package/dist/Column-DlNrvFq1.js +0 -26
  103. package/dist/Column.css +0 -1
  104. package/dist/Menu-DS7zKQbY.js +0 -40
  105. package/dist/Row-CnD7Q7pD.js +0 -20
  106. package/dist/Row.css +0 -1
  107. package/dist/TabSections-oeEBx0nM.js +0 -50
  108. package/dist/stackGap-CDsjER0g.js +0 -7
@@ -0,0 +1,98 @@
1
+ import e from "react";
2
+ import { c as x } from "./cx-CcykAxZN.js";
3
+ import './Breadcrumb.css';const y = "c0r", w = "c0i", B = "c0c", L = "c0n", C = "c0l", F = "c0d", M = "c0s", $ = "c0sv", S = "c0e", l = {
4
+ r: y,
5
+ i: w,
6
+ c: B,
7
+ n: L,
8
+ l: C,
9
+ d: F,
10
+ s: M,
11
+ sv: $,
12
+ e: S
13
+ };
14
+ function Z() {
15
+ return /* @__PURE__ */ e.createElement("svg", { className: l.sv, viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
16
+ "path",
17
+ {
18
+ d: "M4.5 2.5 8 6l-3.5 3.5",
19
+ fill: "none",
20
+ stroke: "currentColor",
21
+ strokeWidth: "1.5",
22
+ strokeLinecap: "round",
23
+ strokeLinejoin: "round"
24
+ }
25
+ ));
26
+ }
27
+ function j() {
28
+ return /* @__PURE__ */ e.createElement("svg", { className: l.d, viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
29
+ "path",
30
+ {
31
+ d: "M6 1.5 8.5 4.5h-5L6 1.5ZM6 10.5 3.5 7.5h5L6 10.5Z",
32
+ fill: "currentColor"
33
+ }
34
+ ));
35
+ }
36
+ function D(t) {
37
+ return t === "slash" ? "/" : t === "chevron" ? /* @__PURE__ */ e.createElement(Z, null) : t;
38
+ }
39
+ function H(t, r) {
40
+ if (r == null || r < 1 || t.length <= r + 1)
41
+ return t.map((a, c) => ({ kind: "item", item: a, index: c }));
42
+ const h = t.slice(0, r).map((a, c) => ({
43
+ kind: "item",
44
+ item: a,
45
+ index: c
46
+ })), o = t[t.length - 1];
47
+ return [
48
+ ...h,
49
+ { kind: "ellipsis" },
50
+ { kind: "item", item: o, index: t.length - 1 }
51
+ ];
52
+ }
53
+ function q({
54
+ items: t,
55
+ separator: r = "chevron",
56
+ collapseAfter: h,
57
+ onEllipsisClick: o,
58
+ onItemClick: a,
59
+ className: c,
60
+ ...k
61
+ }) {
62
+ const N = H(t, h), g = t.length - 1;
63
+ return /* @__PURE__ */ e.createElement("nav", { "aria-label": "Breadcrumb", className: c, ...k }, /* @__PURE__ */ e.createElement("ol", { className: l.r }, N.map((E, p) => {
64
+ const s = [];
65
+ if (p > 0 && s.push(
66
+ /* @__PURE__ */ e.createElement("li", { key: `sep-${p}`, className: l.s, "aria-hidden": !0 }, D(r))
67
+ ), E.kind === "ellipsis")
68
+ return s.push(
69
+ /* @__PURE__ */ e.createElement("li", { key: "ellipsis" }, o ? /* @__PURE__ */ e.createElement(
70
+ "button",
71
+ {
72
+ type: "button",
73
+ className: l.e,
74
+ "aria-label": "Показать путь",
75
+ onClick: o
76
+ },
77
+ "…"
78
+ ) : /* @__PURE__ */ e.createElement("span", { className: l.e }, "…"))
79
+ ), /* @__PURE__ */ e.createElement(e.Fragment, { key: `frag-${p}` }, s);
80
+ const { item: n, index: f } = E, u = f === g, d = /* @__PURE__ */ e.createElement(e.Fragment, null, n.icon != null ? /* @__PURE__ */ e.createElement("span", { className: l.n }, n.icon) : null, n.label != null ? /* @__PURE__ */ e.createElement("span", { className: l.l }, n.label) : null, n.dropdown ? /* @__PURE__ */ e.createElement(j, null) : null), m = x(l.i, u && l.c), b = () => {
81
+ var v;
82
+ n.disabled || u || ((v = n.onClick) == null || v.call(n), a == null || a(n, f));
83
+ };
84
+ let i;
85
+ return u || n.disabled ? i = /* @__PURE__ */ e.createElement(
86
+ "span",
87
+ {
88
+ className: m,
89
+ "aria-current": u ? "page" : void 0,
90
+ "aria-disabled": n.disabled || void 0
91
+ },
92
+ d
93
+ ) : n.href != null ? i = /* @__PURE__ */ e.createElement("a", { className: m, href: n.href, onClick: b }, d) : n.onClick != null || a != null || n.dropdown ? i = /* @__PURE__ */ e.createElement("button", { type: "button", className: m, onClick: b }, d) : i = /* @__PURE__ */ e.createElement("span", { className: m }, d), s.push(/* @__PURE__ */ e.createElement("li", { key: n.value }, i)), /* @__PURE__ */ e.createElement(e.Fragment, { key: n.value }, s);
94
+ })));
95
+ }
96
+ export {
97
+ q as B
98
+ };
@@ -0,0 +1 @@
1
+ @layer elcrm-components{.c0r{display:flex;flex-wrap:wrap;align-items:center;gap:var(--shell-gap-sm);list-style:none;margin:0;padding:0;font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight);color:var(--shell-color-muted);line-height:1.3}.c0i{display:inline-flex;align-items:center;gap:var(--shell-gap-sm);min-width:0;color:inherit}.c0i:is(a,button){-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding:0;border:none;background:transparent;font:inherit;font-weight:inherit;color:inherit;cursor:pointer;text-decoration:none}.c0i:is(a,button):hover{color:var(--shell-color)}.c0c,.c0i[aria-current=page]{color:var(--shell-color-accent);cursor:default}.c0n{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--shell-icon-size);height:var(--shell-icon-size);overflow:hidden;border-radius:calc(var(--shell-radius) * .5);color:inherit}.c0n img{width:100%;height:100%;object-fit:cover;display:block}.c0l{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.c0d{display:inline-flex;flex-shrink:0;width:.75rem;height:.75rem;color:var(--shell-color-muted)}.c0s{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;color:var(--shell-color-muted);-webkit-user-select:none;user-select:none}.c0sv{width:.75rem;height:.75rem;display:block}.c0e{display:inline-flex;align-items:center;justify-content:center;min-width:1em;color:var(--shell-color-muted);letter-spacing:.05em}.c0e:is(button){-webkit-appearance:none;-moz-appearance:none;appearance:none;margin:0;padding:.15rem .35rem;border:none;border-radius:calc(var(--shell-radius) * .4);background:transparent;font:inherit;color:inherit;cursor:pointer}.c0e:is(button):hover{background:var(--shell-background-hover);color:var(--shell-color)}}
@@ -0,0 +1,80 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Вид разделителя между крошками: встроенные `chevron` / `slash` или свой узел.
4
+ */
5
+ export type BreadcrumbSeparator = "chevron" | "slash" | React.ReactNode;
6
+ /**
7
+ * Пункт хлебных крошек {@link Breadcrumb}.
8
+ *
9
+ * Идентификатор — `value` (не `key`).
10
+ * Опциональные поля: нет `icon` / `label` — соответствующий блок не рисуется.
11
+ */
12
+ export type BreadcrumbItem = {
13
+ /** Уникальный идентификатор пункта (не `key`). */
14
+ value: string;
15
+ /** Подпись крошки. */
16
+ label?: React.ReactNode;
17
+ /** Иконка / Avatar слева. */
18
+ icon?: React.ReactNode;
19
+ /** Ссылка; если задана — рендерится `<a>`. */
20
+ href?: string;
21
+ /** Индикатор dropdown (↕) справа от label. */
22
+ dropdown?: boolean;
23
+ /** Недоступный / некликабельный пункт. */
24
+ disabled?: boolean;
25
+ /** Локальный обработчик клика. */
26
+ onClick?: () => void;
27
+ };
28
+ /**
29
+ * Props компонента {@link Breadcrumb}.
30
+ *
31
+ * Групповой UI: декларативный массив `items`.
32
+ */
33
+ export type BreadcrumbProps = Omit<React.HTMLAttributes<HTMLElement>, "onClick"> & {
34
+ /** Цепочка крошек (`value` — идентификатор). */
35
+ items: BreadcrumbItem[];
36
+ /** Разделитель: chevron `>`, slash `/` или свой node. */
37
+ separator?: BreadcrumbSeparator;
38
+ /**
39
+ * Свернуть середину: первые N пунктов, «…», последний.
40
+ * Например `2` → Home, Settings, …, Team.
41
+ */
42
+ collapseAfter?: number;
43
+ /** Клик по «…» (если нужно раскрыть путь). */
44
+ onEllipsisClick?: () => void;
45
+ /** Клик по пункту (кроме текущего и disabled). */
46
+ onItemClick?: (item: BreadcrumbItem, index: number) => void;
47
+ };
48
+ /**
49
+ * Хлебные крошки пути из декларативного массива `items`.
50
+ *
51
+ * Когда использовать:
52
+ * - показать иерархию страницы / вложенных разделов;
53
+ * - нужна свёртка длинного пути (`collapseAfter`).
54
+ *
55
+ * Связанные компоненты:
56
+ * - {@link PageHead} — шапка страницы, куда часто кладут Breadcrumb;
57
+ * - {@link Dropdown} — если крошка с `dropdown` открывает меню выбора.
58
+ *
59
+ * API: декларативные `items`. Идентификатор — `value` (не `key`).
60
+ * Compound-слоты не используются.
61
+ *
62
+ * CSS-токены: `--shell-*`.
63
+ *
64
+ * @example
65
+ * ```tsx
66
+ * const items = [
67
+ * { value: "home", label: "Главная", href: "/" },
68
+ * { value: "settings", label: "Настройки", href: "/settings" },
69
+ * { value: "team", label: "Команда", icon: <Avatar name="Team" size="s" /> },
70
+ * ];
71
+ *
72
+ * <Breadcrumb
73
+ * items={items}
74
+ * separator="chevron"
75
+ * collapseAfter={2}
76
+ * onItemClick={(item) => navigate(item.href ?? "/")}
77
+ * />
78
+ * ```
79
+ */
80
+ export declare function Breadcrumb({ items, separator, collapseAfter, onEllipsisClick, onItemClick, className, ...rest }: BreadcrumbProps): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { B as e } from "./Breadcrumb-D0wXkE38.js";
2
+ export {
3
+ e as Breadcrumb
4
+ };
package/dist/Card.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.cdr{background:var(--card-background);border:1px solid var(--card-border);border-radius:var(--card-radius);padding:var(--card-padding);box-shadow:var(--card-shadow)}.cdt{margin:0 0 .5rem;font-size:1.125rem;font-weight:600}}
1
+ @layer elcrm-components{.cdr{background:var(--shell-background);border:1px solid var(--shell-border);border-radius:var(--shell-radius);padding:var(--shell-padding);box-shadow:var(--shell-shadow)}.cdt{margin:0 0 var(--shell-gap-sm);font-size:var(--shell-font-size-lg);font-weight:var(--shell-font-weight);color:var(--shell-color)}}
package/dist/Card.d.ts CHANGED
@@ -1,7 +1,28 @@
1
1
  import { default as React } from 'react';
2
+ /**
3
+ * Карточка-контейнер с опциональным заголовком.
4
+ *
5
+ * Роли: Card = визуальная рамка для группы контента;
6
+ * для секций модуля — Section; для списков — List / Item.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <Card title="Тариф">
11
+ * <p>Базовый план</p>
12
+ * </Card>
13
+ * ```
14
+ *
15
+ * Токены: `--shell-*`.
16
+ */
2
17
  export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
18
+ /** Заголовок карточки (если нет — только children) */
3
19
  title?: React.ReactNode;
20
+ /** Содержимое карточки */
4
21
  children?: React.ReactNode;
5
22
  };
6
- /** Карточка. Токены: --card-* */
23
+ /**
24
+ * Карточка-контейнер.
25
+ *
26
+ * @see Section, Stack, List
27
+ */
7
28
  export declare function Card({ title, children, className, ...rest }: CardProps): React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ import o from "react";
2
+ import { S as r } from "./Stack-b0-XWwQ1.js";
3
+ function n(t) {
4
+ return /* @__PURE__ */ o.createElement(r, { direction: "column", ...t });
5
+ }
6
+ export {
7
+ n as C
8
+ };
package/dist/Column.d.ts CHANGED
@@ -1,8 +1,24 @@
1
1
  import { default as React } from 'react';
2
- export type ColumnProps = React.HTMLAttributes<HTMLDivElement> & {
3
- /** Отступ между детьми: число (px) или CSS-строка */
4
- gap?: number | string;
5
- children?: React.ReactNode;
6
- };
7
- /** Вертикальная колонка. Токены: --column-* */
8
- export declare function Column({ gap, children, className, style, ...rest }: ColumnProps): React.JSX.Element;
2
+ import { StackProps } from './Stack';
3
+ /**
4
+ * @deprecated Используйте `Stack` (column по умолчанию).
5
+ * Миграция: `elcrm migrate components`.
6
+ *
7
+ * Вертикальный стек (алиас Stack).
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * // Было:
12
+ * <Column gap={12}>…</Column>
13
+ * // Стало:
14
+ * <Stack gap={12}>…</Stack>
15
+ * ```
16
+ */
17
+ export type ColumnProps = Omit<StackProps, "direction">;
18
+ /**
19
+ * @deprecated Используйте `Stack` (column по умолчанию).
20
+ * Миграция: `elcrm migrate components`.
21
+ *
22
+ * @see Stack, Block, Row
23
+ */
24
+ export declare function Column(props: ColumnProps): React.JSX.Element;
package/dist/Column.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as r } from "./Column-DlNrvFq1.js";
1
+ import { C as r } from "./Column-DDaqX-A7.js";
2
2
  export {
3
3
  r as Column
4
4
  };
@@ -0,0 +1,168 @@
1
+ import e from "react";
2
+ import { c as k } from "./cx-CcykAxZN.js";
3
+ import './Dropdown.css';const z = "crr", F = "crt", G = "crc", K = "crp", T = "crh", U = "crg", q = "crd", A = "cri", H = "crn", J = "crk", Q = "crl", V = "cru", X = "crm", Y = "crb", Z = "cro", _ = "crf", $ = "crx", O = "cry", P = "crkv", t = {
4
+ r: z,
5
+ t: F,
6
+ c: G,
7
+ p: K,
8
+ h: T,
9
+ g: U,
10
+ d: q,
11
+ i: A,
12
+ n: H,
13
+ k: J,
14
+ l: Q,
15
+ u: V,
16
+ m: X,
17
+ b: Y,
18
+ o: Z,
19
+ f: _,
20
+ x: $,
21
+ y: O,
22
+ kv: P
23
+ };
24
+ function S() {
25
+ return /* @__PURE__ */ e.createElement("svg", { className: t.c, viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
26
+ "path",
27
+ {
28
+ d: "M2.5 4.5 6 8l3.5-3.5",
29
+ fill: "none",
30
+ stroke: "currentColor",
31
+ strokeWidth: "1.5",
32
+ strokeLinecap: "round",
33
+ strokeLinejoin: "round"
34
+ }
35
+ ));
36
+ }
37
+ function ee() {
38
+ return /* @__PURE__ */ e.createElement("svg", { className: t.m, viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
39
+ "path",
40
+ {
41
+ d: "M4.5 2.5 8 6l-3.5 3.5",
42
+ fill: "none",
43
+ stroke: "currentColor",
44
+ strokeWidth: "1.5",
45
+ strokeLinecap: "round",
46
+ strokeLinejoin: "round"
47
+ }
48
+ ));
49
+ }
50
+ function N({ className: r }) {
51
+ return /* @__PURE__ */ e.createElement("svg", { className: k(t.kv, r), viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
52
+ "path",
53
+ {
54
+ d: "M2.5 6.2 4.8 8.5 9.5 3.5",
55
+ fill: "none",
56
+ stroke: "currentColor",
57
+ strokeWidth: "1.8",
58
+ strokeLinecap: "round",
59
+ strokeLinejoin: "round"
60
+ }
61
+ ));
62
+ }
63
+ function ne({ item: r }) {
64
+ return r.checkbox ? /* @__PURE__ */ e.createElement("span", { className: k(t.x, r.checked && t.y), "aria-hidden": !0 }, r.checked ? /* @__PURE__ */ e.createElement(N, null) : null) : r.checked ? /* @__PURE__ */ e.createElement("span", { className: t.k, "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(N, null)) : r.status != null ? /* @__PURE__ */ e.createElement(
65
+ "span",
66
+ {
67
+ className: k(t.b, r.status === "online" ? t.o : t.f),
68
+ "aria-hidden": !0
69
+ }
70
+ ) : r.icon != null ? /* @__PURE__ */ e.createElement("span", { className: t.n }, r.icon) : null;
71
+ }
72
+ function te(r, l, a) {
73
+ return l != null && l.length > 0 ? a != null && l[0] && l[0].title == null ? [{ ...l[0], title: a }, ...l.slice(1)] : l : r != null && r.length > 0 ? [{ title: a, items: r }] : a != null ? [{ title: a, items: [] }] : [];
74
+ }
75
+ function le({
76
+ items: r,
77
+ groups: l,
78
+ trigger: a,
79
+ label: m,
80
+ hideChevron: x = !1,
81
+ title: y,
82
+ align: L = "end",
83
+ open: E,
84
+ defaultOpen: w = !1,
85
+ onOpenChange: f,
86
+ closeOnSelect: C = !0,
87
+ disabled: v = !1,
88
+ onSelect: h,
89
+ className: g,
90
+ ...B
91
+ }) {
92
+ const p = e.useRef(null), [D, R] = e.useState(w), b = E !== void 0, o = b ? E : D, u = (c) => {
93
+ b || R(c), f == null || f(c);
94
+ }, j = te(r, l, y);
95
+ e.useEffect(() => {
96
+ if (!o) return;
97
+ const c = (n) => {
98
+ const d = p.current;
99
+ d && !d.contains(n.target) && u(!1);
100
+ }, s = (n) => {
101
+ n.key === "Escape" && u(!1);
102
+ };
103
+ return document.addEventListener("mousedown", c), document.addEventListener("keydown", s), () => {
104
+ document.removeEventListener("mousedown", c), document.removeEventListener("keydown", s);
105
+ };
106
+ }, [o]);
107
+ const M = (c) => {
108
+ var n;
109
+ if (c.disabled) return;
110
+ (n = c.onClick) == null || n.call(c), h == null || h(c.value, c), c.checkbox || c.submenu || !C || u(!1);
111
+ }, W = a != null ? /* @__PURE__ */ e.createElement(
112
+ "span",
113
+ {
114
+ onClick: () => {
115
+ v || u(!o);
116
+ }
117
+ },
118
+ a
119
+ ) : /* @__PURE__ */ e.createElement(
120
+ "button",
121
+ {
122
+ type: "button",
123
+ className: t.t,
124
+ "data-open": o ? "true" : void 0,
125
+ disabled: v,
126
+ "aria-haspopup": "menu",
127
+ "aria-expanded": o,
128
+ onClick: () => u(!o)
129
+ },
130
+ m,
131
+ x ? null : /* @__PURE__ */ e.createElement(S, null)
132
+ );
133
+ return /* @__PURE__ */ e.createElement("div", { ref: p, className: k(t.r, g), ...B }, W, o ? /* @__PURE__ */ e.createElement(
134
+ "div",
135
+ {
136
+ className: t.p,
137
+ "data-align": L,
138
+ role: "menu",
139
+ "aria-label": typeof m == "string" ? m : void 0
140
+ },
141
+ j.map((c, s) => /* @__PURE__ */ e.createElement(e.Fragment, { key: s }, s > 0 ? /* @__PURE__ */ e.createElement("hr", { className: t.d }) : null, /* @__PURE__ */ e.createElement("div", { className: t.g, role: "group" }, c.title != null ? /* @__PURE__ */ e.createElement("p", { className: t.h }, c.title) : null, c.items.map((n) => {
142
+ const d = c.items.some(
143
+ (i) => i.checked || i.checkbox || i.status != null || i.icon != null
144
+ ), I = /* @__PURE__ */ e.createElement(ne, { item: n });
145
+ return /* @__PURE__ */ e.createElement(
146
+ "button",
147
+ {
148
+ key: n.value,
149
+ type: "button",
150
+ role: "menuitem",
151
+ className: t.i,
152
+ "data-checked": (n.checked || n.checkbox) && n.checked ? "true" : void 0,
153
+ "data-danger": n.danger ? "true" : void 0,
154
+ disabled: n.disabled,
155
+ "aria-checked": n.checked != null || n.checkbox ? !!n.checked : void 0,
156
+ onClick: () => M(n)
157
+ },
158
+ d ? I ?? /* @__PURE__ */ e.createElement("span", { className: t.n, "aria-hidden": !0 }) : null,
159
+ n.label != null ? /* @__PURE__ */ e.createElement("span", { className: t.l }, n.label) : null,
160
+ n.shortcut != null ? /* @__PURE__ */ e.createElement("span", { className: t.u }, n.shortcut) : null,
161
+ n.submenu ? /* @__PURE__ */ e.createElement(ee, null) : null
162
+ );
163
+ }))))
164
+ ) : null);
165
+ }
166
+ export {
167
+ le as D
168
+ };
@@ -0,0 +1 @@
1
+ @layer elcrm-components{.crr{position:relative;display:inline-flex;vertical-align:middle}.crt{display:inline-flex;align-items:center;gap:var(--shell-gap-sm);margin:0;padding:var(--shell-padding);box-sizing:border-box;font:inherit;font-size:var(--shell-font-size);font-weight:var(--shell-font-weight);color:var(--shell-color);background:var(--shell-background);border:1px solid var(--shell-border);border-radius:var(--shell-radius);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;line-height:1.2}.crt:hover{background:var(--shell-background-hover)}.crt[data-open=true]{border-color:var(--shell-color-accent);color:var(--shell-color-accent)}.crc{display:inline-flex;width:var(--shell-icon-size);height:var(--shell-icon-size);flex-shrink:0;color:var(--shell-color-muted)}.crt[data-open=true] .crc{color:var(--shell-color-accent)}.crp{position:absolute;top:calc(100% + var(--shell-gap-sm));z-index:var(--shell-z);min-width:var(--dropdown-min-width);max-width:var(--dropdown-max-width);max-height:var(--dropdown-max-height);overflow:auto;padding:var(--shell-gap-sm);box-sizing:border-box;background:var(--shell-background);border:1px solid var(--shell-border);border-radius:var(--shell-radius);box-shadow:var(--popup-shadow)}.crp[data-align=start]{left:0}.crp[data-align=end]{right:0}.crh{margin:0;padding:var(--shell-padding);font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight);color:var(--shell-color-muted);line-height:1.3}.crg{display:flex;flex-direction:column;gap:2px}.crd{height:0;margin:var(--shell-gap-sm) 0;border:0;border-top:1px solid var(--shell-border)}.cri{display:flex;align-items:center;gap:var(--shell-gap-sm);width:100%;margin:0;padding:var(--shell-padding);box-sizing:border-box;text-align:left;font:inherit;font-size:var(--shell-font-size);font-weight:500;color:var(--shell-color);background:transparent;border:none;border-radius:calc(var(--shell-radius) * .7);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;line-height:1.35}.cri:hover:not(:disabled),.cri:focus-visible:not(:disabled){background:var(--shell-background-hover);outline:none}.cri[data-checked=true]{background:var(--shell-background-selected)}.cri[data-danger=true]{color:var(--shell-color-danger)}.cri:disabled,.cri[aria-disabled=true]{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.crn,.crk{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--shell-icon-size);height:var(--shell-icon-size);color:var(--shell-color-muted)}.crk{color:var(--shell-color-accent)}.crl{flex:1;min-width:0}.cru{flex-shrink:0;margin-left:auto;font-size:var(--shell-font-size-sm);color:var(--shell-color-muted)}.crm{display:inline-flex;flex-shrink:0;margin-left:auto;width:var(--shell-icon-size);height:var(--shell-icon-size);color:var(--shell-color-muted)}.crb{display:inline-flex;flex-shrink:0;width:var(--shell-status-size);height:var(--shell-status-size);border-radius:50%}.cro{background:var(--shell-color-success)}.crf{background:var(--shell-color-muted)}.crx{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;box-sizing:border-box;width:var(--shell-check-size);height:var(--shell-check-size);border:1px solid var(--shell-border);border-radius:calc(var(--shell-radius) * .4);background:var(--shell-background);color:#fff}.cry{border-color:var(--shell-color-accent);background:var(--shell-color-accent)}.crkv{width:70%;height:70%;display:block}}
@@ -0,0 +1,124 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Пункт выпадающего меню {@link Dropdown}.
4
+ *
5
+ * Идентификатор — всегда `value` (не `key`).
6
+ * Опциональные поля (`icon`, `shortcut`, …): есть → рисуем, нет → пропускаем.
7
+ */
8
+ export type DropdownItem = {
9
+ /** Уникальный идентификатор пункта (не `key`). */
10
+ value: string;
11
+ /** Подпись пункта. */
12
+ label?: React.ReactNode;
13
+ /** Иконка слева. */
14
+ icon?: React.ReactNode;
15
+ /** Горячая клавиша справа (например «⌘X»). */
16
+ shortcut?: React.ReactNode;
17
+ /** Галочка слева (выбор / toggle). */
18
+ checked?: boolean;
19
+ /** Режим чекбокса (колонки списка и т.п.). */
20
+ checkbox?: boolean;
21
+ /** Точка статуса вместо icon. */
22
+ status?: "online" | "offline";
23
+ /** Стрелка submenu справа (визуальный намёк; логика submenu — снаружи). */
24
+ submenu?: boolean;
25
+ /** Недоступный пункт. */
26
+ disabled?: boolean;
27
+ /** Опасное действие (Delete) — красный акцент. */
28
+ danger?: boolean;
29
+ /** Локальный обработчик клика (дополнительно к `onSelect`). */
30
+ onClick?: () => void;
31
+ };
32
+ /**
33
+ * Секция панели dropdown: опциональный заголовок + `items`.
34
+ *
35
+ * Между секциями рисуется разделитель. Декларативный API, без compound-слотов.
36
+ */
37
+ export type DropdownGroup = {
38
+ /** Заголовок секции / панели («Колонки в списке»). */
39
+ title?: React.ReactNode;
40
+ /** Пункты секции. */
41
+ items: DropdownItem[];
42
+ };
43
+ /**
44
+ * Props компонента {@link Dropdown}.
45
+ *
46
+ * Передавайте либо плоский `items`, либо структурированные `groups`.
47
+ */
48
+ export type DropdownProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "onSelect"> & {
49
+ /**
50
+ * Плоский декларативный список пунктов.
51
+ * Для секций с разделителями используйте `groups`.
52
+ */
53
+ items?: DropdownItem[];
54
+ /** Секции с разделителями между ними. */
55
+ groups?: DropdownGroup[];
56
+ /** Кастомный триггер; иначе кнопка из `label`. */
57
+ trigger?: React.ReactNode;
58
+ /** Текст встроенного триггера. */
59
+ label?: React.ReactNode;
60
+ /** Скрыть chevron у встроенного триггера. */
61
+ hideChevron?: boolean;
62
+ /** Заголовок панели (если не задан в `groups[].title`). */
63
+ title?: React.ReactNode;
64
+ /** Выравнивание панели относительно триггера. */
65
+ align?: "start" | "end";
66
+ /** Контролируемое открытие. */
67
+ open?: boolean;
68
+ /** Начальное открытие (неконтролируемый режим). */
69
+ defaultOpen?: boolean;
70
+ /** Колбэк смены open. */
71
+ onOpenChange?: (open: boolean) => void;
72
+ /**
73
+ * Закрывать панель после выбора.
74
+ * Для checkbox обычно `false`.
75
+ */
76
+ closeOnSelect?: boolean;
77
+ /** Заблокировать триггер. */
78
+ disabled?: boolean;
79
+ /** Выбор пункта: `value` + сам item. */
80
+ onSelect?: (value: string, item: DropdownItem) => void;
81
+ };
82
+ /**
83
+ * Выпадающее меню: триггер + панель с `groups` / `items` рядом с якорем (не portal).
84
+ *
85
+ * Когда использовать:
86
+ * - действия строки списка, переключатели колонок, контекстные пункты у кнопки.
87
+ *
88
+ * Отличия:
89
+ * - {@link Menu} — инлайн-навигация в потоке (шапка), без панели;
90
+ * - `@elcrm/overlay` — портальное меню/popover поверх layout (фокус-ловушка, слои).
91
+ *
92
+ * API: декларативные `items` / `groups`. Идентификатор пункта — `value` (не `key`).
93
+ * Compound-слоты (`Dropdown.Item`) не используются.
94
+ *
95
+ * CSS-токены: `--shell-*`, узкие `--dropdown-min-width` / `--dropdown-max-width` / `--dropdown-max-height`, `--popup-shadow`.
96
+ *
97
+ * @example
98
+ * ```tsx
99
+ * const groups = [
100
+ * {
101
+ * title: "Действия",
102
+ * items: [
103
+ * { value: "edit", label: "Изменить", icon: <Icons.Line name="edit" /> },
104
+ * { value: "delete", label: "Удалить", danger: true },
105
+ * ],
106
+ * },
107
+ * {
108
+ * title: "Колонки",
109
+ * items: [
110
+ * { value: "email", label: "Email", checkbox: true, checked: true },
111
+ * { value: "phone", label: "Телефон", checkbox: true },
112
+ * ],
113
+ * },
114
+ * ];
115
+ *
116
+ * <Dropdown
117
+ * label="Действия"
118
+ * groups={groups}
119
+ * closeOnSelect={false}
120
+ * onSelect={(value) => console.log(value)}
121
+ * />
122
+ * ```
123
+ */
124
+ export declare function Dropdown({ items, groups, trigger, label, hideChevron, title, align, open: openProp, defaultOpen, onOpenChange, closeOnSelect, disabled, onSelect, className, ...rest }: DropdownProps): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { D as p } from "./Dropdown-Cw5LKpcT.js";
2
+ export {
3
+ p as Dropdown
4
+ };
@@ -1 +1 @@
1
- @layer elcrm-components{.cpr{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--empty-state-gap);padding:var(--empty-state-padding);text-align:center;color:var(--empty-state-color)}.cpi{display:flex;margin-bottom:var(--empty-state-icon-gap);color:var(--empty-state-icon-color)}.cpt{margin:0;font-size:var(--empty-state-title-size);font-weight:600;color:var(--empty-state-title-color);line-height:1.3}.cpd{margin:0;max-width:var(--empty-state-max-width);font-size:var(--empty-state-description-size);color:var(--empty-state-description-color);line-height:1.45}.cpa{display:flex;flex-wrap:wrap;gap:var(--empty-state-actions-gap);justify-content:center;margin-top:var(--empty-state-actions-margin-top)}}
1
+ @layer elcrm-components{.cpr{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--shell-gap);padding:var(--shell-padding);text-align:center;color:var(--shell-color-muted)}.cpi{display:flex;margin-bottom:var(--shell-gap-sm);color:var(--shell-color-muted)}.cpt{margin:0;font-size:var(--shell-font-size-lg);font-weight:var(--shell-font-weight);color:var(--shell-color);line-height:1.3}.cpd{margin:0;max-width:22rem;font-size:var(--shell-font-size);color:var(--shell-color-muted);line-height:1.45}.cpa{display:flex;flex-wrap:wrap;gap:var(--shell-gap-sm);justify-content:center;margin-top:var(--shell-gap)}}
@@ -1,13 +1,36 @@
1
1
  import { default as React } from 'react';
2
+ /**
3
+ * Заглушка «нет данных»: иконка, заголовок, описание, действия.
4
+ *
5
+ * Роли: EmptyState после загрузки при пустом списке;
6
+ * во время загрузки — Loading; для секций с контентом — Section.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <EmptyState
11
+ * title="Нет клиентов"
12
+ * description="Создайте первого клиента"
13
+ * actions={<Button>Создать</Button>}
14
+ * />
15
+ * ```
16
+ *
17
+ * Токены: `--shell-*`.
18
+ */
2
19
  export type EmptyStateProps = React.HTMLAttributes<HTMLDivElement> & {
20
+ /** Заголовок заглушки */
3
21
  title?: React.ReactNode;
22
+ /** Поясняющий текст */
4
23
  description?: React.ReactNode;
24
+ /** Иконка над заголовком */
5
25
  icon?: React.ReactNode;
26
+ /** Кнопки / CTA под описанием */
6
27
  actions?: React.ReactNode;
28
+ /** Доп. контент между описанием и actions */
7
29
  children?: React.ReactNode;
8
30
  };
9
31
  /**
10
32
  * Заглушка «нет данных».
11
- * Токены: --empty-state-*
33
+ *
34
+ * @see Loading, Section, List
12
35
  */
13
36
  export declare function EmptyState({ title, description, icon, actions, children, className, ...rest }: EmptyStateProps): React.JSX.Element;
package/dist/Footer.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.cfr{padding:var(--footer-padding);color:var(--footer-color);background:var(--footer-background);border-top:1px solid var(--footer-border);font-size:var(--footer-font-size)}}
1
+ @layer elcrm-components{.cfr{padding:var(--shell-padding);color:var(--shell-color-muted);background:transparent;border-top:1px solid var(--shell-border);font-size:var(--shell-font-size-sm)}}
package/dist/Footer.d.ts CHANGED
@@ -1,6 +1,25 @@
1
1
  import { default as React } from 'react';
2
+ /**
3
+ * Подвал страницы или модуля.
4
+ *
5
+ * Роли: Footer в Layout (глобальный) или в PageShell как `footer` (прибит к низу модуля).
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * <Layout footer={<Footer>© elCRM · v1.0</Footer>}>
10
+ * …
11
+ * </Layout>
12
+ * ```
13
+ *
14
+ * Токены: `--shell-*`.
15
+ */
2
16
  export type FooterProps = React.HTMLAttributes<HTMLElement> & {
17
+ /** Содержимое подвала (копирайт, ссылки, статус) */
3
18
  children?: React.ReactNode;
4
19
  };
5
- /** Подвал. Токены: --footer-* */
20
+ /**
21
+ * Подвал приложения / страницы.
22
+ *
23
+ * @see Layout, PageShell, Header
24
+ */
6
25
  export declare function Footer({ children, className, ...rest }: FooterProps): React.JSX.Element;
package/dist/Header.css CHANGED
@@ -1 +1 @@
1
- @layer elcrm-components{.chr{display:flex;align-items:center;gap:var(--header-gap);flex-shrink:0;min-height:var(--header-height);padding:var(--header-padding);color:var(--header-color);background:var(--header-background);border-bottom:1px solid var(--header-border);border-radius:var(--header-radius);box-shadow:var(--header-shadow);margin:var(--header-margin);position:relative;z-index:var(--header-z)}.chb{flex-shrink:0}.chn{display:flex;align-items:center;min-width:0}.chs{flex:1;min-width:0;margin-left:auto;max-width:18rem}.cha{display:flex;align-items:center;gap:.5rem;flex-shrink:0;margin-left:auto}.chs+.cha{margin-left:0}}
1
+ @layer elcrm-components{.chr{display:flex;align-items:center;gap:var(--shell-gap);flex-shrink:0;min-height:var(--header-height);padding:var(--shell-padding);color:var(--shell-color);background:var(--shell-background);border-bottom:1px solid var(--shell-border);border-radius:var(--header-radius);box-shadow:var(--header-shadow);margin:var(--header-margin);position:relative;z-index:var(--header-z)}.chb{flex-shrink:0}.chn{display:flex;align-items:center;min-width:0}.chs{flex:1;min-width:0;margin-left:auto;max-width:18rem}.cha{display:flex;align-items:center;gap:var(--shell-gap-sm);flex-shrink:0;margin-left:auto}.chs+.cha{margin-left:0}}