@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.
- package/README.md +450 -202
- package/dist/Avatar-Jzan87XD.js +101 -0
- package/dist/Avatar.css +1 -1
- package/dist/Avatar.d.ts +56 -5
- package/dist/Avatar.js +1 -1
- package/dist/AvatarGroup-DUe7-T3B.js +81 -0
- package/dist/AvatarGroup.css +1 -0
- package/dist/AvatarGroup.d.ts +77 -0
- package/dist/AvatarGroup.js +4 -0
- package/dist/AvatarName-KBbQrVta.js +56 -0
- package/dist/AvatarName.css +1 -0
- package/dist/AvatarName.d.ts +76 -0
- package/dist/AvatarName.js +4 -0
- package/dist/Badge.css +1 -1
- package/dist/Badge.d.ts +16 -1
- package/dist/Block-BAXh0PPp.js +8 -0
- package/dist/Block.d.ts +27 -6
- package/dist/Block.js +1 -1
- package/dist/Brand.css +1 -1
- package/dist/Brand.d.ts +20 -1
- package/dist/Breadcrumb-D0wXkE38.js +98 -0
- package/dist/Breadcrumb.css +1 -0
- package/dist/Breadcrumb.d.ts +80 -0
- package/dist/Breadcrumb.js +4 -0
- package/dist/Card.css +1 -1
- package/dist/Card.d.ts +22 -1
- package/dist/Column-DDaqX-A7.js +8 -0
- package/dist/Column.d.ts +23 -7
- package/dist/Column.js +1 -1
- package/dist/Dropdown-Cw5LKpcT.js +168 -0
- package/dist/Dropdown.css +1 -0
- package/dist/Dropdown.d.ts +124 -0
- package/dist/Dropdown.js +4 -0
- package/dist/EmptyState.css +1 -1
- package/dist/EmptyState.d.ts +24 -1
- package/dist/Footer.css +1 -1
- package/dist/Footer.d.ts +20 -1
- package/dist/Header.css +1 -1
- package/dist/Header.d.ts +25 -2
- package/dist/Item.css +1 -1
- package/dist/Item.d.ts +24 -1
- package/dist/Layout.css +1 -1
- package/dist/Layout.d.ts +23 -3
- package/dist/List.css +1 -1
- package/dist/List.d.ts +22 -1
- package/dist/Loading.css +1 -1
- package/dist/Loading.d.ts +19 -1
- package/dist/Menu-DRBHvSdJ.js +40 -0
- package/dist/Menu.css +1 -1
- package/dist/Menu.d.ts +64 -8
- package/dist/Menu.js +1 -1
- package/dist/{NavSections-CM-VPai2.js → NavSections-NDcIDD6H.js} +26 -26
- package/dist/NavSections.css +1 -1
- package/dist/NavSections.d.ts +79 -15
- package/dist/NavSections.js +1 -1
- package/dist/PageHead-Baz9BLuA.js +22 -0
- package/dist/PageHead.css +1 -0
- package/dist/PageHead.d.ts +36 -0
- package/dist/PageHead.js +4 -0
- package/dist/PageShell-CghThHS3.js +28 -0
- package/dist/PageShell.css +1 -0
- package/dist/PageShell.d.ts +33 -0
- package/dist/PageShell.js +4 -0
- package/dist/PricingGroup-CiqQtzjd.js +126 -0
- package/dist/PricingGroup.css +1 -0
- package/dist/PricingGroup.d.ts +130 -0
- package/dist/PricingGroup.js +4 -0
- package/dist/PricingTable-DYTJG4AU.js +158 -0
- package/dist/PricingTable.css +1 -0
- package/dist/PricingTable.d.ts +108 -0
- package/dist/PricingTable.js +4 -0
- package/dist/RadioGroup-DxoPHPIF.js +138 -0
- package/dist/RadioGroup.css +1 -0
- package/dist/RadioGroup.d.ts +111 -0
- package/dist/RadioGroup.js +4 -0
- package/dist/Row--HIe6vb9.js +8 -0
- package/dist/Row.d.ts +23 -7
- package/dist/Row.js +1 -1
- package/dist/Section.css +1 -1
- package/dist/Section.d.ts +23 -3
- package/dist/Stack-b0-XWwQ1.js +31 -0
- package/dist/Stack.css +1 -0
- package/dist/Stack.d.ts +34 -0
- package/dist/Stack.js +4 -0
- package/dist/TabSections-DL7DufKU.js +50 -0
- package/dist/TabSections.css +1 -1
- package/dist/TabSections.d.ts +69 -13
- package/dist/TabSections.js +1 -1
- package/dist/cx.d.ts +8 -1
- package/dist/dark.css +24 -0
- package/dist/index.css +32 -18
- package/dist/index.d.ts +21 -1
- package/dist/index.es.js +58 -38
- package/dist/lazyRoute.d.ts +19 -3
- package/dist/light.css +25 -0
- package/dist/stackGap.d.ts +13 -1
- package/dist/tokens.css +57 -0
- package/package.json +20 -4
- package/dist/Avatar-DHNPaIjZ.js +0 -64
- package/dist/Block-CVKukh1e.js +0 -28
- package/dist/Block.css +0 -1
- package/dist/Column-DlNrvFq1.js +0 -26
- package/dist/Column.css +0 -1
- package/dist/Menu-DS7zKQbY.js +0 -40
- package/dist/Row-CnD7Q7pD.js +0 -20
- package/dist/Row.css +0 -1
- package/dist/TabSections-oeEBx0nM.js +0 -50
- package/dist/stackGap-CDsjER0g.js +0 -7
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import e from "react";
|
|
2
|
+
import { c as r } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './RadioGroup.css';const F = "cqr", L = "cqi", M = "cqs", w = "cqp", B = "cqh", I = "cqn", j = "cqm", A = "cqmf", G = "cqc", S = "cqt", T = "cqe", U = "cqd", W = "cqx", $ = "cqy", C = "cqz", H = "cqk", J = "cqkr", K = "cqkc", O = "cqkv", c = {
|
|
4
|
+
r: F,
|
|
5
|
+
i: L,
|
|
6
|
+
s: M,
|
|
7
|
+
p: w,
|
|
8
|
+
h: B,
|
|
9
|
+
n: I,
|
|
10
|
+
m: j,
|
|
11
|
+
mf: A,
|
|
12
|
+
c: G,
|
|
13
|
+
t: S,
|
|
14
|
+
e: T,
|
|
15
|
+
d: U,
|
|
16
|
+
x: W,
|
|
17
|
+
y: $,
|
|
18
|
+
z: C,
|
|
19
|
+
k: H,
|
|
20
|
+
kr: J,
|
|
21
|
+
kc: K,
|
|
22
|
+
kv: O
|
|
23
|
+
};
|
|
24
|
+
function P() {
|
|
25
|
+
return /* @__PURE__ */ e.createElement("svg", { className: c.kv, viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
|
|
26
|
+
"path",
|
|
27
|
+
{
|
|
28
|
+
d: "M2.5 6.2 4.8 8.5 9.5 3.5",
|
|
29
|
+
fill: "none",
|
|
30
|
+
stroke: "currentColor",
|
|
31
|
+
strokeWidth: "1.8",
|
|
32
|
+
strokeLinecap: "round",
|
|
33
|
+
strokeLinejoin: "round"
|
|
34
|
+
}
|
|
35
|
+
));
|
|
36
|
+
}
|
|
37
|
+
function E({
|
|
38
|
+
kind: t,
|
|
39
|
+
selected: l
|
|
40
|
+
}) {
|
|
41
|
+
const a = t === "radio";
|
|
42
|
+
return /* @__PURE__ */ e.createElement(
|
|
43
|
+
"span",
|
|
44
|
+
{
|
|
45
|
+
className: r(c.k, a ? c.kr : c.kc),
|
|
46
|
+
"data-on": l ? "true" : void 0,
|
|
47
|
+
"aria-hidden": !0
|
|
48
|
+
},
|
|
49
|
+
!a && l ? /* @__PURE__ */ e.createElement(P, null) : null
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
function i({
|
|
53
|
+
title: t,
|
|
54
|
+
meta: l,
|
|
55
|
+
price: a,
|
|
56
|
+
badge: s
|
|
57
|
+
}) {
|
|
58
|
+
return t == null && l == null && a == null && s == null ? null : /* @__PURE__ */ e.createElement("div", { className: c.t }, a != null ? /* @__PURE__ */ e.createElement("span", { className: c.z }, a) : null, t ?? null, l != null ? /* @__PURE__ */ e.createElement("span", { className: c.e }, l) : null, s ?? null);
|
|
59
|
+
}
|
|
60
|
+
function f({ children: t }) {
|
|
61
|
+
return /* @__PURE__ */ e.createElement(
|
|
62
|
+
"div",
|
|
63
|
+
{
|
|
64
|
+
className: c.x,
|
|
65
|
+
onClick: (l) => l.stopPropagation(),
|
|
66
|
+
onMouseDown: (l) => l.preventDefault()
|
|
67
|
+
},
|
|
68
|
+
t
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function Y({
|
|
72
|
+
items: t,
|
|
73
|
+
value: l,
|
|
74
|
+
defaultValue: a,
|
|
75
|
+
name: s,
|
|
76
|
+
disabled: m = !1,
|
|
77
|
+
indicator: p = "check",
|
|
78
|
+
indicatorSide: q = "end",
|
|
79
|
+
onValueChange: u,
|
|
80
|
+
className: N,
|
|
81
|
+
...h
|
|
82
|
+
}) {
|
|
83
|
+
const b = e.useId(), x = s ?? b, [y, g] = e.useState(a), k = l !== void 0, z = k ? l : y, R = (n) => {
|
|
84
|
+
k || g(n), u == null || u(n);
|
|
85
|
+
};
|
|
86
|
+
return /* @__PURE__ */ e.createElement(
|
|
87
|
+
"div",
|
|
88
|
+
{
|
|
89
|
+
className: r(c.r, N),
|
|
90
|
+
role: "radiogroup",
|
|
91
|
+
"aria-disabled": m || void 0,
|
|
92
|
+
...h
|
|
93
|
+
},
|
|
94
|
+
t.map((n) => {
|
|
95
|
+
const o = z === n.value, D = m || !!n.disabled, d = n.variant === "split", v = n.iconFramed !== !1;
|
|
96
|
+
return /* @__PURE__ */ e.createElement(
|
|
97
|
+
"label",
|
|
98
|
+
{
|
|
99
|
+
key: n.value,
|
|
100
|
+
className: r(c.i, o && c.s, d && c.p),
|
|
101
|
+
"data-selected": o ? "true" : void 0,
|
|
102
|
+
"data-indicator": d ? void 0 : q
|
|
103
|
+
},
|
|
104
|
+
/* @__PURE__ */ e.createElement(
|
|
105
|
+
"input",
|
|
106
|
+
{
|
|
107
|
+
className: c.n,
|
|
108
|
+
type: "radio",
|
|
109
|
+
name: x,
|
|
110
|
+
value: n.value,
|
|
111
|
+
checked: o,
|
|
112
|
+
disabled: D,
|
|
113
|
+
onChange: () => R(n.value)
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
d ? /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement("div", { className: c.h }, n.icon != null ? /* @__PURE__ */ e.createElement("div", { className: r(c.m, v && c.mf) }, n.icon) : null, /* @__PURE__ */ e.createElement(i, { title: n.title }), /* @__PURE__ */ e.createElement(E, { kind: p, selected: o })), /* @__PURE__ */ e.createElement("div", { className: c.y }, /* @__PURE__ */ e.createElement(
|
|
117
|
+
i,
|
|
118
|
+
{
|
|
119
|
+
price: n.price,
|
|
120
|
+
meta: n.meta,
|
|
121
|
+
badge: n.badge
|
|
122
|
+
}
|
|
123
|
+
), n.description != null ? /* @__PURE__ */ e.createElement("p", { className: c.d }, n.description) : null, n.actions != null ? /* @__PURE__ */ e.createElement(f, null, n.actions) : null)) : /* @__PURE__ */ e.createElement(e.Fragment, null, n.icon != null ? /* @__PURE__ */ e.createElement("div", { className: r(c.m, v && c.mf) }, n.icon) : null, /* @__PURE__ */ e.createElement("div", { className: c.c }, /* @__PURE__ */ e.createElement(
|
|
124
|
+
i,
|
|
125
|
+
{
|
|
126
|
+
title: n.title,
|
|
127
|
+
meta: n.meta,
|
|
128
|
+
price: n.price,
|
|
129
|
+
badge: n.badge
|
|
130
|
+
}
|
|
131
|
+
), n.description != null ? /* @__PURE__ */ e.createElement("p", { className: c.d }, n.description) : null, n.actions != null ? /* @__PURE__ */ e.createElement(f, null, n.actions) : null), /* @__PURE__ */ e.createElement(E, { kind: p, selected: o }))
|
|
132
|
+
);
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
Y as R
|
|
138
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer elcrm-components{.cqr{display:flex;flex-direction:column;gap:var(--shell-gap)}.cqi{display:flex;align-items:flex-start;gap:var(--shell-gap);width:100%;margin:0;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);cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none}.cqi:hover:not(:disabled){background:var(--shell-background-hover)}.cqi:disabled{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cqs,.cqi[data-selected=true]{border-color:var(--shell-color-accent);background:var(--shell-background-selected)}.cqp{flex-direction:column;align-items:stretch;padding:0;gap:0}.cqp.cqs .cqh,.cqp[data-selected=true] .cqh{border-bottom-color:var(--shell-color-accent)}.cqn{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.cqm{display:flex;flex-shrink:0;align-items:center;justify-content:center;overflow:hidden}.cqmf{width:var(--shell-icon-size);height:var(--shell-icon-size);padding:var(--shell-gap-sm);box-sizing:content-box;color:var(--shell-color-muted);background:var(--shell-background-muted);border:1px solid var(--shell-border);border-radius:var(--shell-radius)}.cqc{display:flex;flex:1;flex-direction:column;gap:var(--shell-gap-sm);min-width:0}.cqt{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--shell-gap-sm);font-size:var(--shell-font-size);font-weight:var(--shell-font-weight);color:var(--shell-color);line-height:1.3}.cqe{font-size:var(--shell-font-size-sm);font-weight:400;color:var(--shell-color-muted)}.cqd{margin:0;font-size:var(--shell-font-size-sm);color:var(--shell-color-muted);line-height:1.45}.cqx{display:flex;flex-wrap:wrap;gap:var(--shell-gap-sm);margin-top:var(--shell-gap-sm)}.cqh{display:flex;align-items:center;gap:var(--shell-gap);padding:var(--shell-padding);border-bottom:1px solid var(--shell-border)}.cqh .cqt{flex:1;min-width:0}.cqy{display:flex;flex-direction:column;gap:var(--shell-gap-sm);padding:var(--shell-padding)}.cqz{font-size:var(--shell-font-size-lg);font-weight:700;color:var(--shell-color);line-height:1.2}.cqk{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;width:var(--shell-check-size);height:var(--shell-check-size);margin-left:auto;box-sizing:border-box;border:1px solid var(--shell-border);background:var(--shell-background);color:#fff}.cqi[data-indicator=start]>.cqk{margin-left:0;order:-1}.cqkr{border-radius:50%}.cqkr[data-on=true]{border-color:var(--shell-color-accent);background:var(--shell-color-accent);box-shadow:inset 0 0 0 3px var(--shell-background-selected)}.cqkc{border-radius:calc(var(--shell-radius) * .4)}.cqkc[data-on=true]{border-color:var(--shell-color-accent);background:var(--shell-color-accent)}.cqkv{width:60%;height:60%;display:block}}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Вид индикатора выбора: квадрат с галочкой или круг. */
|
|
3
|
+
export type RadioIndicator = "check" | "radio";
|
|
4
|
+
/** Сторона индикатора в row-варианте. */
|
|
5
|
+
export type RadioIndicatorSide = "start" | "end";
|
|
6
|
+
/**
|
|
7
|
+
* Опция группы выбора {@link RadioGroup}.
|
|
8
|
+
*
|
|
9
|
+
* Идентификатор — `value` (не `key`).
|
|
10
|
+
* Опциональные поля (`icon`, `title`, `description`, …): есть → рисуем, нет → пропускаем.
|
|
11
|
+
* Внешний вид (индикатор, split) — props item/группы, не отдельные compound-слоты.
|
|
12
|
+
*/
|
|
13
|
+
export type RadioGroupItem = {
|
|
14
|
+
/** Значение опции (то, что уходит в value / onValueChange). Не `key`. */
|
|
15
|
+
value: string;
|
|
16
|
+
/** Заголовок опции. */
|
|
17
|
+
title?: React.ReactNode;
|
|
18
|
+
/** Описание под заголовком. */
|
|
19
|
+
description?: React.ReactNode;
|
|
20
|
+
/** Иконка / логотип / Avatar — рисуется только если передано. */
|
|
21
|
+
icon?: React.ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Рамка вокруг icon (квадрат под иконку/лого).
|
|
24
|
+
* Для Avatar — `false`. По умолчанию `true`.
|
|
25
|
+
*/
|
|
26
|
+
iconFramed?: boolean;
|
|
27
|
+
/** Вторичный текст: @handle, $10/month, per month. */
|
|
28
|
+
meta?: React.ReactNode;
|
|
29
|
+
/** Крупная цена (часто с `variant="split"`). */
|
|
30
|
+
price?: React.ReactNode;
|
|
31
|
+
/** Метка (Badge и т.п.). */
|
|
32
|
+
badge?: React.ReactNode;
|
|
33
|
+
/** Edit / Set as default — клик не меняет выбор. */
|
|
34
|
+
actions?: React.ReactNode;
|
|
35
|
+
/** Недоступная опция. */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* `row` — одна строка (планы, люди, оплата).
|
|
39
|
+
* `split` — шапка (icon + title) / тело (price + meta + badge + description).
|
|
40
|
+
*/
|
|
41
|
+
variant?: "row" | "split";
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Props компонента {@link RadioGroup}.
|
|
45
|
+
*
|
|
46
|
+
* Групповой UI: `items` + `value` / `onValueChange`, без `RadioGroup.Item`.
|
|
47
|
+
*/
|
|
48
|
+
export type RadioGroupProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> & {
|
|
49
|
+
/** Декларативный список опций. */
|
|
50
|
+
items: RadioGroupItem[];
|
|
51
|
+
/** Выбранное значение (controlled). */
|
|
52
|
+
value?: string;
|
|
53
|
+
/** Начальное значение (uncontrolled). */
|
|
54
|
+
defaultValue?: string;
|
|
55
|
+
/** `name` для скрытых `input[type=radio]`. */
|
|
56
|
+
name?: string;
|
|
57
|
+
/** Заблокировать всю группу. */
|
|
58
|
+
disabled?: boolean;
|
|
59
|
+
/** `check` — квадрат с галочкой; `radio` — круг. */
|
|
60
|
+
indicator?: RadioIndicator;
|
|
61
|
+
/** Сторона индикатора в row-варианте. */
|
|
62
|
+
indicatorSide?: RadioIndicatorSide;
|
|
63
|
+
/** Смена выбора (`next` = `item.value`). */
|
|
64
|
+
onValueChange?: (value: string) => void;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Группа выбора одной опции из декларативного массива `items`.
|
|
68
|
+
*
|
|
69
|
+
* Когда использовать:
|
|
70
|
+
* - выбор тарифа, способа оплаты, человека, варианта настроек карточкой;
|
|
71
|
+
* - нужен check/radio-индикатор и опциональные icon / meta / actions.
|
|
72
|
+
*
|
|
73
|
+
* Связанные компоненты:
|
|
74
|
+
* - {@link PricingGroup} — тарифные карточки без radio-индикатора;
|
|
75
|
+
* - {@link AvatarName} — часто как `icon` / контент опции «человек».
|
|
76
|
+
*
|
|
77
|
+
* API: только `items` + value/onValueChange. Compound-слоты (`RadioGroup.Item`) не используются.
|
|
78
|
+
* Идентификатор опции — `value` (не `key`).
|
|
79
|
+
*
|
|
80
|
+
* CSS-токены: `--shell-*`.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* const items = [
|
|
85
|
+
* {
|
|
86
|
+
* value: "basic",
|
|
87
|
+
* icon: <Icons.Line name="zap" />,
|
|
88
|
+
* title: "Basic",
|
|
89
|
+
* description: "Для старта",
|
|
90
|
+
* meta: "$9/мес",
|
|
91
|
+
* },
|
|
92
|
+
* {
|
|
93
|
+
* value: "pro",
|
|
94
|
+
* title: "Pro",
|
|
95
|
+
* description: "Для команд",
|
|
96
|
+
* price: "$29",
|
|
97
|
+
* badge: <Badge>Popular</Badge>,
|
|
98
|
+
* variant: "split",
|
|
99
|
+
* },
|
|
100
|
+
* ];
|
|
101
|
+
*
|
|
102
|
+
* <RadioGroup
|
|
103
|
+
* items={items}
|
|
104
|
+
* value={plan}
|
|
105
|
+
* onValueChange={setPlan}
|
|
106
|
+
* indicator="check"
|
|
107
|
+
* indicatorSide="end"
|
|
108
|
+
* />
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export declare function RadioGroup({ items, value: valueProp, defaultValue, name: nameProp, disabled, indicator, indicatorSide, onValueChange, className, ...rest }: RadioGroupProps): React.JSX.Element;
|
package/dist/Row.d.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { StackProps } from './Stack';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Используйте `Stack direction="row"`.
|
|
5
|
+
* Миграция: `elcrm migrate components`.
|
|
6
|
+
*
|
|
7
|
+
* Горизонтальный стек (алиас Stack).
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* // Было:
|
|
12
|
+
* <Row gap={8}>…</Row>
|
|
13
|
+
* // Стало:
|
|
14
|
+
* <Stack direction="row" gap={8}>…</Stack>
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export type RowProps = Omit<StackProps, "direction">;
|
|
18
|
+
/**
|
|
19
|
+
* @deprecated Используйте `Stack direction="row"`.
|
|
20
|
+
* Миграция: `elcrm migrate components`.
|
|
21
|
+
*
|
|
22
|
+
* @see Stack, Block, Column
|
|
23
|
+
*/
|
|
24
|
+
export declare function Row(props: RowProps): React.JSX.Element;
|
package/dist/Row.js
CHANGED
package/dist/Section.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer elcrm-components{.cep{display:flex;flex-direction:column;gap:var(--
|
|
1
|
+
@layer elcrm-components{.cep{display:flex;flex-direction:column;gap:var(--shell-gap);padding:var(--section-padding);flex:1;min-width:0}.ceu{box-sizing:border-box;min-height:100svh;display:flex;align-items:center;justify-content:center;padding:var(--section-padding)}.cek{box-sizing:border-box;width:100%;max-width:var(--section-auth-card-max-width);padding:var(--shell-gap-lg);background:var(--shell-background);border:1px solid var(--shell-border);border-radius:var(--shell-radius);box-shadow:var(--shell-shadow);display:flex;flex-direction:column;gap:var(--shell-gap)}.ceh{display:flex;flex-direction:column;gap:var(--shell-gap-sm)}.cec{text-align:center;align-items:center}.cei{display:flex;margin-bottom:var(--shell-gap-sm)}.cec .cei{justify-content:center}.cet{margin:0;font-size:var(--section-title-size);font-weight:var(--shell-font-weight);letter-spacing:-.02em;color:var(--shell-color);line-height:1.25}.ced{font-size:var(--section-title-size-md)}.cee{margin:0;font-size:var(--shell-font-size);color:var(--shell-color-muted);line-height:1.45}.ceb{display:flex;flex-direction:column;gap:var(--shell-gap);width:100%;text-align:left}.ces{display:flex;flex-direction:column;gap:var(--shell-gap-sm);width:100%}.cel{display:flex;flex-direction:column;gap:var(--shell-gap);margin-bottom:var(--shell-gap-lg);padding-bottom:var(--shell-gap-lg);border-bottom:1px solid var(--shell-border)}.cel:last-of-type{margin-bottom:0;padding-bottom:0;border-bottom:none}.cel .cet{font-size:var(--shell-font-size);font-weight:var(--shell-font-weight)}}
|
package/dist/Section.d.ts
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Единая секция страницы: заголовок, описание, иконка, контент, действия.
|
|
4
|
+
*
|
|
5
|
+
* Роли: Section = блок модуля / панель / auth-карточка;
|
|
6
|
+
* для строки «title + actions» на странице — PageHead;
|
|
7
|
+
* каркас со скроллом — PageShell.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <Section title="Настройки" description="Профиль и безопасность" variant="page">
|
|
12
|
+
* <Stack gap={16}>…</Stack>
|
|
13
|
+
* </Section>
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Токены: `--shell-*`, узкие `--section-padding`, `--section-title-size`, `--section-title-size-md`, `--section-auth-card-max-width`.
|
|
17
|
+
*/
|
|
2
18
|
export type SectionProps = {
|
|
3
|
-
/** Заголовок */
|
|
19
|
+
/** Заголовок секции */
|
|
4
20
|
title?: React.ReactNode;
|
|
5
21
|
/** Подзаголовок / описание */
|
|
6
22
|
description?: React.ReactNode;
|
|
7
23
|
/** Иконка / логотип над или рядом с заголовком */
|
|
8
24
|
icon?: React.ReactNode;
|
|
25
|
+
/** Основной контент секции */
|
|
9
26
|
children?: React.ReactNode;
|
|
10
27
|
/** Блок действий (кнопки под контентом или в шапке) */
|
|
11
28
|
actions?: React.ReactNode;
|
|
@@ -15,15 +32,18 @@ export type SectionProps = {
|
|
|
15
32
|
* `auth` — центрированная карточка входа.
|
|
16
33
|
*/
|
|
17
34
|
variant?: "page" | "block" | "auth";
|
|
18
|
-
/** Выравнивание шапки */
|
|
35
|
+
/** Выравнивание шапки (`start` по умолчанию, для `auth` — `center`) */
|
|
19
36
|
align?: "start" | "center";
|
|
20
37
|
/** Размер заголовка */
|
|
21
38
|
titleSize?: "md" | "lg";
|
|
39
|
+
/** Доп. CSS-класс */
|
|
22
40
|
className?: string;
|
|
41
|
+
/** HTML-тег корня (`main` / `section` / `div`) */
|
|
23
42
|
as?: "section" | "main" | "div";
|
|
24
43
|
};
|
|
25
44
|
/**
|
|
26
45
|
* Единая секция страницы: title + description + icon + children.
|
|
27
|
-
*
|
|
46
|
+
*
|
|
47
|
+
* @see PageHead, PageShell, Stack, EmptyState
|
|
28
48
|
*/
|
|
29
49
|
export declare function Section({ title, description, icon, children, actions, variant, align, titleSize, className, as, }: SectionProps): React.JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import m from "react";
|
|
2
|
+
import { c as u } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './Stack.css';function a(t) {
|
|
4
|
+
if (t != null)
|
|
5
|
+
return { gap: typeof t == "number" ? `${t}px` : t };
|
|
6
|
+
}
|
|
7
|
+
const i = "c1r", l = "c1h", r = {
|
|
8
|
+
r: i,
|
|
9
|
+
h: l
|
|
10
|
+
};
|
|
11
|
+
function p({
|
|
12
|
+
direction: t = "column",
|
|
13
|
+
gap: c,
|
|
14
|
+
children: e,
|
|
15
|
+
className: n,
|
|
16
|
+
style: o,
|
|
17
|
+
...s
|
|
18
|
+
}) {
|
|
19
|
+
return /* @__PURE__ */ m.createElement(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
className: u(r.r, t === "row" && r.h, n),
|
|
23
|
+
style: { ...a(c), ...o },
|
|
24
|
+
...s
|
|
25
|
+
},
|
|
26
|
+
e
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
p as S
|
|
31
|
+
};
|
package/dist/Stack.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer elcrm-components{.c1r{display:flex;flex-direction:column;gap:var(--shell-gap)}.c1h{flex-direction:row;flex-wrap:wrap;align-items:center}}
|
package/dist/Stack.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/** Направление раскладки стека */
|
|
3
|
+
export type StackDirection = "row" | "column";
|
|
4
|
+
/**
|
|
5
|
+
* Универсальный стек (ряд / колонка) с отступами между детьми.
|
|
6
|
+
*
|
|
7
|
+
* Роли: Stack = базовая раскладка; заменяет устаревшие Block / Row / Column.
|
|
8
|
+
* Внутри Section, Card, форм и списков действий.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Stack direction="row" gap={12}>
|
|
13
|
+
* <Button>Отмена</Button>
|
|
14
|
+
* <Button>Сохранить</Button>
|
|
15
|
+
* </Stack>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Токены: `--shell-gap` (если `gap` не задан); при `gap` — inline-стиль.
|
|
19
|
+
*/
|
|
20
|
+
export type StackProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
21
|
+
/** Направление: по умолчанию `column` */
|
|
22
|
+
direction?: StackDirection;
|
|
23
|
+
/** Отступ между детьми: число (px) или CSS-строка */
|
|
24
|
+
gap?: number | string;
|
|
25
|
+
/** Дочерние элементы стека */
|
|
26
|
+
children?: React.ReactNode;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Универсальный стек (ряд / колонка).
|
|
30
|
+
* Вместо устаревших Block / Row / Column.
|
|
31
|
+
*
|
|
32
|
+
* @see Block, Row, Column
|
|
33
|
+
*/
|
|
34
|
+
export declare function Stack({ direction, gap, children, className, style, ...rest }: StackProps): React.JSX.Element;
|
package/dist/Stack.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import t, { useMemo as S, useState as y, Suspense as T } from "react";
|
|
2
|
+
import { c as b } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './TabSections.css';const x = "ctr", I = "ctb", M = "ctn", R = "ctt", $ = "cta", g = "cte", j = "cts", q = "ctm", a = {
|
|
4
|
+
r: x,
|
|
5
|
+
b: I,
|
|
6
|
+
n: M,
|
|
7
|
+
t: R,
|
|
8
|
+
a: $,
|
|
9
|
+
e: g,
|
|
10
|
+
s: j,
|
|
11
|
+
m: q
|
|
12
|
+
};
|
|
13
|
+
function A({
|
|
14
|
+
items: l,
|
|
15
|
+
resolveSection: d,
|
|
16
|
+
value: o,
|
|
17
|
+
defaultValue: n,
|
|
18
|
+
onValueChange: s,
|
|
19
|
+
loading: v = null,
|
|
20
|
+
actions: m,
|
|
21
|
+
className: i,
|
|
22
|
+
navLabel: E = "Вкладки"
|
|
23
|
+
}) {
|
|
24
|
+
const p = S(() => {
|
|
25
|
+
var e;
|
|
26
|
+
return n || (((e = l.find((c) => !c.disabled)) == null ? void 0 : e.value) ?? "");
|
|
27
|
+
}, [n, l]), [N, f] = y(p), r = o ?? N, k = (e) => {
|
|
28
|
+
o == null && f(e), s == null || s(e);
|
|
29
|
+
}, u = r ? d(r) : null;
|
|
30
|
+
return /* @__PURE__ */ t.createElement("div", { className: b(a.r, i) }, /* @__PURE__ */ t.createElement("div", { className: a.b }, /* @__PURE__ */ t.createElement("nav", { className: a.n, "aria-label": E }, l.map((e) => {
|
|
31
|
+
const c = e.value === r;
|
|
32
|
+
return /* @__PURE__ */ t.createElement(
|
|
33
|
+
"button",
|
|
34
|
+
{
|
|
35
|
+
key: e.value,
|
|
36
|
+
type: "button",
|
|
37
|
+
className: b(a.t, c && a.a),
|
|
38
|
+
"data-active": c ? "true" : void 0,
|
|
39
|
+
"aria-current": c ? "page" : void 0,
|
|
40
|
+
disabled: e.disabled,
|
|
41
|
+
onClick: () => k(e.value)
|
|
42
|
+
},
|
|
43
|
+
/* @__PURE__ */ t.createElement("span", null, e.label),
|
|
44
|
+
e.badge != null && e.badge !== "" ? /* @__PURE__ */ t.createElement("em", { className: a.e }, e.badge) : null
|
|
45
|
+
);
|
|
46
|
+
})), m != null ? /* @__PURE__ */ t.createElement("div", { className: a.s }, m) : null), /* @__PURE__ */ t.createElement("div", { className: a.m }, u ? /* @__PURE__ */ t.createElement(T, { fallback: v }, t.createElement(u)) : null));
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
A as T
|
|
50
|
+
};
|
package/dist/TabSections.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer elcrm-components{.ctr{display:flex;flex-direction:column;gap:var(--
|
|
1
|
+
@layer elcrm-components{.ctr{display:flex;flex-direction:column;gap:var(--shell-gap);width:100%;min-width:0}.ctb{display:flex;align-items:flex-end;gap:var(--shell-gap);border-bottom:1px solid var(--shell-border);min-width:0}.ctn{display:flex;align-items:stretch;gap:var(--shell-gap-lg);flex:1 1 auto;min-width:0;overflow-x:auto;scrollbar-width:thin}.ctt{-webkit-appearance:none;-moz-appearance:none;appearance:none;display:inline-flex;align-items:center;gap:var(--shell-gap-sm);flex:0 0 auto;margin:0 0 -1px;padding:var(--shell-padding-block) .15rem;border:none;border-bottom:2px solid transparent;background:transparent;color:var(--shell-color-muted);font:inherit;font-size:var(--shell-font-size);font-weight:500;white-space:nowrap;cursor:pointer}.ctt:hover:not(:disabled){color:var(--shell-color)}.cta{color:var(--shell-color);border-bottom-color:var(--shell-color-accent)}.ctt:disabled{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cte{display:inline-flex;align-items:center;justify-content:center;min-width:1.15em;height:1.15em;padding:0 .35em;border-radius:999px;background:var(--shell-color-danger);color:#fff;font-size:11px;font-style:normal;font-weight:700;line-height:1}.cts{flex:0 0 auto;display:flex;align-items:center;padding-bottom:var(--shell-gap-sm)}.ctm{min-width:0;width:100%}}
|
package/dist/TabSections.d.ts
CHANGED
|
@@ -1,31 +1,87 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { LazyRouteChunk } from './lazyRoute';
|
|
3
|
+
/**
|
|
4
|
+
* Пункт горизонтальных вкладок {@link TabSections}.
|
|
5
|
+
*
|
|
6
|
+
* Идентификатор — `value` (часто имя файла секции). Не путать с React `key`.
|
|
7
|
+
*/
|
|
3
8
|
export type TabSectionsItem = {
|
|
4
|
-
key
|
|
9
|
+
/** Уникальный идентификатор вкладки (не `key`). */
|
|
10
|
+
value: string;
|
|
11
|
+
/** Подпись вкладки. */
|
|
5
12
|
label: React.ReactNode;
|
|
6
|
-
/** Бейдж справа от подписи (число / текст) */
|
|
13
|
+
/** Бейдж справа от подписи (число / текст). Нет — не рисуем. */
|
|
7
14
|
badge?: React.ReactNode;
|
|
15
|
+
/** Недоступная вкладка. */
|
|
8
16
|
disabled?: boolean;
|
|
9
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Props компонента {@link TabSections}.
|
|
20
|
+
*
|
|
21
|
+
* Групповой UI: декларативный массив `items` + `value` / `onValueChange`.
|
|
22
|
+
*/
|
|
10
23
|
export type TabSectionsProps = {
|
|
11
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Горизонтальные вкладки.
|
|
26
|
+
* `value` пункта обычно = имя файла секции для `resolveSection`.
|
|
27
|
+
*/
|
|
12
28
|
items: TabSectionsItem[];
|
|
13
29
|
/**
|
|
14
|
-
*
|
|
30
|
+
* Резолвер панели по `value`.
|
|
31
|
+
* Обычно: `createLazyResolver((v) => import(\`./sections/${v}.tsx\`))`.
|
|
15
32
|
*/
|
|
16
|
-
resolveSection: (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
resolveSection: (value: string) => LazyRouteChunk | React.ComponentType;
|
|
34
|
+
/** Контролируемый активный `value`. */
|
|
35
|
+
value?: string;
|
|
36
|
+
/** Начальный `value` (неконтролируемый режим). */
|
|
37
|
+
defaultValue?: string;
|
|
38
|
+
/** Смена вкладки (`next` = `item.value`). */
|
|
39
|
+
onValueChange?: (value: string) => void;
|
|
40
|
+
/** Fallback для `<Suspense>`. */
|
|
20
41
|
loading?: React.ReactNode;
|
|
21
|
-
/** Справа в ряду вкладок (фильтр, сортировка…) */
|
|
42
|
+
/** Справа в ряду вкладок (фильтр, сортировка…). */
|
|
22
43
|
actions?: React.ReactNode;
|
|
44
|
+
/** CSS-класс корня. */
|
|
23
45
|
className?: string;
|
|
46
|
+
/** `aria-label` для `<nav>` вкладок. */
|
|
24
47
|
navLabel?: string;
|
|
25
48
|
};
|
|
26
49
|
/**
|
|
27
|
-
* Как NavSections, но вкладки
|
|
28
|
-
*
|
|
29
|
-
*
|
|
50
|
+
* Как {@link NavSections}, но вкладки горизонтально; без собственного заголовка —
|
|
51
|
+
* обычно оборачивают в `Section` / `PageHead`.
|
|
52
|
+
*
|
|
53
|
+
* Когда использовать:
|
|
54
|
+
* - несколько панелей на одной странице с горизонтальным переключением;
|
|
55
|
+
* - контент грузится lazy по `value`.
|
|
56
|
+
*
|
|
57
|
+
* Связанные компоненты:
|
|
58
|
+
* - {@link NavSections} — сайдбар + секции;
|
|
59
|
+
* - {@link createLazyResolver} — кэш lazy-импортов;
|
|
60
|
+
* - `Section` — обёртка с заголовком страницы.
|
|
61
|
+
*
|
|
62
|
+
* API: декларативные `items`. Идентификатор — `value` (не `key`).
|
|
63
|
+
* Compound-слоты не используются.
|
|
64
|
+
*
|
|
65
|
+
* CSS-токены: `--shell-*`.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* const items = [
|
|
70
|
+
* { value: "members", label: "Участники", badge: 12 },
|
|
71
|
+
* { value: "invites", label: "Приглашения" },
|
|
72
|
+
* ];
|
|
73
|
+
*
|
|
74
|
+
* const resolveSection = createLazyResolver(
|
|
75
|
+
* (v) => import(`./sections/${v}.tsx`),
|
|
76
|
+
* );
|
|
77
|
+
*
|
|
78
|
+
* <TabSections
|
|
79
|
+
* items={items}
|
|
80
|
+
* resolveSection={resolveSection}
|
|
81
|
+
* value={tab}
|
|
82
|
+
* onValueChange={setTab}
|
|
83
|
+
* actions={<Button size="s">Фильтр</Button>}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
30
86
|
*/
|
|
31
|
-
export declare function TabSections({ items, resolveSection,
|
|
87
|
+
export declare function TabSections({ items, resolveSection, value: valueProp, defaultValue, onValueChange, loading, actions, className, navLabel, }: TabSectionsProps): React.JSX.Element;
|
package/dist/TabSections.js
CHANGED
package/dist/cx.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Склейка CSS className без лишних пробелов и falsy-значений.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* cx(s.r, active && s.a, className) // → "cr c a my"
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
2
9
|
export declare function cx(...parts: Array<string | false | null | undefined>): string;
|
package/dist/dark.css
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Цвета shell (@elcrm/components) для тёмной темы — `elcrm css`.
|
|
3
|
+
*/
|
|
4
|
+
html[data-theme="dark"],
|
|
5
|
+
body[data-theme="dark"] {
|
|
6
|
+
--shell-color: var(--text);
|
|
7
|
+
--shell-color-muted: var(--muted);
|
|
8
|
+
--shell-color-accent: var(--accent);
|
|
9
|
+
--shell-color-danger: var(--danger);
|
|
10
|
+
--shell-color-success: var(--success);
|
|
11
|
+
--shell-color-warning: var(--warning);
|
|
12
|
+
--shell-border: var(--line);
|
|
13
|
+
--shell-background: var(--panel);
|
|
14
|
+
--shell-background-muted: var(--bg);
|
|
15
|
+
--shell-background-hover: color-mix(in srgb, var(--text) 8%, transparent);
|
|
16
|
+
--shell-background-selected: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
17
|
+
--shell-shadow: var(--shadow);
|
|
18
|
+
|
|
19
|
+
--avatar-background: var(--shell-color-accent);
|
|
20
|
+
--avatar-color: var(--button-primary-fg);
|
|
21
|
+
--avatar-border: 1px solid var(--shell-border);
|
|
22
|
+
--avatar-outline-active: 2px solid
|
|
23
|
+
color-mix(in srgb, var(--accent) 70%, white);
|
|
24
|
+
}
|