@sbkbs/ui 1.0.6 → 2.0.0-beta.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/CHANGELOG.md +57 -0
- package/MIGRATION.md +96 -0
- package/README.md +221 -115
- package/dist/assets/favicon/apple-touch-icon.png +0 -0
- package/dist/assets/favicon/favicon-96x96.png +0 -0
- package/dist/assets/favicon/favicon.ico +0 -0
- package/dist/assets/favicon/favicon.svg +1 -0
- package/dist/assets/favicon/site.webmanifest +21 -0
- package/dist/assets/favicon/web-app-manifest-192x192.png +0 -0
- package/dist/assets/favicon/web-app-manifest-512x512.png +0 -0
- package/dist/base.css +38 -0
- package/dist/components/Accordion/Accordion.d.ts +13 -1
- package/dist/components/Accordion/Accordion.js +100 -52
- package/dist/components/Accordion/Accordion.js.map +1 -1
- package/dist/components/Alert/Alert.d.ts +17 -4
- package/dist/components/Alert/Alert.js +64 -20
- package/dist/components/Alert/Alert.js.map +1 -1
- package/dist/components/Avatar/Avatar.d.ts +12 -6
- package/dist/components/Avatar/Avatar.js +66 -0
- package/dist/components/Avatar/Avatar.js.map +1 -0
- package/dist/components/Badge/Badge.d.ts +11 -2
- package/dist/components/Badge/Badge.js +42 -30
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Breadcrumb/Breadcrumb.d.ts +5 -2
- package/dist/components/Breadcrumb/Breadcrumb.js +23 -16
- package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -1
- package/dist/components/Button/Button.d.ts +10 -8
- package/dist/components/Button/Button.js +73 -114
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Card/Card.d.ts +33 -3
- package/dist/components/Card/Card.js +91 -30
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.d.ts +4 -1
- package/dist/components/Checkbox/Checkbox.js +32 -31
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/Chip/Chip.d.ts +7 -1
- package/dist/components/Chip/Chip.js +19 -9
- package/dist/components/Chip/Chip.js.map +1 -1
- package/dist/components/Combobox/Combobox.d.ts +10 -2
- package/dist/components/Combobox/Combobox.js +146 -133
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Divider/Divider.d.ts +7 -1
- package/dist/components/Divider/Divider.js +28 -12
- package/dist/components/Divider/Divider.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +33 -0
- package/dist/components/DropdownMenu/DropdownMenu.js +147 -0
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
- package/dist/components/EmptyState/EmptyState.d.ts +7 -1
- package/dist/components/EmptyState/EmptyState.js +28 -19
- package/dist/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/Field/Field.d.ts +21 -0
- package/dist/components/Field/Field.js +35 -0
- package/dist/components/Field/Field.js.map +1 -0
- package/dist/components/FileUpload/FileUpload.d.ts +9 -2
- package/dist/components/FileUpload/FileUpload.js +128 -102
- package/dist/components/FileUpload/FileUpload.js.map +1 -1
- package/dist/components/Footer/Footer.d.ts +19 -6
- package/dist/components/Footer/Footer.js +47 -115
- package/dist/components/Footer/Footer.js.map +1 -1
- package/dist/components/GlassPanel/GlassPanel.d.ts +18 -0
- package/dist/components/GlassPanel/GlassPanel.js +39 -0
- package/dist/components/GlassPanel/GlassPanel.js.map +1 -0
- package/dist/components/Header/Header.d.ts +25 -14
- package/dist/components/Header/Header.js +88 -148
- package/dist/components/Header/Header.js.map +1 -1
- package/dist/components/Highlight/Highlight.d.ts +13 -0
- package/dist/components/Highlight/Highlight.js +18 -0
- package/dist/components/Highlight/Highlight.js.map +1 -0
- package/dist/components/Icon/Icon.d.ts +21 -0
- package/dist/components/Icon/Icon.js +31 -0
- package/dist/components/Icon/Icon.js.map +1 -0
- package/dist/components/IconButton/IconButton.d.ts +12 -0
- package/dist/components/IconButton/IconButton.js +49 -0
- package/dist/components/IconButton/IconButton.js.map +1 -0
- package/dist/components/Input/Input.d.ts +3 -2
- package/dist/components/Input/Input.js +51 -69
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/Layout/Layout.d.ts +2 -0
- package/dist/components/Layout/Layout.js +24 -18
- package/dist/components/Layout/Layout.js.map +1 -1
- package/dist/components/Logo/Logo.d.ts +27 -0
- package/dist/components/Logo/Logo.js +107 -0
- package/dist/components/Logo/Logo.js.map +1 -0
- package/dist/components/MiniCard/MiniCard.d.ts +21 -0
- package/dist/components/MiniCard/MiniCard.js +90 -0
- package/dist/components/MiniCard/MiniCard.js.map +1 -0
- package/dist/components/Modal/Modal.d.ts +16 -3
- package/dist/components/Modal/Modal.js +124 -93
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Navigation/Navigation.d.ts +1 -0
- package/dist/components/Navigation/Navigation.js +14 -14
- package/dist/components/Navigation/Navigation.js.map +1 -1
- package/dist/components/NumberInput/NumberInput.d.ts +5 -2
- package/dist/components/NumberInput/NumberInput.js +98 -128
- package/dist/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/components/PageHeader/PageHeader.d.ts +8 -1
- package/dist/components/PageHeader/PageHeader.js +26 -25
- package/dist/components/PageHeader/PageHeader.js.map +1 -1
- package/dist/components/Pagination/Pagination.d.ts +15 -2
- package/dist/components/Pagination/Pagination.js +68 -34
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/Paragraph/Paragraph.d.ts +8 -2
- package/dist/components/Paragraph/Paragraph.js +39 -36
- package/dist/components/Paragraph/Paragraph.js.map +1 -1
- package/dist/components/Popover/Popover.d.ts +12 -1
- package/dist/components/Popover/Popover.js +49 -37
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/ProgressBar/ProgressBar.d.ts +12 -6
- package/dist/components/ProgressBar/ProgressBar.js +71 -0
- package/dist/components/ProgressBar/ProgressBar.js.map +1 -0
- package/dist/components/Radio/Radio.d.ts +2 -1
- package/dist/components/Radio/Radio.js +27 -31
- package/dist/components/Radio/Radio.js.map +1 -1
- package/dist/components/SectionHeader/SectionHeader.d.ts +6 -1
- package/dist/components/SectionHeader/SectionHeader.js +14 -12
- package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
- package/dist/components/Select/Select.d.ts +6 -2
- package/dist/components/Select/Select.js +47 -35
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/SideNav/SideNav.d.ts +55 -0
- package/dist/components/SideNav/SideNav.js +141 -0
- package/dist/components/SideNav/SideNav.js.map +1 -0
- package/dist/components/Skeleton/Skeleton.d.ts +12 -1
- package/dist/components/Skeleton/Skeleton.js +33 -18
- package/dist/components/Skeleton/Skeleton.js.map +1 -1
- package/dist/components/Spinner/Spinner.d.ts +11 -3
- package/dist/components/Spinner/Spinner.js +57 -0
- package/dist/components/Spinner/Spinner.js.map +1 -0
- package/dist/components/Stepper/Stepper.d.ts +1 -1
- package/dist/components/Stepper/Stepper.js +41 -53
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/Subtitle/Subtitle.d.ts +9 -3
- package/dist/components/Subtitle/Subtitle.js +29 -35
- package/dist/components/Subtitle/Subtitle.js.map +1 -1
- package/dist/components/Switch/Switch.d.ts +2 -1
- package/dist/components/Switch/Switch.js +45 -33
- package/dist/components/Switch/Switch.js.map +1 -1
- package/dist/components/Table/Table.d.ts +19 -1
- package/dist/components/Table/Table.js +48 -11
- package/dist/components/Table/Table.js.map +1 -1
- package/dist/components/Tabs/Tabs.d.ts +16 -2
- package/dist/components/Tabs/Tabs.js +80 -44
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Tag/Tag.d.ts +8 -1
- package/dist/components/Tag/Tag.js +27 -12
- package/dist/components/Tag/Tag.js.map +1 -1
- package/dist/components/Textarea/Textarea.d.ts +3 -1
- package/dist/components/Textarea/Textarea.js +36 -40
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/Title/Title.d.ts +9 -3
- package/dist/components/Title/Title.js +42 -42
- package/dist/components/Title/Title.js.map +1 -1
- package/dist/components/Toast/Toast.d.ts +39 -11
- package/dist/components/Toast/Toast.js +130 -76
- package/dist/components/Toast/Toast.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +12 -2
- package/dist/components/Tooltip/Tooltip.js +50 -32
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Typography/weight.d.ts +9 -0
- package/dist/components/Typography/weight.js +5 -0
- package/dist/components/Typography/weight.js.map +1 -0
- package/dist/fonts.css +8 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +55 -43
- package/dist/index.js +118 -83
- package/dist/index.js.map +1 -1
- package/dist/reset.css +9 -0
- package/dist/styles.css +1 -0
- package/dist/tailwind.css +153 -0
- package/dist/theme/ThemeProvider.d.ts +21 -3
- package/dist/theme/ThemeProvider.js +57 -18
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/tokens.d.ts +43 -0
- package/dist/theme/tokens.js +39 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/theme.css +296 -263
- package/dist/tokens.css +327 -0
- package/dist/utils/cn.d.ts +2 -2
- package/dist/utils/cn.js +19 -4
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/styles.d.ts +37 -0
- package/dist/utils/styles.js +35 -0
- package/dist/utils/styles.js.map +1 -0
- package/package.json +140 -117
- package/dist/components/Accordion/Accordion.stories.d.ts +0 -7
- package/dist/components/Alert/Alert.stories.d.ts +0 -8
- package/dist/components/Badge/Badge.stories.d.ts +0 -8
- package/dist/components/Breadcrumb/Breadcrumb.stories.d.ts +0 -8
- package/dist/components/Button/Button.stories.d.ts +0 -7
- package/dist/components/Card/Card.stories.d.ts +0 -8
- package/dist/components/Checkbox/Checkbox.stories.d.ts +0 -7
- package/dist/components/Combobox/Combobox.stories.d.ts +0 -9
- package/dist/components/Feedback/Feedback.stories.d.ts +0 -5
- package/dist/components/FileUpload/FileUpload.stories.d.ts +0 -10
- package/dist/components/Input/Input.stories.d.ts +0 -9
- package/dist/components/Modal/Modal.stories.d.ts +0 -8
- package/dist/components/NumberInput/NumberInput.stories.d.ts +0 -8
- package/dist/components/Pagination/Pagination.stories.d.ts +0 -8
- package/dist/components/Radio/Radio.stories.d.ts +0 -7
- package/dist/components/Select/Select.stories.d.ts +0 -8
- package/dist/components/Stepper/Stepper.stories.d.ts +0 -8
- package/dist/components/Switch/Switch.stories.d.ts +0 -8
- package/dist/components/Tabs/Tabs.stories.d.ts +0 -8
- package/dist/components/Textarea/Textarea.stories.d.ts +0 -7
- package/dist/components/Toast/Toast.stories.d.ts +0 -8
- package/dist/oficios-ui.css +0 -1
- package/dist/oficios-ui.umd.cjs +0 -2
- package/dist/oficios-ui.umd.cjs.map +0 -1
- package/dist/sbk-icon.png +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export interface DropdownMenuItem {
|
|
3
|
+
/** Ação destrutiva (ex.: excluir), exibida em cor de erro. */
|
|
4
|
+
danger?: boolean;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
id: string;
|
|
8
|
+
label: React.ReactNode;
|
|
9
|
+
onSelect?: () => void;
|
|
10
|
+
/** Atalho exibido à direita (ex.: "Ctrl+S"). */
|
|
11
|
+
shortcut?: string;
|
|
12
|
+
type?: 'item';
|
|
13
|
+
}
|
|
14
|
+
export interface DropdownMenuSeparator {
|
|
15
|
+
id: string;
|
|
16
|
+
type: 'separator';
|
|
17
|
+
}
|
|
18
|
+
export type DropdownMenuEntry = DropdownMenuItem | DropdownMenuSeparator;
|
|
19
|
+
export interface DropdownMenuProps {
|
|
20
|
+
align?: 'start' | 'end';
|
|
21
|
+
/** Rótulo acessível do menu quando o gatilho não tem texto. */
|
|
22
|
+
'aria-label'?: string;
|
|
23
|
+
items: DropdownMenuEntry[];
|
|
24
|
+
/** Conteúdo do botão gatilho. */
|
|
25
|
+
trigger: React.ReactNode;
|
|
26
|
+
triggerClassName?: string;
|
|
27
|
+
variant?: 'default' | 'glass';
|
|
28
|
+
}
|
|
29
|
+
/** Menu de ações (padrão ARIA menu button): setas, Home/End, Enter, Esc e digitação por letra. */
|
|
30
|
+
export declare const DropdownMenu: {
|
|
31
|
+
({ trigger, items, align, variant, triggerClassName, "aria-label": ariaLabel, }: DropdownMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
displayName: string;
|
|
33
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsxs as N, jsx as n } from "react/jsx-runtime";
|
|
2
|
+
import { useState as S, useId as y, useRef as f, useEffect as K } from "react";
|
|
3
|
+
import { cn as m } from "../../utils/cn.js";
|
|
4
|
+
import { focusRing as q, buttonReset as z } from "../../utils/styles.js";
|
|
5
|
+
const B = (c) => c.type !== "separator", M = ({
|
|
6
|
+
trigger: c,
|
|
7
|
+
items: R,
|
|
8
|
+
align: A = "start",
|
|
9
|
+
variant: C = "default",
|
|
10
|
+
triggerClassName: I,
|
|
11
|
+
"aria-label": p
|
|
12
|
+
}) => {
|
|
13
|
+
const [o, a] = S(!1), b = y(), k = f(null), d = f(null), g = f(null), h = () => {
|
|
14
|
+
var e;
|
|
15
|
+
return Array.from(
|
|
16
|
+
((e = d.current) == null ? void 0 : e.querySelectorAll(
|
|
17
|
+
'[role="menuitem"]:not([aria-disabled="true"])'
|
|
18
|
+
)) ?? []
|
|
19
|
+
);
|
|
20
|
+
}, r = (e) => {
|
|
21
|
+
var s;
|
|
22
|
+
const t = h();
|
|
23
|
+
t.length !== 0 && ((s = t[(e + t.length) % t.length]) == null || s.focus());
|
|
24
|
+
}, i = (e = !0) => {
|
|
25
|
+
var t;
|
|
26
|
+
a(!1), e && ((t = k.current) == null || t.focus());
|
|
27
|
+
};
|
|
28
|
+
K(() => {
|
|
29
|
+
if (!o) return;
|
|
30
|
+
requestAnimationFrame(
|
|
31
|
+
() => {
|
|
32
|
+
var t, s;
|
|
33
|
+
return (s = (t = d.current) == null ? void 0 : t.querySelector('[role="menuitem"]:not([aria-disabled="true"])')) == null ? void 0 : s.focus();
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
const e = (t) => {
|
|
37
|
+
var s;
|
|
38
|
+
(s = g.current) != null && s.contains(t.target) || a(!1);
|
|
39
|
+
};
|
|
40
|
+
return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
41
|
+
}, [o]);
|
|
42
|
+
const E = (e) => {
|
|
43
|
+
const t = h(), s = t.indexOf(document.activeElement);
|
|
44
|
+
if (e.key === "ArrowDown")
|
|
45
|
+
e.preventDefault(), r(s + 1);
|
|
46
|
+
else if (e.key === "ArrowUp")
|
|
47
|
+
e.preventDefault(), r(s - 1);
|
|
48
|
+
else if (e.key === "Home")
|
|
49
|
+
e.preventDefault(), r(0);
|
|
50
|
+
else if (e.key === "End")
|
|
51
|
+
e.preventDefault(), r(t.length - 1);
|
|
52
|
+
else if (e.key === "Escape")
|
|
53
|
+
e.preventDefault(), i();
|
|
54
|
+
else if (e.key === "Tab")
|
|
55
|
+
i(!1);
|
|
56
|
+
else if (e.key.length === 1 && /\S/.test(e.key)) {
|
|
57
|
+
const x = e.key.toLocaleLowerCase("pt-BR"), v = t.findIndex(
|
|
58
|
+
(u, l) => {
|
|
59
|
+
var D;
|
|
60
|
+
return l > s && ((D = u.textContent) == null ? void 0 : D.trim().toLocaleLowerCase("pt-BR").startsWith(x));
|
|
61
|
+
}
|
|
62
|
+
), L = t.findIndex(
|
|
63
|
+
(u) => {
|
|
64
|
+
var l;
|
|
65
|
+
return (l = u.textContent) == null ? void 0 : l.trim().toLocaleLowerCase("pt-BR").startsWith(x);
|
|
66
|
+
}
|
|
67
|
+
), w = v >= 0 ? v : L;
|
|
68
|
+
w >= 0 && r(w);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return /* @__PURE__ */ N("div", { ref: g, className: "relative inline-flex", children: [
|
|
72
|
+
/* @__PURE__ */ n(
|
|
73
|
+
"button",
|
|
74
|
+
{
|
|
75
|
+
ref: k,
|
|
76
|
+
"aria-controls": o ? b : void 0,
|
|
77
|
+
"aria-expanded": o,
|
|
78
|
+
"aria-haspopup": "menu",
|
|
79
|
+
"aria-label": p,
|
|
80
|
+
className: m(
|
|
81
|
+
z,
|
|
82
|
+
"inline-flex cursor-pointer items-center",
|
|
83
|
+
q,
|
|
84
|
+
I
|
|
85
|
+
),
|
|
86
|
+
onClick: () => a((e) => !e),
|
|
87
|
+
onKeyDown: (e) => {
|
|
88
|
+
(e.key === "ArrowDown" || e.key === "ArrowUp") && (e.preventDefault(), a(!0));
|
|
89
|
+
},
|
|
90
|
+
type: "button",
|
|
91
|
+
children: c
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
o && /* @__PURE__ */ n(
|
|
95
|
+
"div",
|
|
96
|
+
{
|
|
97
|
+
ref: d,
|
|
98
|
+
"aria-label": p,
|
|
99
|
+
className: m(
|
|
100
|
+
"absolute top-full z-[var(--sbk-z-dropdown)] mt-1 min-w-48 rounded-sbk-sm p-1 animate-sbk-fade-in",
|
|
101
|
+
C === "glass" ? "sbk-glass sbk-glass-strong" : "border border-sbk-line bg-sbk-surface shadow-sbk-md",
|
|
102
|
+
A === "end" ? "right-0" : "left-0"
|
|
103
|
+
),
|
|
104
|
+
id: b,
|
|
105
|
+
onKeyDown: E,
|
|
106
|
+
role: "menu",
|
|
107
|
+
tabIndex: -1,
|
|
108
|
+
children: R.map(
|
|
109
|
+
(e) => B(e) ? /* @__PURE__ */ N(
|
|
110
|
+
"div",
|
|
111
|
+
{
|
|
112
|
+
"aria-disabled": e.disabled || void 0,
|
|
113
|
+
className: m(
|
|
114
|
+
"flex cursor-pointer select-none items-center gap-2 rounded-sbk-xs px-2.5 py-2 text-sm font-light outline-none",
|
|
115
|
+
"hover:bg-sbk-surface-hover focus:bg-sbk-surface-hover",
|
|
116
|
+
e.danger ? "text-sbk-error-fg" : "text-sbk-fg",
|
|
117
|
+
e.disabled && "pointer-events-none opacity-50",
|
|
118
|
+
"[&_svg]:size-4 [&_svg]:shrink-0"
|
|
119
|
+
),
|
|
120
|
+
onClick: () => {
|
|
121
|
+
var t;
|
|
122
|
+
e.disabled || ((t = e.onSelect) == null || t.call(e), i());
|
|
123
|
+
},
|
|
124
|
+
onKeyDown: (t) => {
|
|
125
|
+
var s;
|
|
126
|
+
(t.key === "Enter" || t.key === " ") && !e.disabled && (t.preventDefault(), (s = e.onSelect) == null || s.call(e), i());
|
|
127
|
+
},
|
|
128
|
+
role: "menuitem",
|
|
129
|
+
tabIndex: -1,
|
|
130
|
+
children: [
|
|
131
|
+
e.icon && /* @__PURE__ */ n("span", { "aria-hidden": "true", className: "inline-flex text-sbk-fg-muted", children: e.icon }),
|
|
132
|
+
/* @__PURE__ */ n("span", { className: "flex-1", children: e.label }),
|
|
133
|
+
e.shortcut && /* @__PURE__ */ n("kbd", { className: "font-sbk text-xs font-light text-sbk-fg-muted", children: e.shortcut })
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
e.id
|
|
137
|
+
) : /* @__PURE__ */ n("div", { className: "my-1 h-px bg-sbk-line", role: "separator" }, e.id)
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
] });
|
|
142
|
+
};
|
|
143
|
+
M.displayName = "DropdownMenu";
|
|
144
|
+
export {
|
|
145
|
+
M as DropdownMenu
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=DropdownMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.js","sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["import type React from 'react';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { cn } from '../../utils/cn';\nimport { buttonReset, focusRing } from '../../utils/styles';\n\nexport interface DropdownMenuItem {\n /** Ação destrutiva (ex.: excluir), exibida em cor de erro. */\n danger?: boolean;\n disabled?: boolean;\n icon?: React.ReactNode;\n id: string;\n label: React.ReactNode;\n onSelect?: () => void;\n /** Atalho exibido à direita (ex.: \"Ctrl+S\"). */\n shortcut?: string;\n type?: 'item';\n}\n\nexport interface DropdownMenuSeparator {\n id: string;\n type: 'separator';\n}\n\nexport type DropdownMenuEntry = DropdownMenuItem | DropdownMenuSeparator;\n\nexport interface DropdownMenuProps {\n align?: 'start' | 'end';\n /** Rótulo acessível do menu quando o gatilho não tem texto. */\n 'aria-label'?: string;\n items: DropdownMenuEntry[];\n /** Conteúdo do botão gatilho. */\n trigger: React.ReactNode;\n triggerClassName?: string;\n variant?: 'default' | 'glass';\n}\n\nconst isItem = (entry: DropdownMenuEntry): entry is DropdownMenuItem => entry.type !== 'separator';\n\n/** Menu de ações (padrão ARIA menu button): setas, Home/End, Enter, Esc e digitação por letra. */\nexport const DropdownMenu = ({\n trigger,\n items,\n align = 'start',\n variant = 'default',\n triggerClassName,\n 'aria-label': ariaLabel,\n}: DropdownMenuProps) => {\n const [open, setOpen] = useState(false);\n const menuId = useId();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const enabledItems = () =>\n Array.from(\n menuRef.current?.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])',\n ) ?? [],\n );\n\n const focusAt = (index: number) => {\n const list = enabledItems();\n if (list.length === 0) return;\n list[(index + list.length) % list.length]?.focus();\n };\n\n const close = (restoreFocus = true) => {\n setOpen(false);\n if (restoreFocus) triggerRef.current?.focus();\n };\n\n useEffect(() => {\n if (!open) return;\n requestAnimationFrame(() =>\n menuRef.current\n ?.querySelector<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n ?.focus(),\n );\n const onClickOutside = (e: MouseEvent) => {\n if (!containerRef.current?.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('mousedown', onClickOutside);\n return () => document.removeEventListener('mousedown', onClickOutside);\n }, [open]);\n\n const onMenuKeyDown = (e: React.KeyboardEvent) => {\n const list = enabledItems();\n const index = list.indexOf(document.activeElement as HTMLElement);\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n focusAt(index + 1);\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n focusAt(index - 1);\n } else if (e.key === 'Home') {\n e.preventDefault();\n focusAt(0);\n } else if (e.key === 'End') {\n e.preventDefault();\n focusAt(list.length - 1);\n } else if (e.key === 'Escape') {\n e.preventDefault();\n close();\n } else if (e.key === 'Tab') {\n close(false);\n } else if (e.key.length === 1 && /\\S/.test(e.key)) {\n const char = e.key.toLocaleLowerCase('pt-BR');\n const match = list.findIndex(\n (el, i) => i > index && el.textContent?.trim().toLocaleLowerCase('pt-BR').startsWith(char),\n );\n const fallback = list.findIndex((el) =>\n el.textContent?.trim().toLocaleLowerCase('pt-BR').startsWith(char),\n );\n const target = match >= 0 ? match : fallback;\n if (target >= 0) focusAt(target);\n }\n };\n\n return (\n <div ref={containerRef} className=\"relative inline-flex\">\n <button\n ref={triggerRef}\n aria-controls={open ? menuId : undefined}\n aria-expanded={open}\n aria-haspopup=\"menu\"\n aria-label={ariaLabel}\n className={cn(\n buttonReset,\n 'inline-flex cursor-pointer items-center',\n focusRing,\n triggerClassName,\n )}\n onClick={() => setOpen((v) => !v)}\n onKeyDown={(e) => {\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {\n e.preventDefault();\n setOpen(true);\n }\n }}\n type=\"button\"\n >\n {trigger}\n </button>\n {open && (\n <div\n ref={menuRef}\n aria-label={ariaLabel}\n className={cn(\n 'absolute top-full z-[var(--sbk-z-dropdown)] mt-1 min-w-48 rounded-sbk-sm p-1 animate-sbk-fade-in',\n variant === 'glass'\n ? 'sbk-glass sbk-glass-strong'\n : 'border border-sbk-line bg-sbk-surface shadow-sbk-md',\n align === 'end' ? 'right-0' : 'left-0',\n )}\n id={menuId}\n onKeyDown={onMenuKeyDown}\n role=\"menu\"\n tabIndex={-1}\n >\n {items.map((entry) =>\n isItem(entry) ? (\n <div\n key={entry.id}\n aria-disabled={entry.disabled || undefined}\n className={cn(\n 'flex cursor-pointer select-none items-center gap-2 rounded-sbk-xs px-2.5 py-2 text-sm font-light outline-none',\n 'hover:bg-sbk-surface-hover focus:bg-sbk-surface-hover',\n entry.danger ? 'text-sbk-error-fg' : 'text-sbk-fg',\n entry.disabled && 'pointer-events-none opacity-50',\n '[&_svg]:size-4 [&_svg]:shrink-0',\n )}\n onClick={() => {\n if (entry.disabled) return;\n entry.onSelect?.();\n close();\n }}\n onKeyDown={(e) => {\n if ((e.key === 'Enter' || e.key === ' ') && !entry.disabled) {\n e.preventDefault();\n entry.onSelect?.();\n close();\n }\n }}\n role=\"menuitem\"\n tabIndex={-1}\n >\n {entry.icon && (\n <span aria-hidden=\"true\" className=\"inline-flex text-sbk-fg-muted\">\n {entry.icon}\n </span>\n )}\n <span className=\"flex-1\">{entry.label}</span>\n {entry.shortcut && (\n <kbd className=\"font-sbk text-xs font-light text-sbk-fg-muted\">\n {entry.shortcut}\n </kbd>\n )}\n </div>\n ) : (\n <div key={entry.id} className=\"my-1 h-px bg-sbk-line\" role=\"separator\" />\n ),\n )}\n </div>\n )}\n </div>\n );\n};\n\nDropdownMenu.displayName = 'DropdownMenu';\n"],"names":["isItem","entry","DropdownMenu","trigger","items","align","variant","triggerClassName","ariaLabel","open","setOpen","useState","menuId","useId","triggerRef","useRef","menuRef","containerRef","enabledItems","_a","focusAt","index","list","close","restoreFocus","useEffect","_b","onClickOutside","e","onMenuKeyDown","char","match","el","i","fallback","target","jsxs","jsx","cn","buttonReset","focusRing","v"],"mappings":"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"}
|
|
@@ -4,5 +4,11 @@ export interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElemen
|
|
|
4
4
|
description?: React.ReactNode;
|
|
5
5
|
icon?: React.ReactNode;
|
|
6
6
|
title: React.ReactNode;
|
|
7
|
+
/** Nível do título. Padrão: h3. */
|
|
8
|
+
titleAs?: 'h2' | 'h3' | 'h4';
|
|
7
9
|
}
|
|
8
|
-
|
|
10
|
+
/** Estado vazio: ausência de dados com orientação do próximo passo. */
|
|
11
|
+
export declare const EmptyState: {
|
|
12
|
+
({ icon, title, titleAs: TitleTag, description, action, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
displayName: string;
|
|
14
|
+
};
|
|
@@ -1,29 +1,38 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { cn as
|
|
3
|
-
const
|
|
4
|
-
icon:
|
|
5
|
-
title:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { jsxs as l, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { cn as n } from "../../utils/cn.js";
|
|
3
|
+
const b = ({
|
|
4
|
+
icon: t,
|
|
5
|
+
title: i,
|
|
6
|
+
titleAs: m = "h3",
|
|
7
|
+
description: s,
|
|
8
|
+
action: r,
|
|
9
|
+
className: d,
|
|
10
|
+
...a
|
|
11
|
+
}) => /* @__PURE__ */ l(
|
|
11
12
|
"div",
|
|
12
13
|
{
|
|
13
|
-
className:
|
|
14
|
-
"grid justify-items-center gap-3 rounded-
|
|
15
|
-
|
|
14
|
+
className: n(
|
|
15
|
+
"grid justify-items-center gap-3 rounded-sbk-md border border-dashed border-sbk-line-strong bg-sbk-surface p-8 text-center",
|
|
16
|
+
d
|
|
16
17
|
),
|
|
17
|
-
...
|
|
18
|
+
...a,
|
|
18
19
|
children: [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
t && /* @__PURE__ */ e(
|
|
21
|
+
"div",
|
|
22
|
+
{
|
|
23
|
+
"aria-hidden": "true",
|
|
24
|
+
className: "inline-flex size-12 items-center justify-center rounded-full bg-sbk-primary-subtle text-sbk-primary [&_svg]:size-6",
|
|
25
|
+
children: t
|
|
26
|
+
}
|
|
27
|
+
),
|
|
28
|
+
/* @__PURE__ */ e(m, { className: "m-0 text-lg font-semibold text-sbk-fg-heading", children: i }),
|
|
29
|
+
s && /* @__PURE__ */ e("p", { className: "m-0 max-w-[30rem] text-sm font-light text-sbk-fg-muted", children: s }),
|
|
30
|
+
r && /* @__PURE__ */ e("div", { className: "mt-1", children: r })
|
|
23
31
|
]
|
|
24
32
|
}
|
|
25
33
|
);
|
|
34
|
+
b.displayName = "EmptyState";
|
|
26
35
|
export {
|
|
27
|
-
|
|
36
|
+
b as EmptyState
|
|
28
37
|
};
|
|
29
38
|
//# sourceMappingURL=EmptyState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmptyState.js","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import type React from 'react';\nimport { cn } from '../../utils/cn';\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n action?: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n title: React.ReactNode;\n}\n\nexport const EmptyState
|
|
1
|
+
{"version":3,"file":"EmptyState.js","sources":["../../../src/components/EmptyState/EmptyState.tsx"],"sourcesContent":["import type React from 'react';\nimport { cn } from '../../utils/cn';\n\nexport interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n action?: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n title: React.ReactNode;\n /** Nível do título. Padrão: h3. */\n titleAs?: 'h2' | 'h3' | 'h4';\n}\n\n/** Estado vazio: ausência de dados com orientação do próximo passo. */\nexport const EmptyState = ({\n icon,\n title,\n titleAs: TitleTag = 'h3',\n description,\n action,\n className,\n ...props\n}: EmptyStateProps) => (\n <div\n className={cn(\n 'grid justify-items-center gap-3 rounded-sbk-md border border-dashed border-sbk-line-strong bg-sbk-surface p-8 text-center',\n className,\n )}\n {...props}\n >\n {icon && (\n <div\n aria-hidden=\"true\"\n className=\"inline-flex size-12 items-center justify-center rounded-full bg-sbk-primary-subtle text-sbk-primary [&_svg]:size-6\"\n >\n {icon}\n </div>\n )}\n <TitleTag className=\"m-0 text-lg font-semibold text-sbk-fg-heading\">{title}</TitleTag>\n {description && (\n <p className=\"m-0 max-w-[30rem] text-sm font-light text-sbk-fg-muted\">{description}</p>\n )}\n {action && <div className=\"mt-1\">{action}</div>}\n </div>\n);\n\nEmptyState.displayName = 'EmptyState';\n"],"names":["EmptyState","icon","title","TitleTag","description","action","className","props","jsxs","cn","jsx"],"mappings":";;AAaO,MAAMA,IAAa,CAAC;AAAA,EACzB,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,SAASC,IAAW;AAAA,EACpB,aAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,GAAGC;AACL,MACE,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAWC;AAAA,MACT;AAAA,MACAH;AAAA,IAAA;AAAA,IAED,GAAGC;AAAA,IAEH,UAAA;AAAA,MAAAN,KACC,gBAAAS;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,eAAY;AAAA,UACZ,WAAU;AAAA,UAET,UAAAT;AAAA,QAAA;AAAA,MAAA;AAAA,MAGL,gBAAAS,EAACP,GAAA,EAAS,WAAU,iDAAiD,UAAAD,GAAM;AAAA,MAC1EE,KACC,gBAAAM,EAAC,KAAA,EAAE,WAAU,0DAA0D,UAAAN,GAAY;AAAA,MAEpFC,KAAU,gBAAAK,EAAC,OAAA,EAAI,WAAU,QAAQ,UAAAL,EAAA,CAAO;AAAA,IAAA;AAAA,EAAA;AAC3C;AAGFL,EAAW,cAAc;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
export interface FieldProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
className?: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
helperText?: string;
|
|
7
|
+
/** id do controle. Os textos de apoio recebem `${id}-helper` e `${id}-error`. */
|
|
8
|
+
id: string;
|
|
9
|
+
label?: React.ReactNode;
|
|
10
|
+
/** Use `span` quando o controle não for rotulável por <label> (ex.: grupo, upload). */
|
|
11
|
+
labelAs?: 'label' | 'span';
|
|
12
|
+
required?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Estrutura comum de campo: rótulo, controle, texto de apoio e erro.
|
|
16
|
+
* Use para compor controles próprios mantendo o padrão visual e de acessibilidade.
|
|
17
|
+
*/
|
|
18
|
+
export declare const Field: {
|
|
19
|
+
({ id, label, labelAs, helperText, error, required, className, children, }: FieldProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
displayName: string;
|
|
21
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsxs as d, jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { fieldLabel as p, fieldError as f, fieldHelper as h } from "../../utils/styles.js";
|
|
4
|
+
const N = ({
|
|
5
|
+
id: e,
|
|
6
|
+
label: l,
|
|
7
|
+
labelAs: a = "label",
|
|
8
|
+
helperText: s,
|
|
9
|
+
error: i,
|
|
10
|
+
required: m,
|
|
11
|
+
className: c,
|
|
12
|
+
children: n
|
|
13
|
+
}) => {
|
|
14
|
+
const o = a;
|
|
15
|
+
return /* @__PURE__ */ d("div", { className: t("grid gap-1.5", c), children: [
|
|
16
|
+
l && /* @__PURE__ */ d(
|
|
17
|
+
o,
|
|
18
|
+
{
|
|
19
|
+
className: p,
|
|
20
|
+
...a === "label" ? { htmlFor: e } : { id: `${e}-label` },
|
|
21
|
+
children: [
|
|
22
|
+
l,
|
|
23
|
+
m && /* @__PURE__ */ r("span", { "aria-hidden": "true", className: "ml-0.5 text-sbk-error-fg", children: "*" })
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
),
|
|
27
|
+
n,
|
|
28
|
+
i ? /* @__PURE__ */ r("span", { className: f, id: `${e}-error`, role: "alert", children: i }) : s && /* @__PURE__ */ r("span", { className: h, id: `${e}-helper`, children: s })
|
|
29
|
+
] });
|
|
30
|
+
};
|
|
31
|
+
N.displayName = "Field";
|
|
32
|
+
export {
|
|
33
|
+
N as Field
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=Field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Field.js","sources":["../../../src/components/Field/Field.tsx"],"sourcesContent":["import type React from 'react';\nimport { cn } from '../../utils/cn';\nimport { fieldError, fieldHelper, fieldLabel } from '../../utils/styles';\n\nexport interface FieldProps {\n children: React.ReactNode;\n className?: string;\n error?: string;\n helperText?: string;\n /** id do controle. Os textos de apoio recebem `${id}-helper` e `${id}-error`. */\n id: string;\n label?: React.ReactNode;\n /** Use `span` quando o controle não for rotulável por <label> (ex.: grupo, upload). */\n labelAs?: 'label' | 'span';\n required?: boolean;\n}\n\n/**\n * Estrutura comum de campo: rótulo, controle, texto de apoio e erro.\n * Use para compor controles próprios mantendo o padrão visual e de acessibilidade.\n */\nexport const Field = ({\n id,\n label,\n labelAs = 'label',\n helperText,\n error,\n required,\n className,\n children,\n}: FieldProps) => {\n const LabelTag = labelAs;\n return (\n <div className={cn('grid gap-1.5', className)}>\n {label && (\n <LabelTag\n className={fieldLabel}\n {...(labelAs === 'label' ? { htmlFor: id } : { id: `${id}-label` })}\n >\n {label}\n {required && (\n <span aria-hidden=\"true\" className=\"ml-0.5 text-sbk-error-fg\">\n *\n </span>\n )}\n </LabelTag>\n )}\n {children}\n {error ? (\n <span className={fieldError} id={`${id}-error`} role=\"alert\">\n {error}\n </span>\n ) : (\n helperText && (\n <span className={fieldHelper} id={`${id}-helper`}>\n {helperText}\n </span>\n )\n )}\n </div>\n );\n};\n\nField.displayName = 'Field';\n"],"names":["Field","id","label","labelAs","helperText","error","required","className","children","LabelTag","cn","jsxs","fieldLabel","jsx","fieldError","fieldHelper"],"mappings":";;;AAqBO,MAAMA,IAAQ,CAAC;AAAA,EACpB,IAAAC;AAAA,EACA,OAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,YAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AACF,MAAkB;AAChB,QAAMC,IAAWN;AACjB,2BACG,OAAA,EAAI,WAAWO,EAAG,gBAAgBH,CAAS,GACzC,UAAA;AAAA,IAAAL,KACC,gBAAAS;AAAA,MAACF;AAAA,MAAA;AAAA,QACC,WAAWG;AAAA,QACV,GAAIT,MAAY,UAAU,EAAE,SAASF,EAAA,IAAO,EAAE,IAAI,GAAGA,CAAE,SAAA;AAAA,QAEvD,UAAA;AAAA,UAAAC;AAAA,UACAI,KACC,gBAAAO,EAAC,QAAA,EAAK,eAAY,QAAO,WAAU,4BAA2B,UAAA,IAAA,CAE9D;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAILL;AAAA,IACAH,sBACE,QAAA,EAAK,WAAWS,GAAY,IAAI,GAAGb,CAAE,UAAU,MAAK,SAClD,aACH,IAEAG,uBACG,QAAA,EAAK,WAAWW,GAAa,IAAI,GAAGd,CAAE,WACpC,UAAAG,EAAA,CACH;AAAA,EAAA,GAGN;AAEJ;AAEAJ,EAAM,cAAc;"}
|
|
@@ -5,9 +5,16 @@ export interface FileUploadProps {
|
|
|
5
5
|
disabled?: boolean;
|
|
6
6
|
error?: string;
|
|
7
7
|
helperText?: string;
|
|
8
|
-
|
|
8
|
+
id?: string;
|
|
9
|
+
label?: React.ReactNode;
|
|
9
10
|
maxSizeBytes?: number;
|
|
10
11
|
multiple?: boolean;
|
|
11
12
|
onChange?: (files: File[]) => void;
|
|
13
|
+
/** Lista os arquivos selecionados com opção de remover. Padrão: true. */
|
|
14
|
+
showFileList?: boolean;
|
|
12
15
|
}
|
|
13
|
-
|
|
16
|
+
/** Área de upload com arrastar e soltar, acessível por teclado. */
|
|
17
|
+
export declare const FileUpload: {
|
|
18
|
+
({ label, helperText, error, accept, multiple, maxSizeBytes, disabled, showFileList, onChange, id, className, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
displayName: string;
|
|
20
|
+
};
|
|
@@ -1,116 +1,142 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useRef as
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { jsxs as l, jsx as r } from "react/jsx-runtime";
|
|
2
|
+
import { useId as U, useRef as w, useState as g } from "react";
|
|
3
|
+
import { UploadCloud as K, FileText as T, X as C } from "lucide-react";
|
|
4
|
+
import { cn as D } from "../../utils/cn.js";
|
|
5
|
+
import { focusRing as F, transitionColors as O, buttonReset as W } from "../../utils/styles.js";
|
|
6
|
+
import { Field as X } from "../Field/Field.js";
|
|
7
|
+
const h = (o) => o >= 1024 * 1024 ? `${(o / 1024 / 1024).toFixed(1)} MB` : `${Math.max(1, Math.round(o / 1024))} KB`, _ = ({
|
|
8
|
+
label: o,
|
|
9
|
+
helperText: k,
|
|
10
|
+
error: z,
|
|
11
|
+
accept: c,
|
|
12
|
+
multiple: x = !1,
|
|
10
13
|
maxSizeBytes: n,
|
|
11
|
-
disabled:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
disabled: s = !1,
|
|
15
|
+
showFileList: I = !0,
|
|
16
|
+
onChange: m,
|
|
17
|
+
id: j,
|
|
18
|
+
className: q
|
|
14
19
|
}) => {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
const A = U(), i = j ?? A, d = w(null), [R, u] = g(!1), [E, v] = g(null), [a, M] = g([]), y = (e) => {
|
|
21
|
+
M(e), m == null || m(e);
|
|
22
|
+
}, N = (e) => {
|
|
23
|
+
if (!e || e.length === 0) return;
|
|
24
|
+
const t = Array.from(e);
|
|
18
25
|
if (n) {
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
const b = t.find((p) => p.size > n);
|
|
27
|
+
if (b) {
|
|
28
|
+
v(
|
|
29
|
+
`Arquivo "${b.name}" excede o tamanho máximo de ${h(n)}.`
|
|
30
|
+
);
|
|
23
31
|
return;
|
|
24
32
|
}
|
|
25
33
|
}
|
|
26
|
-
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
v(null), y(x ? [...a, ...t] : t.slice(0, 1));
|
|
35
|
+
}, f = z ?? E ?? void 0, $ = [
|
|
36
|
+
c && `Formatos aceitos: ${c}`,
|
|
37
|
+
n && `Tamanho máximo: ${h(n)}`
|
|
38
|
+
].filter(Boolean).join(" · ");
|
|
39
|
+
return /* @__PURE__ */ l(
|
|
40
|
+
X,
|
|
41
|
+
{
|
|
42
|
+
className: q,
|
|
43
|
+
error: f,
|
|
44
|
+
helperText: k,
|
|
45
|
+
id: i,
|
|
46
|
+
label: o,
|
|
47
|
+
labelAs: "span",
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ l(
|
|
50
|
+
"div",
|
|
51
|
+
{
|
|
52
|
+
"aria-describedby": f ? `${i}-error` : k ? `${i}-helper` : void 0,
|
|
53
|
+
"aria-disabled": s || void 0,
|
|
54
|
+
"aria-labelledby": o ? `${i}-label` : void 0,
|
|
55
|
+
className: D(
|
|
56
|
+
"flex cursor-pointer flex-col items-center justify-center gap-2 rounded-sbk-md border-2 border-dashed p-8 text-center",
|
|
57
|
+
O,
|
|
58
|
+
F,
|
|
59
|
+
R ? "border-sbk-primary bg-sbk-primary-subtle" : "border-sbk-line-strong bg-sbk-surface hover:border-sbk-primary hover:bg-sbk-surface-hover",
|
|
60
|
+
f && "border-sbk-error",
|
|
61
|
+
s && "pointer-events-none opacity-60"
|
|
62
|
+
),
|
|
63
|
+
onClick: () => {
|
|
64
|
+
var e;
|
|
65
|
+
return !s && ((e = d.current) == null ? void 0 : e.click());
|
|
66
|
+
},
|
|
67
|
+
onDragLeave: () => u(!1),
|
|
68
|
+
onDragOver: (e) => {
|
|
69
|
+
e.preventDefault(), s || u(!0);
|
|
70
|
+
},
|
|
71
|
+
onDrop: (e) => {
|
|
72
|
+
e.preventDefault(), u(!1), s || N(e.dataTransfer.files);
|
|
73
|
+
},
|
|
74
|
+
onKeyDown: (e) => {
|
|
75
|
+
var t;
|
|
76
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), (t = d.current) == null || t.click());
|
|
77
|
+
},
|
|
78
|
+
role: "button",
|
|
79
|
+
tabIndex: s ? -1 : 0,
|
|
80
|
+
children: [
|
|
81
|
+
/* @__PURE__ */ r(K, { "aria-hidden": "true", className: "size-8 text-sbk-primary", strokeWidth: 1.5 }),
|
|
82
|
+
/* @__PURE__ */ l("p", { className: "m-0 text-sm font-light text-sbk-fg", children: [
|
|
83
|
+
"Arraste arquivos aqui ou",
|
|
84
|
+
" ",
|
|
85
|
+
/* @__PURE__ */ r("span", { className: "font-semibold text-sbk-primary underline", children: "clique para selecionar" })
|
|
86
|
+
] }),
|
|
87
|
+
$ && /* @__PURE__ */ r("p", { className: "m-0 text-xs font-light text-sbk-fg-muted", children: $ })
|
|
88
|
+
]
|
|
89
|
+
}
|
|
46
90
|
),
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
91
|
+
/* @__PURE__ */ r(
|
|
92
|
+
"input",
|
|
93
|
+
{
|
|
94
|
+
ref: d,
|
|
95
|
+
accept: c,
|
|
96
|
+
className: "sr-only",
|
|
97
|
+
disabled: s,
|
|
98
|
+
id: i,
|
|
99
|
+
multiple: x,
|
|
100
|
+
onChange: (e) => {
|
|
101
|
+
N(e.target.files), e.target.value = "";
|
|
102
|
+
},
|
|
103
|
+
tabIndex: -1,
|
|
104
|
+
type: "file"
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
I && a.length > 0 && /* @__PURE__ */ r("ul", { "aria-label": "Arquivos selecionados", className: "m-0 grid list-none gap-1.5 p-0", children: a.map((e, t) => /* @__PURE__ */ l(
|
|
108
|
+
"li",
|
|
109
|
+
{
|
|
110
|
+
className: "flex items-center gap-2 rounded-sbk-sm border border-sbk-line bg-sbk-surface px-3 py-2 text-sm",
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ r(T, { "aria-hidden": "true", className: "size-4 shrink-0 text-sbk-fg-muted" }),
|
|
113
|
+
/* @__PURE__ */ r("span", { className: "min-w-0 flex-1 truncate font-light text-sbk-fg", children: e.name }),
|
|
114
|
+
/* @__PURE__ */ r("span", { className: "shrink-0 text-xs font-light text-sbk-fg-muted", children: h(e.size) }),
|
|
115
|
+
/* @__PURE__ */ r(
|
|
116
|
+
"button",
|
|
68
117
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
118
|
+
"aria-label": `Remover ${e.name}`,
|
|
119
|
+
className: D(
|
|
120
|
+
W,
|
|
121
|
+
"inline-flex size-6 cursor-pointer items-center justify-center rounded-sbk-xs text-sbk-fg-muted hover:bg-sbk-surface-hover hover:text-sbk-fg",
|
|
122
|
+
F
|
|
123
|
+
),
|
|
124
|
+
disabled: s,
|
|
125
|
+
onClick: () => y(a.filter((b, p) => p !== t)),
|
|
126
|
+
type: "button",
|
|
127
|
+
children: /* @__PURE__ */ r(C, { "aria-hidden": "true", className: "size-4" })
|
|
72
128
|
}
|
|
73
129
|
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
] }),
|
|
82
|
-
i && /* @__PURE__ */ a("p", { className: "mt-0.5 text-xs text-[var(--text-muted)]", children: [
|
|
83
|
-
"Formatos aceitos: ",
|
|
84
|
-
i
|
|
85
|
-
] }),
|
|
86
|
-
n && /* @__PURE__ */ a("p", { className: "text-xs text-[var(--text-muted)]", children: [
|
|
87
|
-
"Tamanho máximo: ",
|
|
88
|
-
(n / 1024 / 1024).toFixed(0),
|
|
89
|
-
" MB"
|
|
90
|
-
] })
|
|
91
|
-
] })
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
),
|
|
95
|
-
/* @__PURE__ */ t(
|
|
96
|
-
"input",
|
|
97
|
-
{
|
|
98
|
-
ref: c,
|
|
99
|
-
accept: i,
|
|
100
|
-
className: "sr-only",
|
|
101
|
-
disabled: e,
|
|
102
|
-
multiple: D,
|
|
103
|
-
tabIndex: -1,
|
|
104
|
-
type: "file",
|
|
105
|
-
onChange: (r) => p(r.target.files)
|
|
106
|
-
}
|
|
107
|
-
),
|
|
108
|
-
s && /* @__PURE__ */ t("span", { className: "text-[var(--font-size-xs)] text-[var(--md-error)]", role: "alert", children: s }),
|
|
109
|
-
f && !s && /* @__PURE__ */ t("span", { className: "text-[var(--font-size-xs)] text-[var(--md-on-surface-variant)]", children: f })
|
|
110
|
-
] });
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
`${e.name}-${t}`
|
|
133
|
+
)) })
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
);
|
|
111
137
|
};
|
|
112
|
-
|
|
138
|
+
_.displayName = "FileUpload";
|
|
113
139
|
export {
|
|
114
|
-
|
|
140
|
+
_ as FileUpload
|
|
115
141
|
};
|
|
116
142
|
//# sourceMappingURL=FileUpload.js.map
|