@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.
Files changed (210) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/MIGRATION.md +96 -0
  3. package/README.md +221 -115
  4. package/dist/assets/favicon/apple-touch-icon.png +0 -0
  5. package/dist/assets/favicon/favicon-96x96.png +0 -0
  6. package/dist/assets/favicon/favicon.ico +0 -0
  7. package/dist/assets/favicon/favicon.svg +1 -0
  8. package/dist/assets/favicon/site.webmanifest +21 -0
  9. package/dist/assets/favicon/web-app-manifest-192x192.png +0 -0
  10. package/dist/assets/favicon/web-app-manifest-512x512.png +0 -0
  11. package/dist/base.css +38 -0
  12. package/dist/components/Accordion/Accordion.d.ts +13 -1
  13. package/dist/components/Accordion/Accordion.js +100 -52
  14. package/dist/components/Accordion/Accordion.js.map +1 -1
  15. package/dist/components/Alert/Alert.d.ts +17 -4
  16. package/dist/components/Alert/Alert.js +64 -20
  17. package/dist/components/Alert/Alert.js.map +1 -1
  18. package/dist/components/Avatar/Avatar.d.ts +12 -6
  19. package/dist/components/Avatar/Avatar.js +66 -0
  20. package/dist/components/Avatar/Avatar.js.map +1 -0
  21. package/dist/components/Badge/Badge.d.ts +11 -2
  22. package/dist/components/Badge/Badge.js +42 -30
  23. package/dist/components/Badge/Badge.js.map +1 -1
  24. package/dist/components/Breadcrumb/Breadcrumb.d.ts +5 -2
  25. package/dist/components/Breadcrumb/Breadcrumb.js +23 -16
  26. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -1
  27. package/dist/components/Button/Button.d.ts +10 -8
  28. package/dist/components/Button/Button.js +73 -114
  29. package/dist/components/Button/Button.js.map +1 -1
  30. package/dist/components/Card/Card.d.ts +33 -3
  31. package/dist/components/Card/Card.js +91 -30
  32. package/dist/components/Card/Card.js.map +1 -1
  33. package/dist/components/Checkbox/Checkbox.d.ts +4 -1
  34. package/dist/components/Checkbox/Checkbox.js +32 -31
  35. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  36. package/dist/components/Chip/Chip.d.ts +7 -1
  37. package/dist/components/Chip/Chip.js +19 -9
  38. package/dist/components/Chip/Chip.js.map +1 -1
  39. package/dist/components/Combobox/Combobox.d.ts +10 -2
  40. package/dist/components/Combobox/Combobox.js +146 -133
  41. package/dist/components/Combobox/Combobox.js.map +1 -1
  42. package/dist/components/Divider/Divider.d.ts +7 -1
  43. package/dist/components/Divider/Divider.js +28 -12
  44. package/dist/components/Divider/Divider.js.map +1 -1
  45. package/dist/components/DropdownMenu/DropdownMenu.d.ts +33 -0
  46. package/dist/components/DropdownMenu/DropdownMenu.js +147 -0
  47. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
  48. package/dist/components/EmptyState/EmptyState.d.ts +7 -1
  49. package/dist/components/EmptyState/EmptyState.js +28 -19
  50. package/dist/components/EmptyState/EmptyState.js.map +1 -1
  51. package/dist/components/Field/Field.d.ts +21 -0
  52. package/dist/components/Field/Field.js +35 -0
  53. package/dist/components/Field/Field.js.map +1 -0
  54. package/dist/components/FileUpload/FileUpload.d.ts +9 -2
  55. package/dist/components/FileUpload/FileUpload.js +128 -102
  56. package/dist/components/FileUpload/FileUpload.js.map +1 -1
  57. package/dist/components/Footer/Footer.d.ts +19 -6
  58. package/dist/components/Footer/Footer.js +47 -115
  59. package/dist/components/Footer/Footer.js.map +1 -1
  60. package/dist/components/GlassPanel/GlassPanel.d.ts +18 -0
  61. package/dist/components/GlassPanel/GlassPanel.js +39 -0
  62. package/dist/components/GlassPanel/GlassPanel.js.map +1 -0
  63. package/dist/components/Header/Header.d.ts +25 -14
  64. package/dist/components/Header/Header.js +88 -148
  65. package/dist/components/Header/Header.js.map +1 -1
  66. package/dist/components/Highlight/Highlight.d.ts +13 -0
  67. package/dist/components/Highlight/Highlight.js +18 -0
  68. package/dist/components/Highlight/Highlight.js.map +1 -0
  69. package/dist/components/Icon/Icon.d.ts +21 -0
  70. package/dist/components/Icon/Icon.js +31 -0
  71. package/dist/components/Icon/Icon.js.map +1 -0
  72. package/dist/components/IconButton/IconButton.d.ts +12 -0
  73. package/dist/components/IconButton/IconButton.js +49 -0
  74. package/dist/components/IconButton/IconButton.js.map +1 -0
  75. package/dist/components/Input/Input.d.ts +3 -2
  76. package/dist/components/Input/Input.js +51 -69
  77. package/dist/components/Input/Input.js.map +1 -1
  78. package/dist/components/Layout/Layout.d.ts +2 -0
  79. package/dist/components/Layout/Layout.js +24 -18
  80. package/dist/components/Layout/Layout.js.map +1 -1
  81. package/dist/components/Logo/Logo.d.ts +27 -0
  82. package/dist/components/Logo/Logo.js +107 -0
  83. package/dist/components/Logo/Logo.js.map +1 -0
  84. package/dist/components/MiniCard/MiniCard.d.ts +21 -0
  85. package/dist/components/MiniCard/MiniCard.js +90 -0
  86. package/dist/components/MiniCard/MiniCard.js.map +1 -0
  87. package/dist/components/Modal/Modal.d.ts +16 -3
  88. package/dist/components/Modal/Modal.js +124 -93
  89. package/dist/components/Modal/Modal.js.map +1 -1
  90. package/dist/components/Navigation/Navigation.d.ts +1 -0
  91. package/dist/components/Navigation/Navigation.js +14 -14
  92. package/dist/components/Navigation/Navigation.js.map +1 -1
  93. package/dist/components/NumberInput/NumberInput.d.ts +5 -2
  94. package/dist/components/NumberInput/NumberInput.js +98 -128
  95. package/dist/components/NumberInput/NumberInput.js.map +1 -1
  96. package/dist/components/PageHeader/PageHeader.d.ts +8 -1
  97. package/dist/components/PageHeader/PageHeader.js +26 -25
  98. package/dist/components/PageHeader/PageHeader.js.map +1 -1
  99. package/dist/components/Pagination/Pagination.d.ts +15 -2
  100. package/dist/components/Pagination/Pagination.js +68 -34
  101. package/dist/components/Pagination/Pagination.js.map +1 -1
  102. package/dist/components/Paragraph/Paragraph.d.ts +8 -2
  103. package/dist/components/Paragraph/Paragraph.js +39 -36
  104. package/dist/components/Paragraph/Paragraph.js.map +1 -1
  105. package/dist/components/Popover/Popover.d.ts +12 -1
  106. package/dist/components/Popover/Popover.js +49 -37
  107. package/dist/components/Popover/Popover.js.map +1 -1
  108. package/dist/components/ProgressBar/ProgressBar.d.ts +12 -6
  109. package/dist/components/ProgressBar/ProgressBar.js +71 -0
  110. package/dist/components/ProgressBar/ProgressBar.js.map +1 -0
  111. package/dist/components/Radio/Radio.d.ts +2 -1
  112. package/dist/components/Radio/Radio.js +27 -31
  113. package/dist/components/Radio/Radio.js.map +1 -1
  114. package/dist/components/SectionHeader/SectionHeader.d.ts +6 -1
  115. package/dist/components/SectionHeader/SectionHeader.js +14 -12
  116. package/dist/components/SectionHeader/SectionHeader.js.map +1 -1
  117. package/dist/components/Select/Select.d.ts +6 -2
  118. package/dist/components/Select/Select.js +47 -35
  119. package/dist/components/Select/Select.js.map +1 -1
  120. package/dist/components/SideNav/SideNav.d.ts +55 -0
  121. package/dist/components/SideNav/SideNav.js +141 -0
  122. package/dist/components/SideNav/SideNav.js.map +1 -0
  123. package/dist/components/Skeleton/Skeleton.d.ts +12 -1
  124. package/dist/components/Skeleton/Skeleton.js +33 -18
  125. package/dist/components/Skeleton/Skeleton.js.map +1 -1
  126. package/dist/components/Spinner/Spinner.d.ts +11 -3
  127. package/dist/components/Spinner/Spinner.js +57 -0
  128. package/dist/components/Spinner/Spinner.js.map +1 -0
  129. package/dist/components/Stepper/Stepper.d.ts +1 -1
  130. package/dist/components/Stepper/Stepper.js +41 -53
  131. package/dist/components/Stepper/Stepper.js.map +1 -1
  132. package/dist/components/Subtitle/Subtitle.d.ts +9 -3
  133. package/dist/components/Subtitle/Subtitle.js +29 -35
  134. package/dist/components/Subtitle/Subtitle.js.map +1 -1
  135. package/dist/components/Switch/Switch.d.ts +2 -1
  136. package/dist/components/Switch/Switch.js +45 -33
  137. package/dist/components/Switch/Switch.js.map +1 -1
  138. package/dist/components/Table/Table.d.ts +19 -1
  139. package/dist/components/Table/Table.js +48 -11
  140. package/dist/components/Table/Table.js.map +1 -1
  141. package/dist/components/Tabs/Tabs.d.ts +16 -2
  142. package/dist/components/Tabs/Tabs.js +80 -44
  143. package/dist/components/Tabs/Tabs.js.map +1 -1
  144. package/dist/components/Tag/Tag.d.ts +8 -1
  145. package/dist/components/Tag/Tag.js +27 -12
  146. package/dist/components/Tag/Tag.js.map +1 -1
  147. package/dist/components/Textarea/Textarea.d.ts +3 -1
  148. package/dist/components/Textarea/Textarea.js +36 -40
  149. package/dist/components/Textarea/Textarea.js.map +1 -1
  150. package/dist/components/Title/Title.d.ts +9 -3
  151. package/dist/components/Title/Title.js +42 -42
  152. package/dist/components/Title/Title.js.map +1 -1
  153. package/dist/components/Toast/Toast.d.ts +39 -11
  154. package/dist/components/Toast/Toast.js +130 -76
  155. package/dist/components/Toast/Toast.js.map +1 -1
  156. package/dist/components/Tooltip/Tooltip.d.ts +12 -2
  157. package/dist/components/Tooltip/Tooltip.js +50 -32
  158. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  159. package/dist/components/Typography/weight.d.ts +9 -0
  160. package/dist/components/Typography/weight.js +5 -0
  161. package/dist/components/Typography/weight.js.map +1 -0
  162. package/dist/fonts.css +8 -0
  163. package/dist/index.cjs +2 -0
  164. package/dist/index.cjs.map +1 -0
  165. package/dist/index.d.ts +55 -43
  166. package/dist/index.js +118 -83
  167. package/dist/index.js.map +1 -1
  168. package/dist/reset.css +9 -0
  169. package/dist/styles.css +1 -0
  170. package/dist/tailwind.css +153 -0
  171. package/dist/theme/ThemeProvider.d.ts +21 -3
  172. package/dist/theme/ThemeProvider.js +57 -18
  173. package/dist/theme/ThemeProvider.js.map +1 -1
  174. package/dist/theme/tokens.d.ts +43 -0
  175. package/dist/theme/tokens.js +39 -0
  176. package/dist/theme/tokens.js.map +1 -0
  177. package/dist/theme.css +296 -263
  178. package/dist/tokens.css +327 -0
  179. package/dist/utils/cn.d.ts +2 -2
  180. package/dist/utils/cn.js +19 -4
  181. package/dist/utils/cn.js.map +1 -1
  182. package/dist/utils/styles.d.ts +37 -0
  183. package/dist/utils/styles.js +35 -0
  184. package/dist/utils/styles.js.map +1 -0
  185. package/package.json +140 -117
  186. package/dist/components/Accordion/Accordion.stories.d.ts +0 -7
  187. package/dist/components/Alert/Alert.stories.d.ts +0 -8
  188. package/dist/components/Badge/Badge.stories.d.ts +0 -8
  189. package/dist/components/Breadcrumb/Breadcrumb.stories.d.ts +0 -8
  190. package/dist/components/Button/Button.stories.d.ts +0 -7
  191. package/dist/components/Card/Card.stories.d.ts +0 -8
  192. package/dist/components/Checkbox/Checkbox.stories.d.ts +0 -7
  193. package/dist/components/Combobox/Combobox.stories.d.ts +0 -9
  194. package/dist/components/Feedback/Feedback.stories.d.ts +0 -5
  195. package/dist/components/FileUpload/FileUpload.stories.d.ts +0 -10
  196. package/dist/components/Input/Input.stories.d.ts +0 -9
  197. package/dist/components/Modal/Modal.stories.d.ts +0 -8
  198. package/dist/components/NumberInput/NumberInput.stories.d.ts +0 -8
  199. package/dist/components/Pagination/Pagination.stories.d.ts +0 -8
  200. package/dist/components/Radio/Radio.stories.d.ts +0 -7
  201. package/dist/components/Select/Select.stories.d.ts +0 -8
  202. package/dist/components/Stepper/Stepper.stories.d.ts +0 -8
  203. package/dist/components/Switch/Switch.stories.d.ts +0 -8
  204. package/dist/components/Tabs/Tabs.stories.d.ts +0 -8
  205. package/dist/components/Textarea/Textarea.stories.d.ts +0 -7
  206. package/dist/components/Toast/Toast.stories.d.ts +0 -8
  207. package/dist/oficios-ui.css +0 -1
  208. package/dist/oficios-ui.umd.cjs +0 -2
  209. package/dist/oficios-ui.umd.cjs.map +0 -1
  210. 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
