@elcrm/components 0.0.1 → 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 +81 -2
  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 +26 -2
  16. package/dist/Block-BAXh0PPp.js +8 -0
  17. package/dist/Block.d.ts +30 -2
  18. package/dist/Block.js +1 -1
  19. package/dist/Brand.css +1 -1
  20. package/dist/Brand.d.ts +37 -2
  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 +28 -2
  27. package/dist/Column-DDaqX-A7.js +8 -0
  28. package/dist/Column.d.ts +24 -2
  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 +36 -2
  36. package/dist/Footer.css +1 -1
  37. package/dist/Footer.d.ts +25 -2
  38. package/dist/Header.css +1 -1
  39. package/dist/Header.d.ts +36 -2
  40. package/dist/Item.css +1 -1
  41. package/dist/Item.d.ts +42 -2
  42. package/dist/Layout.css +1 -1
  43. package/dist/Layout.d.ts +30 -2
  44. package/dist/List.css +1 -1
  45. package/dist/List.d.ts +29 -2
  46. package/dist/Loading.css +1 -1
  47. package/dist/Loading.d.ts +24 -2
  48. package/dist/Menu-DRBHvSdJ.js +40 -0
  49. package/dist/Menu.css +1 -1
  50. package/dist/Menu.d.ts +78 -2
  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 +101 -2
  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 +24 -2
  78. package/dist/Row.js +1 -1
  79. package/dist/Section.css +1 -1
  80. package/dist/Section.d.ts +49 -2
  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 +87 -2
  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 +33 -6
  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,33 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Каркас модуля: скролл в body, опциональный footer прибит к низу.
