@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.
- package/README.md +450 -202
- package/dist/Avatar-Jzan87XD.js +101 -0
- package/dist/Avatar.css +1 -1
- package/dist/Avatar.d.ts +81 -2
- 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 +26 -2
- package/dist/Block-BAXh0PPp.js +8 -0
- package/dist/Block.d.ts +30 -2
- package/dist/Block.js +1 -1
- package/dist/Brand.css +1 -1
- package/dist/Brand.d.ts +37 -2
- 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 +28 -2
- package/dist/Column-DDaqX-A7.js +8 -0
- package/dist/Column.d.ts +24 -2
- 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 +36 -2
- package/dist/Footer.css +1 -1
- package/dist/Footer.d.ts +25 -2
- package/dist/Header.css +1 -1
- package/dist/Header.d.ts +36 -2
- package/dist/Item.css +1 -1
- package/dist/Item.d.ts +42 -2
- package/dist/Layout.css +1 -1
- package/dist/Layout.d.ts +30 -2
- package/dist/List.css +1 -1
- package/dist/List.d.ts +29 -2
- package/dist/Loading.css +1 -1
- package/dist/Loading.d.ts +24 -2
- package/dist/Menu-DRBHvSdJ.js +40 -0
- package/dist/Menu.css +1 -1
- package/dist/Menu.d.ts +78 -2
- 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 +101 -2
- 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 +24 -2
- package/dist/Row.js +1 -1
- package/dist/Section.css +1 -1
- package/dist/Section.d.ts +49 -2
- 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 +87 -2
- 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 +33 -6
- 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,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;
|
package/dist/Card.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer elcrm-components{.cdr{background:var(--
|
|
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,2 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
+
*/
|
|
17
|
+
export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
18
|
+
/** Заголовок карточки (если нет — только children) */
|
|
19
|
+
title?: React.ReactNode;
|
|
20
|
+
/** Содержимое карточки */
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Карточка-контейнер.
|
|
25
|
+
*
|
|
26
|
+
* @see Section, Stack, List
|
|
27
|
+
*/
|
|
28
|
+
export declare function Card({ title, children, className, ...rest }: CardProps): React.JSX.Element;
|
package/dist/Column.d.ts
CHANGED
|
@@ -1,2 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { default as React } from 'react';
|
|
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
|
@@ -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;
|
package/dist/Dropdown.js
ADDED
package/dist/EmptyState.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer elcrm-components{.cpr{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--
|
|
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)}}
|
package/dist/EmptyState.d.ts
CHANGED
|
@@ -1,2 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
+
*/
|
|
19
|
+
export type EmptyStateProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
20
|
+
/** Заголовок заглушки */
|
|
21
|
+
title?: React.ReactNode;
|
|
22
|
+
/** Поясняющий текст */
|
|
23
|
+
description?: React.ReactNode;
|
|
24
|
+
/** Иконка над заголовком */
|
|
25
|
+
icon?: React.ReactNode;
|
|
26
|
+
/** Кнопки / CTA под описанием */
|
|
27
|
+
actions?: React.ReactNode;
|
|
28
|
+
/** Доп. контент между описанием и actions */
|
|
29
|
+
children?: React.ReactNode;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Заглушка «нет данных».
|
|
33
|
+
*
|
|
34
|
+
* @see Loading, Section, List
|
|
35
|
+
*/
|
|
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(--
|
|
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,2 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
+
*/
|
|
16
|
+
export type FooterProps = React.HTMLAttributes<HTMLElement> & {
|
|
17
|
+
/** Содержимое подвала (копирайт, ссылки, статус) */
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Подвал приложения / страницы.
|
|
22
|
+
*
|
|
23
|
+
* @see Layout, PageShell, Header
|
|
24
|
+
*/
|
|
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(--
|
|
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}}
|