@elcrm/components 0.2.9 → 0.2.13
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 +37 -4
- package/dist/Banner-B72lcEMo.js +34 -0
- package/dist/Banner.css +1 -0
- package/dist/Banner.d.ts +19 -0
- package/dist/Banner.js +4 -0
- package/dist/CodeBlock-CRzVopIv.js +25 -0
- package/dist/CodeBlock.css +1 -0
- package/dist/CodeBlock.d.ts +26 -0
- package/dist/CodeBlock.js +4 -0
- package/dist/IconButton-DPjpBKJn.js +32 -0
- package/dist/IconButton.d.ts +27 -0
- package/dist/IconButton.js +4 -0
- package/dist/NavSections-D6rDEtT1.js +142 -0
- package/dist/NavSections.css +1 -1
- package/dist/NavSections.d.ts +38 -10
- package/dist/NavSections.js +1 -1
- package/dist/Sidebar-CYFaPqdC.js +178 -0
- package/dist/Sidebar.css +1 -1
- package/dist/Sidebar.d.ts +41 -4
- package/dist/Sidebar.js +1 -1
- package/dist/SplitPane-DR7PCZ8f.js +74 -0
- package/dist/SplitPane.css +1 -0
- package/dist/SplitPane.d.ts +29 -0
- package/dist/SplitPane.js +5 -0
- package/dist/Table-BoDr3X2d.js +25 -0
- package/dist/Table.css +1 -0
- package/dist/Table.d.ts +29 -0
- package/dist/Table.js +4 -0
- package/dist/index.css +10 -2
- package/dist/index.d.ts +10 -0
- package/dist/index.es.d.ts +10 -0
- package/dist/index.es.js +71 -60
- package/dist/navTree-BRyaILN9.js +57 -0
- package/dist/navTree.d.ts +33 -0
- package/dist/tokens.css +29 -0
- package/package.json +4 -1
- package/dist/NavSections-BpyZPimZ.js +0 -79
- package/dist/Sidebar-BgZiGEDH.js +0 -108
- package/dist/kit-manifest.json +0 -1493
package/README.md
CHANGED
|
@@ -49,6 +49,11 @@ import {
|
|
|
49
49
|
RadioGroup,
|
|
50
50
|
PricingGroup,
|
|
51
51
|
PricingTable,
|
|
52
|
+
Table,
|
|
53
|
+
CodeBlock,
|
|
54
|
+
SplitPane,
|
|
55
|
+
Banner,
|
|
56
|
+
IconButton,
|
|
52
57
|
Dropdown,
|
|
53
58
|
Breadcrumb,
|
|
54
59
|
PageHead,
|
|
@@ -88,18 +93,23 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
|
|
|
88
93
|
| **RadioGroup** | Выбор одной опции-карточки | `items`, `value`, `indicator`, `indicatorSide` |
|
|
89
94
|
| **PricingGroup** | Тарифные карточки в ряд | `items` (`PricingPlan`), `selectable`, `value` |
|
|
90
95
|
| **PricingTable** | Сравнение тарифов (таблица) | те же `items` + `sections` |
|
|
96
|
+
| **Table** | Таблица данных со скроллом | `layout` (`auto`/`fixed`), стандартные props `<table>` |
|
|
97
|
+
| **CodeBlock** | Блок кода с bar и копированием | `title`, `code` / `children`, `onCopy`, `actions` |
|
|
98
|
+
| **SplitPane** | Две панели с разделителем | `direction`, `ratio`, `onRatioChange`, `first`, `second`, `minFirst` / `minSecond` |
|
|
99
|
+
| **Banner** | Статусная полоса | `tone` (`info`/`warning`/`danger`/`success`), не toast |
|
|
100
|
+
| **IconButton** | Иконка + tooltip | `name`, обязательный `label`, `tooltip` (`false` — без подсказки) |
|
|
91
101
|
| **Menu** | Навигация в шапке (не popover) | `items` (`value`, `badge`: `0` скрыт), `value`, `onSelect`, `variant` (`plain`/`line`) |
|
|
92
102
|
| **Dropdown** | Выпадающее меню (items, portal) | `label`/`trigger`, `items`/`groups`, `onSelect` |
|
|
93
103
|
| **Popover** | Свой попап в portal | `trigger`, `children`, `align`, `open` |
|
|
94
104
|
| **Breadcrumb** | Хлебные крошки | `items`, `separator`, `collapseAfter` |
|
|
95
|
-
| **Sidebar** | Левое меню приложения | `groups`, `value`, `
|
|
105
|
+
| **Sidebar** | Левое меню приложения | `groups`, `value`, `item.items` + `defaultOpen`, `open` / `onOpenChange`, `footerItems` |
|
|
96
106
|
| **PageShell** | Каркас модуля (скролл + footer) | `wide`, `scroll`, `footer`, `children` |
|
|
97
107
|
| **ChatList** | Колонка диалогов | `header`, `footer`, `children` (`List`/`Item`), `bodyRef` |
|
|
98
108
|
| **ChatMessages** | Колонка переписки | `header`, `footer`, `children`, `bodyRef`, `onBodyScroll` |
|
|
99
109
|
| **ChatSplit** | Список + лента | `list`, `messages` |
|
|
100
110
|
| **PageHead** | Заголовок страницы + actions | `title`, `sup` (текст/Badge), `subtitle`, `actions`, `sticky`, `children` |
|
|
101
111
|
| **Section** | Блок контента / auth / подраздел | `title`, `description`, `icon`, `actions`, `variant` |
|
|
102
|
-
| **NavSections** | Сайдбар + lazy-панель | `groups`
|
|
112
|
+
| **NavSections** | Сайдбар + lazy-панель | `groups`, `item.items` + `defaultOpen`, `open` / `onOpenChange`, `resolveSection` |
|
|
103
113
|
| **TabSections** | Горизонтальные вкладки + lazy | `items` (`value` + `badge`), `resolveSection` |
|
|
104
114
|
| **Card** | Карточка | `title`, `--card-pointer` |
|
|
105
115
|
| **ActionGroup** | Ряд иконок в карточке | `label`, `items` и/или `children` |
|
|
@@ -136,7 +146,17 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
|
|
|
136
146
|
{
|
|
137
147
|
value: "admin",
|
|
138
148
|
title: "Админ",
|
|
139
|
-
items: [
|
|
149
|
+
items: [
|
|
150
|
+
{
|
|
151
|
+
value: "bridge",
|
|
152
|
+
label: "VK Bridge",
|
|
153
|
+
defaultOpen: true,
|
|
154
|
+
items: [
|
|
155
|
+
{ value: "intro", label: "Введение" },
|
|
156
|
+
{ value: "events", label: "События" },
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
],
|
|
140
160
|
},
|
|
141
161
|
]}
|
|
142
162
|
footer={<AvatarName title="Паша" description="mail@…" />}
|
|
@@ -150,7 +170,7 @@ import { RadioGroup } from "@elcrm/components/RadioGroup";
|
|
|
150
170
|
/>
|
|
151
171
|
```
|
|
152
172
|
|
|
153
|
-
Токены: `--sidebar-width`, `--sidebar-item-font-size`, `--sidebar-item-padding`, `--sidebar-icon-size`, `--sidebar-group-font-size`, `--sidebar-item-active`. Для orange active в `plain` переопределите `--sidebar-item-active` в теме приложения.
|
|
173
|
+
Токены: `--sidebar-width`, `--sidebar-item-font-size`, `--sidebar-item-padding`, `--sidebar-icon-size`, `--sidebar-group-font-size`, `--sidebar-indent`, `--sidebar-item-active`. Для orange active в `plain` переопределите `--sidebar-item-active` в теме приложения.
|
|
154
174
|
|
|
155
175
|
### Stack
|
|
156
176
|
|
|
@@ -656,6 +676,15 @@ const resolve = createLazyResolver(
|
|
|
656
676
|
label: "Личное",
|
|
657
677
|
items: [
|
|
658
678
|
{ value: "Profile", label: "Профиль" },
|
|
679
|
+
{
|
|
680
|
+
value: "bridge",
|
|
681
|
+
label: "VK Bridge",
|
|
682
|
+
defaultOpen: true,
|
|
683
|
+
items: [
|
|
684
|
+
{ value: "Intro", label: "Введение" },
|
|
685
|
+
{ value: "Events", label: "События" },
|
|
686
|
+
],
|
|
687
|
+
},
|
|
659
688
|
{ value: "Appearance", label: "Оформление" },
|
|
660
689
|
],
|
|
661
690
|
},
|
|
@@ -741,6 +770,10 @@ const resolve = createLazyResolver(
|
|
|
741
770
|
--stack-grid-min
|
|
742
771
|
--toolbar-gap
|
|
743
772
|
--healthmap-cell / --healthmap-gap / --healthmap-l0…l4
|
|
773
|
+
--table-font-size / --table-cell-padding / --table-head-font-size / --table-code-font-size
|
|
774
|
+
--code-block-bg / --code-block-padding / --code-block-font-size
|
|
775
|
+
--split-handle-size / --split-handle-bg / --split-handle-bg-hover
|
|
776
|
+
--banner-padding / --banner-bg / --banner-border
|
|
744
777
|
--panel-info-width / --panel-info-width-min / --panel-info-overlay-max / --panel-info-backdrop
|
|
745
778
|
--row-gap / --column-gap
|
|
746
779
|
--popup-shadow
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import e from "react";
|
|
2
|
+
import { c as a } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './Banner.css';const i = "cLr", d = "cLi", m = "cLw", f = "cLd", w = "cLs", s = {
|
|
4
|
+
r: i,
|
|
5
|
+
i: d,
|
|
6
|
+
w: m,
|
|
7
|
+
d: f,
|
|
8
|
+
s: w
|
|
9
|
+
}, L = {
|
|
10
|
+
info: s.i,
|
|
11
|
+
warning: s.w,
|
|
12
|
+
danger: s.d,
|
|
13
|
+
success: s.s
|
|
14
|
+
};
|
|
15
|
+
function p({
|
|
16
|
+
tone: c = "info",
|
|
17
|
+
className: n,
|
|
18
|
+
role: t = "status",
|
|
19
|
+
children: o,
|
|
20
|
+
...r
|
|
21
|
+
}) {
|
|
22
|
+
return /* @__PURE__ */ e.createElement(
|
|
23
|
+
"div",
|
|
24
|
+
{
|
|
25
|
+
className: a(s.r, L[c], n),
|
|
26
|
+
role: t,
|
|
27
|
+
...r
|
|
28
|
+
},
|
|
29
|
+
o
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
p as B
|
|
34
|
+
};
|
package/dist/Banner.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer elcrm-components{.cLr{margin:0;padding:var(--banner-padding);border:1px solid var(--banner-border);border-radius:var(--shell-radius);background:var(--banner-bg);font-size:var(--shell-font-size-sm);color:var(--shell-color);line-height:1.45}.cLi{--banner-bg: color-mix(in srgb, var(--shell-color-accent) 8%, var(--shell-bg));--banner-border: color-mix(in srgb, var(--shell-color-accent) 35%, var(--shell-border))}.cLw{--banner-bg: color-mix(in srgb, var(--shell-color-warning) 10%, var(--shell-bg));--banner-border: color-mix(in srgb, var(--shell-color-warning) 40%, var(--shell-border))}.cLd{--banner-bg: color-mix(in srgb, var(--shell-color-danger) 10%, var(--shell-bg));--banner-border: color-mix(in srgb, var(--shell-color-danger) 40%, var(--shell-border))}.cLs{--banner-bg: color-mix(in srgb, var(--shell-color-success) 10%, var(--shell-bg));--banner-border: color-mix(in srgb, var(--shell-color-success) 40%, var(--shell-border))}.cLr code{font-size:.92em}}
|
package/dist/Banner.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export type BannerTone = "info" | "warning" | "danger" | "success";
|
|
3
|
+
export type BannerProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
/** Визуальный акцент */
|
|
5
|
+
tone?: BannerTone;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Статусная полоса (offline, maintenance). Не toast — не путать с Notice.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Banner tone="info" role="status">
|
|
13
|
+
* Каталог из снимка <code>kit-catalog.json</code>
|
|
14
|
+
* </Banner>
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Токены: `--banner-*`, `--shell-bg`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function Banner({ tone, className, role, children, ...rest }: BannerProps): React.JSX.Element;
|
package/dist/Banner.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import e from "react";
|
|
2
|
+
import { c as u } from "./cx-CcykAxZN.js";
|
|
3
|
+
import './CodeBlock.css';const p = "cEr", b = "cEb", d = "cEp", i = "cEa", N = "cEc", c = {
|
|
4
|
+
r: p,
|
|
5
|
+
b,
|
|
6
|
+
p: d,
|
|
7
|
+
a: i,
|
|
8
|
+
c: N
|
|
9
|
+
};
|
|
10
|
+
function k({
|
|
11
|
+
title: l,
|
|
12
|
+
code: n,
|
|
13
|
+
children: s,
|
|
14
|
+
onCopy: t,
|
|
15
|
+
copyLabel: r = "Копировать",
|
|
16
|
+
actions: a,
|
|
17
|
+
className: m,
|
|
18
|
+
...o
|
|
19
|
+
}) {
|
|
20
|
+
const E = s ?? (n != null ? /* @__PURE__ */ e.createElement("code", null, n) : null);
|
|
21
|
+
return /* @__PURE__ */ e.createElement("div", { className: u(c.r, m), ...o }, l != null || t || a ? /* @__PURE__ */ e.createElement("div", { className: c.b }, /* @__PURE__ */ e.createElement("span", null, l ?? "Код"), /* @__PURE__ */ e.createElement("div", { className: c.a }, a, t ? /* @__PURE__ */ e.createElement("button", { type: "button", className: c.c, onClick: t }, r) : null)) : null, /* @__PURE__ */ e.createElement("pre", { className: c.p }, E));
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
k as C
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer elcrm-components{.cEr{border:1px solid var(--shell-border);border-radius:var(--shell-radius);background:var(--code-block-bg);overflow:hidden}.cEb{display:flex;justify-content:space-between;align-items:center;gap:var(--shell-gap-sm);padding:var(--code-block-bar-padding);border-bottom:1px solid var(--shell-border);font-size:var(--shell-font-size-sm);color:var(--shell-color-muted)}.cEp{margin:0;padding:var(--code-block-padding);overflow:auto;font-family:var(--code-block-font-family);font-size:var(--code-block-font-size);line-height:var(--code-block-line-height);color:var(--shell-color);white-space:pre}.cEa{display:inline-flex;align-items:center;gap:.35rem;flex-shrink:0}.cEc{border:0;background:transparent;color:var(--shell-color-accent);cursor:pointer;font:inherit;padding:0}.cEc:hover,.cEc:focus-visible{text-decoration:underline}}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
export type CodeBlockProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
/** Заголовок в полосе над кодом */
|
|
4
|
+
title?: React.ReactNode;
|
|
5
|
+
/** Текст без подсветки (если нет children) */
|
|
6
|
+
code?: string;
|
|
7
|
+
/** Подсветка / разметка — свой рендер в pre */
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
/** Кнопка «Копировать»; без onCopy не показывается */
|
|
10
|
+
onCopy?: () => void;
|
|
11
|
+
/** Подпись кнопки копирования */
|
|
12
|
+
copyLabel?: string;
|
|
13
|
+
/** Доп. действия справа в bar */
|
|
14
|
+
actions?: React.ReactNode;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Блок кода: bar + pre. Подсветку передаёт приложение через `children`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <CodeBlock title="Пример" code={snippet} onCopy={() => copy(snippet)} />
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Токены: `--code-block-*`, `--shell-border`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function CodeBlock({ title, code, children, onCopy, copyLabel, actions, className, ...rest }: CodeBlockProps): React.JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import t from "react";
|
|
2
|
+
import { IconButton as f } from "@elcrm/button/IconButton";
|
|
3
|
+
import { Icons as p } from "@elcrm/icons";
|
|
4
|
+
import { Tooltip as u } from "@elcrm/tooltip";
|
|
5
|
+
const I = {
|
|
6
|
+
s: 16,
|
|
7
|
+
m: 18,
|
|
8
|
+
l: 20
|
|
9
|
+
};
|
|
10
|
+
function C({
|
|
11
|
+
name: c,
|
|
12
|
+
label: n,
|
|
13
|
+
tooltip: o,
|
|
14
|
+
iconSize: m,
|
|
15
|
+
size: e = "m",
|
|
16
|
+
tooltipPlacement: i,
|
|
17
|
+
...s
|
|
18
|
+
}) {
|
|
19
|
+
const a = m ?? I[e], r = /* @__PURE__ */ t.createElement(
|
|
20
|
+
f,
|
|
21
|
+
{
|
|
22
|
+
label: n,
|
|
23
|
+
size: e,
|
|
24
|
+
icon: /* @__PURE__ */ t.createElement(p.Line, { name: c, size: a }),
|
|
25
|
+
...s
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
return o === !1 ? r : /* @__PURE__ */ t.createElement(u, { content: o ?? n, placement: i }, r);
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
C as I
|
|
32
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { TIconButton } from '@elcrm/button/IconButton';
|
|
3
|
+
import { TButtonSize } from '@elcrm/button';
|
|
4
|
+
import { TooltipPlacement } from '@elcrm/tooltip';
|
|
5
|
+
export type IconButtonProps = Omit<TIconButton, "icon" | "label" | "size"> & {
|
|
6
|
+
/** Имя из `icons update` / `Icons.Line` (в приложении — `IconLineName`) */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Подпись: `aria-label` и текст tooltip по умолчанию */
|
|
9
|
+
label: string;
|
|
10
|
+
size?: TButtonSize;
|
|
11
|
+
/** Подсказка; `false` — без Tooltip (например, внутри toolbar с aria-label) */
|
|
12
|
+
tooltip?: React.ReactNode | false;
|
|
13
|
+
/** Размер SVG; по умолчанию от `size` кнопки */
|
|
14
|
+
iconSize?: number;
|
|
15
|
+
/** Сторона tooltip */
|
|
16
|
+
tooltipPlacement?: TooltipPlacement;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Иконка-кнопка: `Icons.Line` + {@link ShellIconButton} + {@link Tooltip}.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <IconButton name="power" label="Выход" onClick={logout} />
|
|
24
|
+
* <IconButton name="copy_01" label="Копировать" tooltip="Скопировать CSS" onClick={copy} />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function IconButton({ name, label, tooltip, iconSize, size, tooltipPlacement, ...rest }: IconButtonProps): React.JSX.Element;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import e, { useMemo as B, Suspense as D } from "react";
|
|
2
|
+
import { c as K } from "./cx-CcykAxZN.js";
|
|
3
|
+
import { I as F } from "./Item-D4okhsfW.js";
|
|
4
|
+
import { f as I, c as M, a as O, b as P, i as V } from "./navTree-BRyaILN9.js";
|
|
5
|
+
import { h as j } from "./rovingKeys-CGlfTtNQ.js";
|
|
6
|
+
import { u as y } from "./useControllableValue-CWqH1kpM.js";
|
|
7
|
+
import './NavSections.css';const A = "cvr", W = "cvn", $ = "cvg", q = "cvl", z = "cvi", G = "cva", H = "cvc", J = "cvt", Q = "cve", T = "cvs", U = "cvf", X = "cvm", c = {
|
|
8
|
+
r: A,
|
|
9
|
+
n: W,
|
|
10
|
+
g: $,
|
|
11
|
+
l: q,
|
|
12
|
+
i: z,
|
|
13
|
+
a: G,
|
|
14
|
+
c: H,
|
|
15
|
+
t: J,
|
|
16
|
+
e: Q,
|
|
17
|
+
s: T,
|
|
18
|
+
f: U,
|
|
19
|
+
m: X
|
|
20
|
+
};
|
|
21
|
+
function Y({ className: a }) {
|
|
22
|
+
return /* @__PURE__ */ e.createElement("svg", { className: a, viewBox: "0 0 12 12", "aria-hidden": !0 }, /* @__PURE__ */ e.createElement(
|
|
23
|
+
"path",
|
|
24
|
+
{
|
|
25
|
+
d: "M2.5 4.5 6 8l3.5-3.5",
|
|
26
|
+
fill: "none",
|
|
27
|
+
stroke: "currentColor",
|
|
28
|
+
strokeWidth: "1.5",
|
|
29
|
+
strokeLinecap: "round",
|
|
30
|
+
strokeLinejoin: "round"
|
|
31
|
+
}
|
|
32
|
+
));
|
|
33
|
+
}
|
|
34
|
+
function C({
|
|
35
|
+
items: a,
|
|
36
|
+
value: d,
|
|
37
|
+
open: f,
|
|
38
|
+
onSelect: m,
|
|
39
|
+
onToggle: E
|
|
40
|
+
}) {
|
|
41
|
+
return /* @__PURE__ */ e.createElement(e.Fragment, null, a.map((t) => {
|
|
42
|
+
const v = t.items, s = v != null && v.length > 0, u = s && f.includes(t.value), r = !s && t.value === d;
|
|
43
|
+
return /* @__PURE__ */ e.createElement(e.Fragment, { key: t.value }, /* @__PURE__ */ e.createElement(
|
|
44
|
+
F,
|
|
45
|
+
{
|
|
46
|
+
as: "button",
|
|
47
|
+
active: r,
|
|
48
|
+
className: K(c.i, r && c.a),
|
|
49
|
+
"data-active": r ? "true" : void 0,
|
|
50
|
+
"data-open": u ? "true" : void 0,
|
|
51
|
+
"aria-current": r ? "page" : void 0,
|
|
52
|
+
"aria-expanded": s ? u : void 0,
|
|
53
|
+
disabled: t.disabled,
|
|
54
|
+
onClick: () => {
|
|
55
|
+
s ? E(t.value) : m(t.value);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
t.icon != null ? /* @__PURE__ */ e.createElement("span", { className: c.c, "aria-hidden": !0 }, t.icon) : null,
|
|
59
|
+
/* @__PURE__ */ e.createElement("span", { className: c.t }, t.label),
|
|
60
|
+
s ? /* @__PURE__ */ e.createElement(Y, { className: c.e }) : null
|
|
61
|
+
), s && u ? /* @__PURE__ */ e.createElement("div", { className: c.s }, /* @__PURE__ */ e.createElement(
|
|
62
|
+
C,
|
|
63
|
+
{
|
|
64
|
+
items: v,
|
|
65
|
+
value: d,
|
|
66
|
+
open: f,
|
|
67
|
+
onSelect: m,
|
|
68
|
+
onToggle: E
|
|
69
|
+
}
|
|
70
|
+
)) : null);
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function le({
|
|
74
|
+
groups: a,
|
|
75
|
+
resolveSection: d,
|
|
76
|
+
value: f,
|
|
77
|
+
defaultValue: m,
|
|
78
|
+
onValueChange: E,
|
|
79
|
+
open: t,
|
|
80
|
+
defaultOpen: v,
|
|
81
|
+
onOpenChange: s,
|
|
82
|
+
loading: u = null,
|
|
83
|
+
footer: r,
|
|
84
|
+
className: R,
|
|
85
|
+
navLabel: S = "Разделы"
|
|
86
|
+
}) {
|
|
87
|
+
const g = B(
|
|
88
|
+
() => m || I(a),
|
|
89
|
+
[m, a]
|
|
90
|
+
), [l, b] = y(
|
|
91
|
+
f,
|
|
92
|
+
g,
|
|
93
|
+
E
|
|
94
|
+
), [o = [], N] = y(
|
|
95
|
+
t,
|
|
96
|
+
M(a, v, l),
|
|
97
|
+
s
|
|
98
|
+
), x = O(a).map((n) => n.value), p = e.useRef(l);
|
|
99
|
+
e.useEffect(() => {
|
|
100
|
+
if (l == null || l === p.current) {
|
|
101
|
+
p.current = l;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
p.current = l;
|
|
105
|
+
const i = P(a, l).filter((w) => !o.includes(w));
|
|
106
|
+
i.length && N([...o, ...i]);
|
|
107
|
+
}, [l, a, o, N]);
|
|
108
|
+
const h = (n) => {
|
|
109
|
+
N(
|
|
110
|
+
o.includes(n) ? o.filter((i) => i !== n) : [...o, n]
|
|
111
|
+
);
|
|
112
|
+
}, L = (n) => {
|
|
113
|
+
V(a, n) ? h(n) : b(n);
|
|
114
|
+
}, k = l ? d(l) : null;
|
|
115
|
+
return /* @__PURE__ */ e.createElement("div", { className: K(c.r, R) }, /* @__PURE__ */ e.createElement(
|
|
116
|
+
"nav",
|
|
117
|
+
{
|
|
118
|
+
className: c.n,
|
|
119
|
+
"aria-label": S,
|
|
120
|
+
onKeyDown: (n) => j(n, {
|
|
121
|
+
values: x,
|
|
122
|
+
value: l,
|
|
123
|
+
onSelect: L,
|
|
124
|
+
vertical: !0
|
|
125
|
+
})
|
|
126
|
+
},
|
|
127
|
+
a.map((n, i) => /* @__PURE__ */ e.createElement("div", { key: i, className: c.g }, n.label != null ? /* @__PURE__ */ e.createElement("div", { className: c.l }, n.label) : null, /* @__PURE__ */ e.createElement(
|
|
128
|
+
C,
|
|
129
|
+
{
|
|
130
|
+
items: n.items,
|
|
131
|
+
value: l,
|
|
132
|
+
open: o,
|
|
133
|
+
onSelect: b,
|
|
134
|
+
onToggle: h
|
|
135
|
+
}
|
|
136
|
+
))),
|
|
137
|
+
r != null ? /* @__PURE__ */ e.createElement("div", { className: c.f }, r) : null
|
|
138
|
+
), /* @__PURE__ */ e.createElement("div", { className: c.m }, k ? /* @__PURE__ */ e.createElement(D, { fallback: u }, e.createElement(k)) : null));
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
le as N
|
|
142
|
+
};
|
package/dist/NavSections.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer elcrm-components{.cvr{display:flex;align-items:stretch;gap:var(--shell-gap-lg);width:100%;flex:1 1 auto;min-height:0;box-sizing:border-box;padding:var(--nav-sections-padding)}.cvn{display:flex;flex-direction:column;gap:var(--shell-gap);flex:0 0 var(--nav-sections-nav-width);width:var(--nav-sections-nav-width);min-width:var(--nav-sections-nav-width);box-sizing:border-box}.cvg{display:flex;flex-direction:column;gap:2px;padding-bottom:var(--shell-gap);border-bottom:1px solid var(--shell-border)}.cvg:last-of-type{border-bottom:none;padding-bottom:0}.cvl{font-size:var(--sidebar-group-font-size);font-weight:var(--shell-font-weight);letter-spacing:.04em;text-transform:uppercase;color:var(--shell-color-muted);padding:0 var(--shell-padding-inline) var(--shell-gap-sm)}.cvi:is(button){position:relative;font-size:var(--shell-font-size-sm)}.cva:before{content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;border-radius:0 2px 2px 0;background:var(--shell-color-accent)}.cvi:disabled{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cvc{display:inline-flex;flex:0 0 auto;color:var(--shell-color-muted);line-height:0}.cva .cvc{color:var(--shell-color)}.cvt{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cvf{margin-top:auto;padding-top:var(--shell-gap);border-top:1px solid var(--shell-border)}.cvm{flex:1 1 auto;min-width:0;max-width:var(--nav-sections-main-max-width);width:100%;min-height:0;overflow:auto}@media (max-width: 720px){.cvr{flex-direction:column}.cvn{flex:none;width:100%;min-width:0;max-width:none}}}
|
|
1
|
+
@layer elcrm-components{.cvr{display:flex;align-items:stretch;gap:var(--shell-gap-lg);width:100%;flex:1 1 auto;min-height:0;box-sizing:border-box;padding:var(--nav-sections-padding)}.cvn{display:flex;flex-direction:column;gap:var(--shell-gap);flex:0 0 var(--nav-sections-nav-width);width:var(--nav-sections-nav-width);min-width:var(--nav-sections-nav-width);box-sizing:border-box}.cvg{display:flex;flex-direction:column;gap:2px;padding-bottom:var(--shell-gap);border-bottom:1px solid var(--shell-border)}.cvg:last-of-type{border-bottom:none;padding-bottom:0}.cvl{font-size:var(--sidebar-group-font-size);font-weight:var(--shell-font-weight);letter-spacing:.04em;text-transform:uppercase;color:var(--shell-color-muted);padding:0 var(--shell-padding-inline) var(--shell-gap-sm)}.cvi:is(button){position:relative;display:flex;width:100%;align-items:center;font-size:var(--shell-font-size-sm)}.cva:before{content:"";position:absolute;left:0;top:6px;bottom:6px;width:3px;border-radius:0 2px 2px 0;background:var(--shell-color-accent)}.cvi:disabled{opacity:var(--shell-disabled-opacity);cursor:not-allowed}.cvc{display:inline-flex;flex:0 0 auto;color:var(--shell-color-muted);line-height:0}.cva .cvc{color:var(--shell-color)}.cvt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cve{flex-shrink:0;width:12px;height:12px;margin-left:auto;color:var(--shell-color-muted);transition:transform .15s ease}.cvi:is(button)[data-open=true] .cve{transform:rotate(180deg);color:inherit}.cvs{display:flex;flex-direction:column;gap:2px;padding-inline-start:var(--sidebar-indent)}.cvf{margin-top:auto;padding-top:var(--shell-gap);border-top:1px solid var(--shell-border)}.cvm{flex:1 1 auto;min-width:0;max-width:var(--nav-sections-main-max-width);width:100%;min-height:0;overflow:auto}@media (max-width: 720px){.cvr{flex-direction:column}.cvn{flex:none;width:100%;min-width:0;max-width:none}}}
|
package/dist/NavSections.d.ts
CHANGED
|
@@ -15,6 +15,17 @@ export type NavSectionsItem = {
|
|
|
15
15
|
icon?: React.ReactNode;
|
|
16
16
|
/** Недоступный пункт. */
|
|
17
17
|
disabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Вложенные пункты — есть → шеврон и раскрытие.
|
|
20
|
+
* Клик по родителю только открывает/закрывает, панель не меняем.
|
|
21
|
+
*/
|
|
22
|
+
items?: NavSectionsItem[];
|
|
23
|
+
/**
|
|
24
|
+
* Подменю открыто при первом рендере.
|
|
25
|
+
* Если выбран потомок — ветка откроется сама.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
defaultOpen?: boolean;
|
|
18
29
|
};
|
|
19
30
|
/**
|
|
20
31
|
* Группа пунктов сайдбара (опциональный заголовок + `items`).
|
|
@@ -46,6 +57,17 @@ export type NavSectionsProps = {
|
|
|
46
57
|
defaultValue?: string;
|
|
47
58
|
/** Смена активного раздела (`next` = `item.value`). */
|
|
48
59
|
onValueChange?: (value: string) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Раскрытые родители (их `value`). Контролируемый режим.
|
|
62
|
+
* Вместе с `onOpenChange`.
|
|
63
|
+
*/
|
|
64
|
+
open?: string[];
|
|
65
|
+
/**
|
|
66
|
+
* Начально раскрытые (плюс `item.defaultOpen` и предки выбранного).
|
|
67
|
+
*/
|
|
68
|
+
defaultOpen?: string[];
|
|
69
|
+
/** Смена набора раскрытых родителей */
|
|
70
|
+
onOpenChange?: (open: string[]) => void;
|
|
49
71
|
/** Fallback для `<Suspense>` при ленивой загрузке секции. */
|
|
50
72
|
loading?: React.ReactNode;
|
|
51
73
|
/** Низ сайдбара (например кнопка «Назад»). */
|
|
@@ -68,34 +90,40 @@ export type NavSectionsProps = {
|
|
|
68
90
|
* - {@link Menu} — простая навигация без панелей.
|
|
69
91
|
*
|
|
70
92
|
* API: декларативные `groups` / `items`. Идентификатор раздела — `value` (не `key`).
|
|
93
|
+
* У пункта `items` + `defaultOpen` — раскрываемое подменю (как у Sidebar).
|
|
71
94
|
* Compound-слоты (`NavSections.Item`) не используются.
|
|
72
95
|
*
|
|
73
96
|
* CSS-токены: `--shell-*`, `--nav-sections-padding`, `--nav-sections-nav-width`,
|
|
74
|
-
* `--nav-sections-main-max-width`.
|
|
97
|
+
* `--nav-sections-main-max-width`, `--sidebar-indent`.
|
|
75
98
|
*
|
|
76
99
|
* @example
|
|
77
100
|
* ```tsx
|
|
78
101
|
* const groups = [
|
|
79
102
|
* {
|
|
80
|
-
* label: "
|
|
103
|
+
* label: "Документация",
|
|
81
104
|
* items: [
|
|
82
|
-
* {
|
|
83
|
-
*
|
|
105
|
+
* {
|
|
106
|
+
* value: "bridge",
|
|
107
|
+
* label: "VK Bridge",
|
|
108
|
+
* defaultOpen: true,
|
|
109
|
+
* items: [
|
|
110
|
+
* { value: "intro", label: "Введение" },
|
|
111
|
+
* { value: "events", label: "События", items: [
|
|
112
|
+
* { value: "evt-init", label: "VKWebAppInit" },
|
|
113
|
+
* ] },
|
|
114
|
+
* ],
|
|
115
|
+
* },
|
|
116
|
+
* { value: "auth", label: "Авторизация VK" },
|
|
84
117
|
* ],
|
|
85
118
|
* },
|
|
86
119
|
* ];
|
|
87
120
|
*
|
|
88
|
-
* const resolveSection = createLazyResolver(
|
|
89
|
-
* (v) => import(`./sections/${v}.tsx`),
|
|
90
|
-
* );
|
|
91
|
-
*
|
|
92
121
|
* <NavSections
|
|
93
122
|
* groups={groups}
|
|
94
123
|
* resolveSection={resolveSection}
|
|
95
124
|
* value={section}
|
|
96
125
|
* onValueChange={setSection}
|
|
97
|
-
* footer={<Button onSend={() => navigate(-1)}>Назад</Button>}
|
|
98
126
|
* />
|
|
99
127
|
* ```
|
|
100
128
|
*/
|
|
101
|
-
export declare function NavSections({ groups, resolveSection, value: valueProp, defaultValue, onValueChange, loading, footer, className, navLabel, }: NavSectionsProps): React.JSX.Element;
|
|
129
|
+
export declare function NavSections({ groups, resolveSection, value: valueProp, defaultValue, onValueChange, open: openProp, defaultOpen, onOpenChange, loading, footer, className, navLabel, }: NavSectionsProps): React.JSX.Element;
|
package/dist/NavSections.js
CHANGED