4
+ *
5
+ * Роли: PageShell = внутренний каркас контента в Layout;
6
+ * сверху часто PageHead / Section, снизу — панель действий.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <PageShell wide footer={<Footer><Button>Сохранить</Button></Footer>}>
11
+ * <PageHead title="Редактор" />
12
+ * …
13
+ * </PageShell>
14
+ * ```
15
+ *
16
+ * Токены: `--shell-*`.
17
+ */
18
+ export type PageShellProps = {
19
+ /** Контент модуля (скроллится) */
20
+ children: React.ReactNode;
21
+ /** Доп. CSS-класс */
22
+ className?: string;
23
+ /** Шире контент (редакторы, таблицы) */
24
+ wide?: boolean;
25
+ /** Нижняя панель вне скролла */
26
+ footer?: React.ReactNode;
27
+ } & Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "className">;
28
+ /**
29
+ * Каркас модуля: скролл в body, footer прибит к низу.
30
+ *
31
+ * @see PageHead, Layout, Section, Footer
32
+ */
33
+ export declare function PageShell({ children, className, wide, footer, ...rootProps }: PageShellProps): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { P as l } from "./PageShell-CghThHS3.js";
2
+ export {
3
+ l as PageShell
4
+ };
@@ -0,0 +1,126 @@
1
+ import e from "react";
2
+ import { c as i } from "./cx-CcykAxZN.js";
3
+ import './PricingGroup.css';const z = "cur", O = "cui", j = "cus", M = "cub", R = "cug", A = "cugt", G = "cum", I = "cuh", K = "cut", T = "cup", U = "cuz", W = "cuo", $ = "cue", q = "cud", C = "cux", H = "cuf", J = "cuy", Q = "cul", X = "cuu", Y = "cun", Z = "cuc", _ = "cuk", V = "cukv", t = {
4
+ r: z,
5
+ i: O,
6
+ s: j,
7
+ b: M,
8
+ g: R,
9
+ gt: A,
10
+ m: G,
11
+ h: I,
12
+ t: K,
13
+ p: T,
14
+ z: U,
15
+ o: W,
16
+ e: $,
17
+ d: q,
18
+ x: C,
19
+ f: H,
20
+ y: J,
21
+ l: Q,
22
+ u: X,
23
+ n: Y,
24
+ c: Z,
25
+ k: _,
26
+ kv: V
27
+ };
28
+ function k() {
29
+ return /* @__PURE__ */ e.createElement("span", { className: t.k, "aria-hidden": !0 }, /* @__PURE__ */ e.createElement("svg", { className: t.kv, viewBox: "0 0 12 12" }, /* @__PURE__ */ e.createElement(
30
+ "path",
31
+ {
32
+ d: "M2.5 6.2 4.8 8.5 9.5 3.5",
33
+ fill: "none",
34
+ stroke: "currentColor",
35
+ strokeWidth: "1.8",
36
+ strokeLinecap: "round",
37
+ strokeLinejoin: "round"
38
+ }
39
+ )));
40
+ }
41
+ function ee({ feature: l }) {
42
+ if (l != null && typeof l == "object" && !e.isValidElement(l) && "label" in l) {
43
+ const c = l;
44
+ return /* @__PURE__ */ e.createElement("li", { className: t.c }, c.icon != null ? c.icon : /* @__PURE__ */ e.createElement(k, null), /* @__PURE__ */ e.createElement("span", null, c.label));
45
+ }
46
+ return /* @__PURE__ */ e.createElement("li", { className: t.c }, /* @__PURE__ */ e.createElement(k, null), /* @__PURE__ */ e.createElement("span", null, l));
47
+ }
48
+ function N({ item: l }) {
49
+ const c = l.badge != null && (l.badgePlacement ?? "pill") === "pill";
50
+ return l.title == null && !c ? null : /* @__PURE__ */ e.createElement("div", { className: t.h }, l.title != null ? /* @__PURE__ */ e.createElement("h3", { className: t.t }, l.title) : null, c ? /* @__PURE__ */ e.createElement("span", { className: t.g }, l.badge) : null);
51
+ }
52
+ function h({ item: l }) {
53
+ return l.price == null && l.priceOld == null && l.meta == null ? null : /* @__PURE__ */ e.createElement("div", { className: t.p }, l.priceOld != null ? /* @__PURE__ */ e.createElement("span", { className: t.o }, l.priceOld) : null, l.price != null ? /* @__PURE__ */ e.createElement("span", { className: t.z }, l.price) : null, l.meta != null ? /* @__PURE__ */ e.createElement("span", { className: t.e }, l.meta) : null);
54
+ }
55
+ function d({ item: l }) {
56
+ return l.description == null ? null : /* @__PURE__ */ e.createElement("p", { className: t.d }, l.description);
57
+ }
58
+ function le({
59
+ actions: l,
60
+ placement: c
61
+ }) {
62
+ return /* @__PURE__ */ e.createElement(
63
+ "div",
64
+ {
65
+ className: c === "footer" ? t.f : t.x,
66
+ onClick: (a) => a.stopPropagation(),
67
+ onMouseDown: (a) => a.preventDefault()
68
+ },
69
+ l
70
+ );
71
+ }
72
+ function ne({ item: l }) {
73
+ return l.features == null && l.featuresLabel == null && l.featuresSummary == null ? null : /* @__PURE__ */ e.createElement("div", { className: t.y }, l.featuresLabel != null ? /* @__PURE__ */ e.createElement("p", { className: t.l }, l.featuresLabel) : null, l.featuresSummary != null ? /* @__PURE__ */ e.createElement("p", { className: t.u }, l.featuresSummary) : null, l.features != null && l.features.length > 0 ? /* @__PURE__ */ e.createElement("ul", { className: t.n }, l.features.map((c, a) => /* @__PURE__ */ e.createElement(ee, { key: a, feature: c }))) : null);
74
+ }
75
+ function ae({
76
+ items: l,
77
+ value: c,
78
+ defaultValue: a,
79
+ selectable: y = !1,
80
+ disabled: P = !1,
81
+ onValueChange: u,
82
+ className: x,
83
+ ...B
84
+ }) {
85
+ const [D, F] = e.useState(a), m = c !== void 0, w = m ? c : D, f = (n) => {
86
+ m || F(n), u == null || u(n);
87
+ };
88
+ return /* @__PURE__ */ e.createElement("div", { className: i(t.r, x), role: "list", ...B }, l.map((n) => {
89
+ const p = w === n.value, E = P || !!n.disabled, L = !!n.highlighted || p, g = n.badgePlacement ?? "pill", r = n.actionsPlacement ?? "after", v = n.descriptionPlacement ?? "after-price", s = y && !E, b = n.actions != null ? /* @__PURE__ */ e.createElement(
90
+ le,
91
+ {
92
+ actions: n.actions,
93
+ placement: r
94
+ }
95
+ ) : null, S = /* @__PURE__ */ e.createElement(e.Fragment, null, n.priceFirst ? /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(h, { item: n }), /* @__PURE__ */ e.createElement(N, { item: n }), /* @__PURE__ */ e.createElement(d, { item: n })) : /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(N, { item: n }), v === "before-price" ? /* @__PURE__ */ e.createElement(d, { item: n }) : null, /* @__PURE__ */ e.createElement(h, { item: n }), v === "after-price" ? /* @__PURE__ */ e.createElement(d, { item: n }) : null));
96
+ return /* @__PURE__ */ e.createElement(
97
+ "div",
98
+ {
99
+ key: n.value,
100
+ role: "listitem",
101
+ className: i(t.i, L && t.s),
102
+ "data-selected": p ? "true" : void 0,
103
+ "data-highlighted": n.highlighted ? "true" : void 0,
104
+ "data-selectable": s ? "true" : void 0,
105
+ "aria-disabled": E || void 0,
106
+ tabIndex: s ? 0 : void 0,
107
+ onClick: () => {
108
+ s && f(n.value);
109
+ },
110
+ onKeyDown: (o) => {
111
+ s && (o.key === "Enter" || o.key === " ") && (o.preventDefault(), f(n.value));
112
+ }
113
+ },
114
+ n.badge != null && g === "banner" ? /* @__PURE__ */ e.createElement("div", { className: t.b }, n.badge) : null,
115
+ n.badge != null && g === "float" ? /* @__PURE__ */ e.createElement("div", { className: i(t.g, t.gt) }, n.badge) : null,
116
+ n.icon != null ? /* @__PURE__ */ e.createElement("div", { className: t.m }, n.icon) : null,
117
+ S,
118
+ r === "before" ? b : null,
119
+ /* @__PURE__ */ e.createElement(ne, { item: n }),
120
+ r === "after" || r === "footer" ? b : null
121
+ );
122
+ }));
123
+ }
124
+ export {
125
+ ae as P
126
+ };
@@ -0,0 +1 @@
1
+ @layer elcrm-components{.cur{display:grid;grid-template-columns:repeat(auto-fit,minmax(var(--pricing-card-min-width),1fr));gap:var(--shell-gap-lg);align-items:stretch}.cui{position:relative;display:flex;flex-direction:column;gap:var(--shell-gap);padding:var(--shell-padding);box-sizing:border-box;text-align:left;font:inherit;color:var(--shell-color);background:var(--shell-background);border:1px solid var(--shell-border);border-radius:var(--shell-radius);box-shadow:var(--shell-shadow)}.cui[data-selectable=true]{cursor:pointer}.cui:disabled,.cui[aria-disabled=true]{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cus,.cui[data-selected=true],.cui[data-highlighted=true]{border-color:var(--shell-color-accent);background:var(--shell-background-selected);box-shadow:var(--shell-shadow)}.cub{display:flex;align-items:center;justify-content:center;margin:calc(-1 * var(--shell-padding-block)) calc(-1 * var(--shell-padding-inline)) 0;padding:var(--shell-gap-sm) var(--shell-padding-inline);border-radius:var(--shell-radius) var(--shell-radius) 0 0;background:var(--shell-color-accent);color:#fff;font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight);text-align:center}.cug{display:inline-flex;align-items:center;width:fit-content;padding:.15rem .5rem;border-radius:999px;background:var(--shell-background-hover);color:var(--shell-color-accent);font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight);line-height:1.2}.cugt{position:absolute;top:0;left:50%;transform:translate(-50%,-50%)}.cum{display:flex;flex-shrink:0;align-items:center;justify-content:center;width:calc(var(--shell-icon-size) * 1.6);height:calc(var(--shell-icon-size) * 1.6);color:var(--shell-color-accent);background:var(--shell-background-hover);border-radius:50%;overflow:hidden}.cuh{display:flex;flex-wrap:wrap;align-items:center;gap:var(--shell-gap-sm)}.cut{margin:0;font-size:var(--shell-font-size);font-weight:var(--shell-font-weight);color:var(--shell-color);line-height:1.3}.cup{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--shell-gap-sm)}.cuz{font-size:calc(var(--shell-font-size-lg) * 1.35);font-weight:700;color:var(--shell-color);line-height:1.15}.cui[data-highlighted=true] .cuz,.cui[data-selected=true] .cuz{color:var(--shell-color-accent)}.cuo{font-size:var(--shell-font-size-sm);color:var(--shell-color-muted);text-decoration:line-through}.cue{font-size:var(--shell-font-size-sm);color:var(--shell-color-muted)}.cud{margin:0;font-size:var(--shell-font-size-sm);color:var(--shell-color-muted);line-height:1.45}.cux{display:flex;flex-direction:column;gap:var(--shell-gap-sm)}.cuf{display:flex;flex-direction:column;gap:var(--shell-gap-sm);margin:auto calc(-1 * var(--shell-padding-inline)) calc(-1 * var(--shell-padding-block));padding:var(--shell-padding);background:var(--shell-background-muted);border-top:1px solid var(--shell-border);border-radius:0 0 var(--shell-radius) var(--shell-radius)}.cuy{display:flex;flex-direction:column;gap:var(--shell-gap-sm);margin-top:auto}.cul{margin:0;font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight);letter-spacing:.04em;text-transform:uppercase;color:var(--shell-color-muted)}.cuu{margin:0;font-size:var(--shell-font-size-sm);color:var(--shell-color-muted);line-height:1.4}.cun{display:flex;flex-direction:column;gap:var(--shell-gap-sm);margin:0;padding:0;list-style:none}.cuc{display:flex;align-items:flex-start;gap:var(--shell-gap-sm);font-size:var(--shell-font-size-sm);color:var(--shell-color);line-height:1.4}.cuk{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--shell-check-size);height:var(--shell-check-size);margin-top:.1em;color:#fff;background:var(--shell-color-success);border-radius:50%}.cukv{width:60%;height:60%;display:block}}
@@ -0,0 +1,130 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Фича тарифа: простой узел или объект с подписью и опциональной иконкой.
4
+ */
5
+ export type PricingFeature = React.ReactNode | {
6
+ /** Текст фичи. */
7
+ label: React.ReactNode;
8
+ /** Своя иконка; иначе — галочка из коробки. */
9
+ icon?: React.ReactNode;
10
+ };
11
+ /**
12
+ * Общий тип тарифного плана для {@link PricingGroup} и {@link PricingTable}.
13
+ *
14
+ * Один массив `items` (`PricingPlan[]`) можно переиспользовать в обоих компонентах.
15
+ * Идентификатор плана — `value` (не `key`).
16
+ * Опциональные поля: нет → соответствующий блок не рисуется.
17
+ * Декларативный API `items`, без compound-слотов.
18
+ */
19
+ export type PricingPlan = {
20
+ /** Уникальный идентификатор плана (не `key`). */
21
+ value: string;
22
+ /** Название тарифа. */
23
+ title?: React.ReactNode;
24
+ /** Краткое описание. */
25
+ description?: React.ReactNode;
26
+ /** Иконка сверху — только если передана. */
27
+ icon?: React.ReactNode;
28
+ /** Цена. */
29
+ price?: React.ReactNode;
30
+ /** Старая цена (зачёркнутая). */
31
+ priceOld?: React.ReactNode;
32
+ /** «per month», «Billed annually», «в месяц…». */
33
+ meta?: React.ReactNode;
34
+ /** Popular / Рекомендуем / Most popular plan. */
35
+ badge?: React.ReactNode;
36
+ /**
37
+ * Расположение badge:
38
+ * `banner` — полоса сверху;
39
+ * `pill` — рядом с title;
40
+ * `float` — над верхней гранью по центру.
41
+ */
42
+ badgePlacement?: "banner" | "pill" | "float";
43
+ /** Цена выше title (макет «Most popular»). */
44
+ priceFirst?: boolean;
45
+ /** description до или после блока цены. */
46
+ descriptionPlacement?: "before-price" | "after-price";
47
+ /** Список фич. */
48
+ features?: PricingFeature[];
49
+ /** Заголовок блока фич («FEATURES»). */
50
+ featuresLabel?: React.ReactNode;
51
+ /** Сводка («Everything in Starter plus…»). */
52
+ featuresSummary?: React.ReactNode;
53
+ /** Кнопки (приложение даёт `@elcrm/button` и т.п.). */
54
+ actions?: React.ReactNode;
55
+ /**
56
+ * Куда ставить actions:
57
+ * `after` — под фичами (по умолчанию);
58
+ * `before` — над фичами;
59
+ * `footer` — в подложке внизу.
60
+ */
61
+ actionsPlacement?: "before" | "after" | "footer";
62
+ /** Акцентная рамка / «рекомендуем». */
63
+ highlighted?: boolean;
64
+ /** Недоступный план. */
65
+ disabled?: boolean;
66
+ };
67
+ /** @deprecated используй {@link PricingPlan} */
68
+ export type PricingGroupItem = PricingPlan;
69
+ /**
70
+ * Props компонента {@link PricingGroup}.
71
+ *
72
+ * Групповой UI: `items: PricingPlan[]` + опциональный выбор по `value`.
73
+ */
74
+ export type PricingGroupProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
75
+ /** Тарифные карточки — тот же shape, что у {@link PricingTable}. */
76
+ items: PricingPlan[];
77
+ /** Выбранный план (controlled), сравнивается с `item.value`. */
78
+ value?: string;
79
+ /** Начальный выбранный план (uncontrolled). */
80
+ defaultValue?: string;
81
+ /** Клик по карточке выбирает value (actions не трогают выбор). */
82
+ selectable?: boolean;
83
+ /** Заблокировать выбор. */
84
+ disabled?: boolean;
85
+ /** Смена выбранного плана. */
86
+ onValueChange?: (value: string) => void;
87
+ };
88
+ /**
89
+ * Горизонтальная группа тарифных карточек из декларативного `items`.
90
+ *
91
+ * Когда использовать:
92
+ * - страница тарифов / upgrade без матрицы сравнения;
93
+ * - один и тот же массив {@link PricingPlan} позже уйдёт в {@link PricingTable}.
94
+ *
95
+ * Связанные компоненты:
96
+ * - {@link PricingTable} — сравнительная матрица поверх тех же `items`;
97
+ * - {@link RadioGroup} — выбор опции с индикатором, не обязательно тариф.
98
+ *
99
+ * Общий тип плана — {@link PricingPlan}. Идентификатор — `value` (не `key`).
100
+ * Compound-слоты не используются.
101
+ *
102
+ * CSS-токены: `--shell-*`, узкие `--pricing-card-min-width`.
103
+ *
104
+ * @example
105
+ * ```tsx
106
+ * const items: PricingPlan[] = [
107
+ * {
108
+ * value: "starter",
109
+ * title: "Starter",
110
+ * price: "$9",
111
+ * meta: "в месяц",
112
+ * features: ["1 проект", "Email-поддержка"],
113
+ * actions: <Button onSend={…}>Выбрать</Button>,
114
+ * },
115
+ * {
116
+ * value: "pro",
117
+ * title: "Pro",
118
+ * price: "$29",
119
+ * badge: "Popular",
120
+ * badgePlacement: "float",
121
+ * highlighted: true,
122
+ * features: ["Безлимит", "SSO"],
123
+ * actions: <Button onSend={…}>Выбрать</Button>,
124
+ * },
125
+ * ];
126
+ *
127
+ * <PricingGroup items={items} selectable value={plan} onValueChange={setPlan} />
128
+ * ```
129
+ */
130
+ export declare function PricingGroup({ items, value: valueProp, defaultValue, selectable, disabled, onValueChange, className, ...rest }: PricingGroupProps): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { P as i } from "./PricingGroup-CiqQtzjd.js";
2
+ export {
3
+ i as PricingGroup
4
+ };
@@ -0,0 +1,158 @@
1
+ import l from "react";
2
+ import { c as z } from "./cx-CcykAxZN.js";
3
+ import './PricingTable.css';const B = "cxr", C = "cxh", M = "cxm", O = "cxn", T = "cxt", I = "cxg", L = "cxp", S = "cxz", j = "cxo", H = "cxe", K = "cxd", R = "cxx", U = "cxb", W = "cxy", $ = "cxl", A = "cxw", G = "cxc", J = "cxk", Q = "cxkv", X = "cxa", Y = "cxft", Z = "cxq", n = {
4
+ r: B,
5
+ h: C,
6
+ m: M,
7
+ n: O,
8
+ t: T,
9
+ g: I,
10
+ p: L,
11
+ z: S,
12
+ o: j,
13
+ e: H,
14
+ d: K,
15
+ x: R,
16
+ b: U,
17
+ y: W,
18
+ l: $,
19
+ w: A,
20
+ c: G,
21
+ k: J,
22
+ kv: Q,
23
+ a: X,
24
+ ft: Y,
25
+ q: Z
26
+ };
27
+ function _() {
28
+ return /* @__PURE__ */ l.createElement("span", { className: n.k, "aria-hidden": !0 }, /* @__PURE__ */ l.createElement("svg", { className: n.kv, viewBox: "0 0 12 12" }, /* @__PURE__ */ l.createElement(
29
+ "path",
30
+ {
31
+ d: "M2.5 6.2 4.8 8.5 9.5 3.5",
32
+ fill: "none",
33
+ stroke: "currentColor",
34
+ strokeWidth: "1.8",
35
+ strokeLinecap: "round",
36
+ strokeLinejoin: "round"
37
+ }
38
+ )));
39
+ }
40
+ function V({ value: e }) {
41
+ return e === !0 ? /* @__PURE__ */ l.createElement(_, null) : e === !1 || e == null || e === "" ? /* @__PURE__ */ l.createElement("span", { className: n.a }, "—") : /* @__PURE__ */ l.createElement(l.Fragment, null, e);
42
+ }
43
+ function ee({
44
+ item: e,
45
+ highlighted: r,
46
+ selectable: o,
47
+ disabled: u,
48
+ onSelect: d
49
+ }) {
50
+ const i = e.badge != null && (e.badgePlacement ?? "pill") === "pill", m = e.badge != null && e.badgePlacement === "banner";
51
+ return /* @__PURE__ */ l.createElement(
52
+ "div",
53
+ {
54
+ className: n.h,
55
+ "data-highlighted": r ? "true" : void 0,
56
+ "data-selectable": o ? "true" : void 0,
57
+ tabIndex: o && !u ? 0 : void 0,
58
+ onClick: () => {
59
+ o && !u && d();
60
+ },
61
+ onKeyDown: (a) => {
62
+ !o || u || (a.key === "Enter" || a.key === " ") && (a.preventDefault(), d());
63
+ }
64
+ },
65
+ m ? /* @__PURE__ */ l.createElement("span", { className: n.g }, e.badge) : null,
66
+ e.icon != null ? /* @__PURE__ */ l.createElement("div", { className: n.m }, e.icon) : null,
67
+ e.priceFirst ? /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(f, { item: e }), /* @__PURE__ */ l.createElement(x, { item: e, pill: i }), e.description != null ? /* @__PURE__ */ l.createElement("p", { className: n.d }, e.description) : null) : /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(x, { item: e, pill: i }), e.descriptionPlacement === "before-price" && e.description != null ? /* @__PURE__ */ l.createElement("p", { className: n.d }, e.description) : null, /* @__PURE__ */ l.createElement(f, { item: e }), e.descriptionPlacement !== "before-price" && e.description != null ? /* @__PURE__ */ l.createElement("p", { className: n.d }, e.description) : null),
68
+ e.actions != null ? /* @__PURE__ */ l.createElement(
69
+ "div",
70
+ {
71
+ className: n.x,
72
+ onClick: (a) => a.stopPropagation(),
73
+ onMouseDown: (a) => a.preventDefault()
74
+ },
75
+ e.actions
76
+ ) : null
77
+ );
78
+ }
79
+ function x({
80
+ item: e,
81
+ pill: r
82
+ }) {
83
+ return e.title == null && !r ? null : /* @__PURE__ */ l.createElement("div", { className: n.n }, e.title != null ? /* @__PURE__ */ l.createElement("h3", { className: n.t }, e.title) : null, r ? /* @__PURE__ */ l.createElement("span", { className: n.g }, e.badge) : null);
84
+ }
85
+ function f({ item: e }) {
86
+ return e.price == null && e.priceOld == null && e.meta == null ? null : /* @__PURE__ */ l.createElement("div", { className: n.p }, e.priceOld != null ? /* @__PURE__ */ l.createElement("span", { className: n.o }, e.priceOld) : null, e.price != null ? /* @__PURE__ */ l.createElement("span", { className: n.z }, e.price) : null, e.meta != null ? /* @__PURE__ */ l.createElement("span", { className: n.e }, e.meta) : null);
87
+ }
88
+ function te({
89
+ items: e,
90
+ sections: r,
91
+ footerActions: o = !0,
92
+ value: u,
93
+ defaultValue: d,
94
+ selectable: i = !1,
95
+ disabled: m = !1,
96
+ onValueChange: a,
97
+ className: N,
98
+ style: g,
99
+ ...h
100
+ }) {
101
+ const [k, b] = l.useState(d), p = u !== void 0, y = p ? u : k, w = (t) => {
102
+ p || b(t), a == null || a(t);
103
+ }, P = e.length, D = {
104
+ ...g,
105
+ gridTemplateColumns: `var(--pricing-table-label-width) repeat(${P}, minmax(12rem, 1fr))`
106
+ };
107
+ let F = 0;
108
+ return /* @__PURE__ */ l.createElement("div", { className: z(n.r, N), style: D, role: "table", ...h }, /* @__PURE__ */ l.createElement("div", { className: n.q, role: "columnheader" }), e.map((t) => {
109
+ const s = y === t.value, c = !!t.highlighted || s;
110
+ return /* @__PURE__ */ l.createElement(
111
+ ee,
112
+ {
113
+ key: t.value,
114
+ item: t,
115
+ highlighted: c,
116
+ selectable: i && !m && !t.disabled,
117
+ disabled: m || !!t.disabled,
118
+ onSelect: () => w(t.value)
119
+ }
120
+ );
121
+ }), r == null ? void 0 : r.map((t, s) => /* @__PURE__ */ l.createElement(l.Fragment, { key: s }, t.title != null ? /* @__PURE__ */ l.createElement("div", { className: n.b, role: "rowheader" }, t.title) : null, t.rows.map((c) => {
122
+ const q = F++ % 2 === 1;
123
+ return /* @__PURE__ */ l.createElement(
124
+ "div",
125
+ {
126
+ key: c.key,
127
+ className: n.y,
128
+ "data-alt": q ? "true" : void 0,
129
+ role: "row"
130
+ },
131
+ /* @__PURE__ */ l.createElement("div", { className: n.l, role: "rowheader" }, /* @__PURE__ */ l.createElement("span", null, c.label), c.hint != null && c.hint !== "" ? /* @__PURE__ */ l.createElement(
132
+ "button",
133
+ {
134
+ type: "button",
135
+ className: n.w,
136
+ title: c.hint,
137
+ "aria-label": c.hint
138
+ },
139
+ "?"
140
+ ) : null),
141
+ e.map((E) => {
142
+ var v;
143
+ return /* @__PURE__ */ l.createElement("div", { key: E.value, className: n.c, role: "cell" }, /* @__PURE__ */ l.createElement(V, { value: (v = c.values) == null ? void 0 : v[E.value] }));
144
+ })
145
+ );
146
+ }))), o ? /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement("div", { className: n.q }), e.map((t) => /* @__PURE__ */ l.createElement("div", { key: t.value, className: n.ft }, t.actions != null ? /* @__PURE__ */ l.createElement(
147
+ "div",
148
+ {
149
+ className: n.x,
150
+ onClick: (s) => s.stopPropagation(),
151
+ onMouseDown: (s) => s.preventDefault()
152
+ },
153
+ t.actions
154
+ ) : null))) : null);
155
+ }
156
+ export {
157
+ te as P
158
+ };
@@ -0,0 +1 @@
1
+ @layer elcrm-components{.cxr{display:grid;width:100%;column-gap:var(--shell-gap);row-gap:0;align-items:stretch;overflow-x:auto}.cxh{display:flex;flex-direction:column;gap:var(--shell-gap-sm);padding:var(--shell-padding);box-sizing:border-box;color:var(--shell-color);background:var(--shell-background);border:1px solid transparent;border-radius:var(--shell-radius)}.cxh[data-highlighted=true]{border-color:var(--shell-color-accent);background:var(--shell-background-selected)}.cxm{display:flex;align-items:center;justify-content:center;width:calc(var(--shell-icon-size) * 1.4);height:calc(var(--shell-icon-size) * 1.4);color:var(--shell-color-accent);background:var(--shell-background-hover);border-radius:var(--shell-radius);overflow:hidden}.cxn{display:flex;flex-wrap:wrap;align-items:center;gap:var(--shell-gap-sm)}.cxt{margin:0;font-size:var(--shell-font-size);font-weight:var(--shell-font-weight);color:var(--shell-color);line-height:1.3}.cxg{display:inline-flex;align-items:center;padding:.15rem .5rem;border-radius:999px;background:var(--shell-background-hover);color:var(--shell-color-accent);font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight)}.cxp{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--shell-gap-sm)}.cxz{font-size:var(--shell-font-size-lg);font-weight:700;color:var(--shell-color);line-height:1.15}.cxo{font-size:var(--shell-font-size-sm);color:var(--shell-color-muted);text-decoration:line-through}.cxe{font-size:var(--shell-font-size-sm);color:var(--shell-color-muted)}.cxd{margin:0;font-size:var(--shell-font-size-sm);color:var(--shell-color-muted);line-height:1.45}.cxx{display:flex;flex-direction:column;gap:var(--shell-gap-sm)}.cxb{grid-column:1 / -1;margin:var(--shell-gap) 0 var(--shell-gap-sm);padding:0;font-size:var(--shell-font-size-sm);font-weight:var(--shell-font-weight);color:var(--shell-color-accent)}.cxf,.cxy{display:contents}.cxy>*{padding:var(--shell-padding);box-sizing:border-box;border-bottom:1px solid var(--shell-border);background:var(--shell-background)}.cxy[data-alt=true]>*{background:var(--shell-background-muted)}.cxl{display:flex;align-items:center;gap:var(--shell-gap-sm);font-size:var(--shell-font-size-sm);color:var(--shell-color);line-height:1.4}.cxw{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--shell-status-size);height:var(--shell-status-size);padding:0;border:1px solid var(--shell-border);border-radius:50%;background:transparent;color:var(--shell-color-muted);font-size:9px;line-height:1;cursor:help}.cxc{display:flex;align-items:center;justify-content:center;text-align:center;font-size:var(--shell-font-size-sm);color:var(--shell-color);font-weight:500}.cxk{display:inline-flex;align-items:center;justify-content:center;width:var(--shell-check-size);height:var(--shell-check-size);color:#fff;background:var(--shell-color-success);border-radius:50%}.cxkv{width:60%;height:60%;display:block}.cxa{color:var(--shell-color-muted)}.cxft{display:flex;flex-direction:column;gap:var(--shell-gap-sm);padding:var(--shell-padding);box-sizing:border-box}.cxq{min-width:var(--pricing-table-label-width)}}
@@ -0,0 +1,108 @@
1
+ import { default as React } from 'react';
2
+ import { PricingPlan } from './PricingGroup';
3
+ /**
4
+ * Колонка тарифа в таблице — тот же {@link PricingPlan}, что у {@link PricingGroup}.
5
+ * Один массив `items` можно шарить между Group и Table.
6
+ */
7
+ export type PricingTableItem = PricingPlan;
8
+ /**
9
+ * Значение ячейки матрицы:
10
+ * `true` — галочка, `false` / `null` / нет ключа — «—», иначе текст/узел.
11
+ */
12
+ export type PricingTableCellValue = boolean | React.ReactNode | null | undefined;
13
+ /**
14
+ * Строка сравнения в секции таблицы.
15
+ *
16
+ * Ключи в `values` — это `item.value` планов (не React `key` колонки).
17
+ */
18
+ export type PricingTableRow = {
19
+ /** Стабильный ключ строки в секции (React key / id строки). */
20
+ key: string;
21
+ /** Подпись слева. */
22
+ label: React.ReactNode;
23
+ /** Подсказка у «?» рядом с label. */
24
+ hint?: string;
25
+ /**
26
+ * Значения по `item.value` плана.
27
+ * `true` — галочка, `false` / `null` / нет ключа — «—», иначе текст/node.
28
+ */
29
+ values?: Record<string, PricingTableCellValue>;
30
+ };
31
+ /**
32
+ * Секция матрицы сравнения (заголовок + строки).
33
+ */
34
+ export type PricingTableSection = {
35
+ /** Заголовок секции («Безопасность»). Нет — не рисуем. */
36
+ title?: React.ReactNode;
37
+ /** Строки фич / параметров. */
38
+ rows: PricingTableRow[];
39
+ };
40
+ /**
41
+ * Props компонента {@link PricingTable}.
42
+ *
43
+ * `items` — колонки планов ({@link PricingPlan}); `sections` — матрица сравнения.
44
+ */
45
+ export type PricingTableProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
46
+ /** Колонки планов — тот же shape, что у {@link PricingGroup}. */
47
+ items: PricingTableItem[];
48
+ /** Матрица сравнения (секции → строки → values по plan.value). */
49
+ sections?: PricingTableSection[];
50
+ /** Повторить actions внизу таблицы (как на макетах). */
51
+ footerActions?: boolean;
52
+ /** Выбранный план (controlled). */
53
+ value?: string;
54
+ /** Начальный план (uncontrolled). */
55
+ defaultValue?: string;
56
+ /** Клик по шапке колонки выбирает план. */
57
+ selectable?: boolean;
58
+ /** Заблокировать выбор. */
59
+ disabled?: boolean;
60
+ /** Смена выбранного плана (`item.value`). */
61
+ onValueChange?: (value: string) => void;
62
+ };
63
+ /**
64
+ * Сравнительная таблица тарифов: колонки из `items` + матрица `sections`.
65
+ *
66
+ * Когда использовать:
67
+ * - страница сравнения фич между планами;
68
+ * - те же {@link PricingPlan} уже используются в {@link PricingGroup}.
69
+ *
70
+ * Связанные компоненты:
71
+ * - {@link PricingGroup} — карточки без матрицы;
72
+ * - общий тип — {@link PricingPlan}.
73
+ *
74
+ * API: декларативные `items` (+ `sections`). Идентификатор плана — `value` (не `key`).
75
+ * Compound-слоты не используются.
76
+ *
77
+ * CSS-токены: `--shell-*`, узкие `--pricing-table-label-width`, `--pricing-card-min-width`.
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * const items: PricingPlan[] = [
82
+ * { value: "starter", title: "Starter", price: "$9", actions: <Button>…</Button> },
83
+ * { value: "pro", title: "Pro", price: "$29", highlighted: true, actions: <Button>…</Button> },
84
+ * ];
85
+ *
86
+ * const sections = [
87
+ * {
88
+ * title: "Команда",
89
+ * rows: [
90
+ * {
91
+ * key: "seats",
92
+ * label: "Участники",
93
+ * values: { starter: "3", pro: "Безлимит" },
94
+ * },
95
+ * {
96
+ * key: "sso",
97
+ * label: "SSO",
98
+ * hint: "Единый вход",
99
+ * values: { starter: false, pro: true },
100
+ * },
101
+ * ],
102
+ * },
103
+ * ];
104
+ *
105
+ * <PricingTable items={items} sections={sections} selectable />
106
+ * ```
107
+ */
108
+ export declare function PricingTable({ items, sections, footerActions, value: valueProp, defaultValue, selectable, disabled, onValueChange, className, style, ...rest }: PricingTableProps): React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import { P as e } from "./PricingTable-DYTJG4AU.js";
2
+ export {
3
+ e as PricingTable
4
+ };