- export declare const EmptyState: React.FC<EmptyStateProps>;
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 o, jsx as e } from "react/jsx-runtime";
2
- import { cn as c } from "../../utils/cn.js";
3
- const i = ({
4
- icon: r,
5
- title: d,
6
- description: t,
7
- action: a,
8
- className: s,
9
- ...m
10
- }) => /* @__PURE__ */ o(
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: c(
14
- "grid justify-items-center gap-3 rounded-[var(--border-radius-lg)] border border-dashed border-[var(--border-color)] bg-[var(--bg-secondary)] p-6 text-center",
15
- s
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
- ...m,
18
+ ...a,
18
19
  children: [
19
- r && /* @__PURE__ */ e("div", { className: "text-[var(--text-muted)]", children: r }),
20
- /* @__PURE__ */ e("h3", { className: "m-0 text-lg font-semibold text-[var(--text-primary)]", children: d }),
21
- t && /* @__PURE__ */ e("p", { className: "m-0 max-w-[30rem] text-sm text-[var(--text-secondary)]", children: t }),
22
- a && /* @__PURE__ */ e("div", { className: "mt-1", children: a })
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
- i as EmptyState
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: React.FC<EmptyStateProps> = ({\n icon,\n title,\n description,\n action,\n className,\n ...props\n}) => {\n return (\n <div\n className={cn(\n 'grid justify-items-center gap-3 rounded-[var(--border-radius-lg)] border border-dashed border-[var(--border-color)] bg-[var(--bg-secondary)] p-6 text-center',\n className,\n )}\n {...props}\n >\n {icon && <div className=\"text-[var(--text-muted)]\">{icon}</div>}\n <h3 className=\"m-0 text-lg font-semibold text-[var(--text-primary)]\">{title}</h3>\n {description && (\n <p className=\"m-0 max-w-[30rem] text-sm text-[var(--text-secondary)]\">{description}</p>\n )}\n {action && <div className=\"mt-1\">{action}</div>}\n </div>\n );\n};\n"],"names":["EmptyState","icon","title","description","action","className","props","jsxs","cn","jsx"],"mappings":";;AAUO,MAAMA,IAAwC,CAAC;AAAA,EACpD,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,aAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,GAAGC;AACL,MAEI,gBAAAC;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAWC;AAAA,MACT;AAAA,MACAH;AAAA,IAAA;AAAA,IAED,GAAGC;AAAA,IAEH,UAAA;AAAA,MAAAL,KAAQ,gBAAAQ,EAAC,OAAA,EAAI,WAAU,4BAA4B,UAAAR,GAAK;AAAA,MACzD,gBAAAQ,EAAC,MAAA,EAAG,WAAU,wDAAwD,UAAAP,GAAM;AAAA,MAC3EC,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;AAAA;"}
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
- label?: string;
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
- export declare const FileUpload: React.FC<FileUploadProps>;
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 a, jsx as t } from "react/jsx-runtime";
2
- import { useRef as q, useState as h } from "react";
3
- import { cn as g } from "../../utils/cn.js";
4
- const E = ({
5
- label: v,
6
- helperText: f,
7
- error: y,
8
- accept: i,
9
- multiple: D = !1,
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: e = !1,
12
- onChange: l,
13
- className: N
14
+ disabled: s = !1,
15
+ showFileList: I = !0,
16
+ onChange: m,
17
+ id: j,
18
+ className: q
14
19
  }) => {
15
- const c = q(null), [b, d] = h(!1), [k, u] = h(null), p = (r) => {
16
- if (!r) return;
17
- const o = Array.from(r);
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 x = o.find((m) => m.size > n);
20
- if (x) {
21
- const m = (n / 1024 / 1024).toFixed(0);
22
- u(`Arquivo "${x.name}" excede o tamanho máximo de ${m} MB.`);
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
- u(null), l == null || l(o);
27
- }, w = (r) => {
28
- r.preventDefault(), e || d(!0);
29
- }, F = () => d(!1), A = (r) => {
30
- r.preventDefault(), d(!1), e || p(r.dataTransfer.files);
31
- }, j = (r) => {
32
- var o;
33
- (r.key === "Enter" || r.key === " ") && (r.preventDefault(), (o = c.current) == null || o.click());
34
- }, s = y ?? k;
35
- return /* @__PURE__ */ a("div", { className: g("flex flex-col gap-[var(--spacing-xs)]", N), children: [
36
- v && /* @__PURE__ */ t("span", { className: "text-[var(--font-size-sm)] font-[var(--font-weight-semibold)] text-[var(--md-on-surface)]", children: v }),
37
- /* @__PURE__ */ a(
38
- "div",
39
- {
40
- "aria-disabled": e,
41
- className: g(
42
- "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-[var(--border-radius-lg)] border-2 border-dashed p-8 text-center transition",
43
- b ? "border-[var(--md-primary)] bg-[var(--md-primary-container)]" : "border-[var(--md-outline-variant)] hover:border-[var(--md-primary)] hover:bg-[var(--bg-tertiary)]",
44
- s && "border-[var(--md-error)]",
45
- e && "pointer-events-none cursor-not-allowed opacity-60"
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
- onClick: () => {
48
- var r;
49
- return !e && ((r = c.current) == null ? void 0 : r.click());
50
- },
51
- onDragLeave: F,
52
- onDragOver: w,
53
- onDrop: A,
54
- onKeyDown: j,
55
- role: "button",
56
- tabIndex: e ? -1 : 0,
57
- children: [
58
- /* @__PURE__ */ t(
59
- "svg",
60
- {
61
- className: "h-8 w-8 text-[var(--md-primary)]",
62
- fill: "none",
63
- stroke: "currentColor",
64
- strokeWidth: "1.5",
65
- viewBox: "0 0 24 24",
66
- children: /* @__PURE__ */ t(
67
- "path",
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
- d: "M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5",
70
- strokeLinecap: "round",
71
- strokeLinejoin: "round"
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
- /* @__PURE__ */ a("div", { children: [
77
- /* @__PURE__ */ a("p", { className: "text-sm font-medium text-[var(--text-primary)]", children: [
78
- "Arraste arquivos aqui ou",
79
- " ",
80
- /* @__PURE__ */ t("span", { className: "text-[var(--md-primary)] underline", children: "clique para selecionar" })
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
- E.displayName = "FileUpload";
138
+ _.displayName = "FileUpload";
113
139
  export {
114
- E as FileUpload
140
+ _ as FileUpload
115
141
  };
116
142
  //# sourceMappingURL=FileUpload.js.map