@rojaostudio/ds 1.0.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 (291) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -0
  3. package/brand/rojao-black-on-white.png +0 -0
  4. package/brand/rojao-black.png +0 -0
  5. package/brand/rojao-white-on-black.png +0 -0
  6. package/brand/rojao-white.png +0 -0
  7. package/dist/chunk-ORMEWXMH.js +37 -0
  8. package/dist/compat/generate.d.ts +6 -0
  9. package/dist/compat/generate.js +1 -0
  10. package/dist/compat/recipes.d.ts +1 -0
  11. package/dist/compat/recipes.js +1 -0
  12. package/dist/compat/themes.d.ts +1 -0
  13. package/dist/compat/themes.js +1 -0
  14. package/dist/compat/tokens.d.ts +1 -0
  15. package/dist/compat/tokens.js +1 -0
  16. package/dist/components/accordion.d.ts +35 -0
  17. package/dist/components/accordion.js +95 -0
  18. package/dist/components/alert.d.ts +24 -0
  19. package/dist/components/alert.js +72 -0
  20. package/dist/components/avatar.d.ts +45 -0
  21. package/dist/components/avatar.js +97 -0
  22. package/dist/components/badge.d.ts +45 -0
  23. package/dist/components/badge.js +114 -0
  24. package/dist/components/blocker-card.d.ts +42 -0
  25. package/dist/components/blocker-card.js +45 -0
  26. package/dist/components/bottom-sheet.d.ts +31 -0
  27. package/dist/components/bottom-sheet.js +120 -0
  28. package/dist/components/breadcrumb.d.ts +28 -0
  29. package/dist/components/breadcrumb.js +52 -0
  30. package/dist/components/button.d.ts +27 -0
  31. package/dist/components/button.js +124 -0
  32. package/dist/components/card.d.ts +27 -0
  33. package/dist/components/card.js +52 -0
  34. package/dist/components/chat-bubble.d.ts +16 -0
  35. package/dist/components/chat-bubble.js +55 -0
  36. package/dist/components/checkbox.d.ts +14 -0
  37. package/dist/components/checkbox.js +78 -0
  38. package/dist/components/chip-input.d.ts +26 -0
  39. package/dist/components/chip-input.js +182 -0
  40. package/dist/components/chips.d.ts +22 -0
  41. package/dist/components/chips.js +94 -0
  42. package/dist/components/choice-card.d.ts +16 -0
  43. package/dist/components/choice-card.js +60 -0
  44. package/dist/components/choice-carousel.d.ts +16 -0
  45. package/dist/components/choice-carousel.js +154 -0
  46. package/dist/components/choice-preview-card.d.ts +37 -0
  47. package/dist/components/choice-preview-card.js +39 -0
  48. package/dist/components/color-input.d.ts +20 -0
  49. package/dist/components/color-input.js +79 -0
  50. package/dist/components/combobox.d.ts +70 -0
  51. package/dist/components/combobox.js +213 -0
  52. package/dist/components/context-menu.d.ts +33 -0
  53. package/dist/components/context-menu.js +70 -0
  54. package/dist/components/copilot-hint.d.ts +18 -0
  55. package/dist/components/copilot-hint.js +63 -0
  56. package/dist/components/copy-field.d.ts +16 -0
  57. package/dist/components/copy-field.js +41 -0
  58. package/dist/components/currency-input.d.ts +32 -0
  59. package/dist/components/currency-input.js +138 -0
  60. package/dist/components/danger-zone.d.ts +20 -0
  61. package/dist/components/danger-zone.js +21 -0
  62. package/dist/components/data-table-header.d.ts +43 -0
  63. package/dist/components/data-table-header.js +205 -0
  64. package/dist/components/date-picker.d.ts +40 -0
  65. package/dist/components/date-picker.js +252 -0
  66. package/dist/components/divider.d.ts +20 -0
  67. package/dist/components/divider.js +47 -0
  68. package/dist/components/drawer.d.ts +26 -0
  69. package/dist/components/drawer.js +82 -0
  70. package/dist/components/dropzone.d.ts +19 -0
  71. package/dist/components/dropzone.js +65 -0
  72. package/dist/components/empty-state.d.ts +48 -0
  73. package/dist/components/empty-state.js +91 -0
  74. package/dist/components/fab.d.ts +38 -0
  75. package/dist/components/fab.js +100 -0
  76. package/dist/components/filter-chip.d.ts +54 -0
  77. package/dist/components/filter-chip.js +66 -0
  78. package/dist/components/floating-stepper.d.ts +44 -0
  79. package/dist/components/floating-stepper.js +53 -0
  80. package/dist/components/form-card-header.d.ts +13 -0
  81. package/dist/components/form-card-header.js +8 -0
  82. package/dist/components/icon-button.d.ts +17 -0
  83. package/dist/components/icon-button.js +80 -0
  84. package/dist/components/image-crop-modal.d.ts +59 -0
  85. package/dist/components/image-crop-modal.js +240 -0
  86. package/dist/components/image-upload.d.ts +72 -0
  87. package/dist/components/image-upload.js +271 -0
  88. package/dist/components/index.d.ts +85 -0
  89. package/dist/components/index.js +200 -0
  90. package/dist/components/input.d.ts +25 -0
  91. package/dist/components/input.js +136 -0
  92. package/dist/components/insight-card.d.ts +19 -0
  93. package/dist/components/insight-card.js +27 -0
  94. package/dist/components/label.d.ts +16 -0
  95. package/dist/components/label.js +43 -0
  96. package/dist/components/media-tile.d.ts +24 -0
  97. package/dist/components/media-tile.js +28 -0
  98. package/dist/components/menu.d.ts +50 -0
  99. package/dist/components/menu.js +80 -0
  100. package/dist/components/modal.d.ts +25 -0
  101. package/dist/components/modal.js +71 -0
  102. package/dist/components/notice.d.ts +50 -0
  103. package/dist/components/notice.js +103 -0
  104. package/dist/components/option-tile.d.ts +60 -0
  105. package/dist/components/option-tile.js +144 -0
  106. package/dist/components/page-shell.d.ts +29 -0
  107. package/dist/components/page-shell.js +36 -0
  108. package/dist/components/page-skeleton.d.ts +35 -0
  109. package/dist/components/page-skeleton.js +12 -0
  110. package/dist/components/page-tabs.d.ts +35 -0
  111. package/dist/components/page-tabs.js +55 -0
  112. package/dist/components/pagination.d.ts +31 -0
  113. package/dist/components/pagination.js +80 -0
  114. package/dist/components/paywall-banner.d.ts +37 -0
  115. package/dist/components/paywall-banner.js +42 -0
  116. package/dist/components/paywall.d.ts +44 -0
  117. package/dist/components/paywall.js +55 -0
  118. package/dist/components/phone-frame.d.ts +9 -0
  119. package/dist/components/phone-frame.js +18 -0
  120. package/dist/components/phone-input.d.ts +24 -0
  121. package/dist/components/phone-input.js +92 -0
  122. package/dist/components/pix-icon.d.ts +8 -0
  123. package/dist/components/pix-icon.js +24 -0
  124. package/dist/components/popover.d.ts +44 -0
  125. package/dist/components/popover.js +140 -0
  126. package/dist/components/pricing-card.d.ts +42 -0
  127. package/dist/components/pricing-card.js +29 -0
  128. package/dist/components/qr-display.d.ts +19 -0
  129. package/dist/components/qr-display.js +24 -0
  130. package/dist/components/radio.d.ts +33 -0
  131. package/dist/components/radio.js +97 -0
  132. package/dist/components/row-actions.d.ts +24 -0
  133. package/dist/components/row-actions.js +76 -0
  134. package/dist/components/saving-bar-context.d.ts +40 -0
  135. package/dist/components/saving-bar-context.js +125 -0
  136. package/dist/components/saving-bar.d.ts +18 -0
  137. package/dist/components/saving-bar.js +72 -0
  138. package/dist/components/search.d.ts +30 -0
  139. package/dist/components/search.js +81 -0
  140. package/dist/components/section-card.d.ts +14 -0
  141. package/dist/components/section-card.js +49 -0
  142. package/dist/components/section-header.d.ts +29 -0
  143. package/dist/components/section-header.js +7 -0
  144. package/dist/components/select.d.ts +21 -0
  145. package/dist/components/select.js +140 -0
  146. package/dist/components/selectable-card.d.ts +35 -0
  147. package/dist/components/selectable-card.js +27 -0
  148. package/dist/components/setting-row.d.ts +33 -0
  149. package/dist/components/setting-row.js +19 -0
  150. package/dist/components/settings-list.d.ts +25 -0
  151. package/dist/components/settings-list.js +22 -0
  152. package/dist/components/skeleton.d.ts +24 -0
  153. package/dist/components/skeleton.js +49 -0
  154. package/dist/components/slider.d.ts +32 -0
  155. package/dist/components/slider.js +79 -0
  156. package/dist/components/spinner.d.ts +14 -0
  157. package/dist/components/spinner.js +36 -0
  158. package/dist/components/step-progress.d.ts +12 -0
  159. package/dist/components/step-progress.js +24 -0
  160. package/dist/components/summary-bar.d.ts +31 -0
  161. package/dist/components/summary-bar.js +54 -0
  162. package/dist/components/table.d.ts +23 -0
  163. package/dist/components/table.js +80 -0
  164. package/dist/components/textarea.d.ts +21 -0
  165. package/dist/components/textarea.js +95 -0
  166. package/dist/components/themed.d.ts +36 -0
  167. package/dist/components/themed.js +22 -0
  168. package/dist/components/time-picker.d.ts +34 -0
  169. package/dist/components/time-picker.js +213 -0
  170. package/dist/components/toaster.d.ts +84 -0
  171. package/dist/components/toaster.js +284 -0
  172. package/dist/components/toggle-card-compact.d.ts +21 -0
  173. package/dist/components/toggle-card-compact.js +99 -0
  174. package/dist/components/toggle-card.d.ts +15 -0
  175. package/dist/components/toggle-card.js +76 -0
  176. package/dist/components/toggle-group.d.ts +67 -0
  177. package/dist/components/toggle-group.js +80 -0
  178. package/dist/components/toggle.d.ts +13 -0
  179. package/dist/components/toggle.js +64 -0
  180. package/dist/components/tooltip.d.ts +17 -0
  181. package/dist/components/tooltip.js +89 -0
  182. package/dist/components/typing-indicator.d.ts +8 -0
  183. package/dist/components/typing-indicator.js +27 -0
  184. package/dist/components/use-dismiss.d.ts +9 -0
  185. package/dist/components/use-dismiss.js +28 -0
  186. package/dist/components/use-focus-trap.d.ts +5 -0
  187. package/dist/components/use-focus-trap.js +54 -0
  188. package/dist/icons/index.d.ts +1 -0
  189. package/dist/icons/index.js +89 -0
  190. package/dist/targets/native/components/accordion.d.ts +18 -0
  191. package/dist/targets/native/components/accordion.js +122 -0
  192. package/dist/targets/native/components/alert.d.ts +16 -0
  193. package/dist/targets/native/components/alert.js +58 -0
  194. package/dist/targets/native/components/avatar.d.ts +25 -0
  195. package/dist/targets/native/components/avatar.js +88 -0
  196. package/dist/targets/native/components/badge.d.ts +27 -0
  197. package/dist/targets/native/components/badge.js +59 -0
  198. package/dist/targets/native/components/button.d.ts +18 -0
  199. package/dist/targets/native/components/button.js +84 -0
  200. package/dist/targets/native/components/card.d.ts +6 -0
  201. package/dist/targets/native/components/card.js +36 -0
  202. package/dist/targets/native/components/checkbox.d.ts +12 -0
  203. package/dist/targets/native/components/checkbox.js +59 -0
  204. package/dist/targets/native/components/chips.d.ts +17 -0
  205. package/dist/targets/native/components/chips.js +77 -0
  206. package/dist/targets/native/components/choice-card.d.ts +11 -0
  207. package/dist/targets/native/components/choice-card.js +52 -0
  208. package/dist/targets/native/components/context-menu.d.ts +11 -0
  209. package/dist/targets/native/components/context-menu.js +19 -0
  210. package/dist/targets/native/components/currency-input.d.ts +22 -0
  211. package/dist/targets/native/components/currency-input.js +105 -0
  212. package/dist/targets/native/components/danger-zone.d.ts +13 -0
  213. package/dist/targets/native/components/danger-zone.js +27 -0
  214. package/dist/targets/native/components/date-picker.d.ts +13 -0
  215. package/dist/targets/native/components/date-picker.js +185 -0
  216. package/dist/targets/native/components/divider.d.ts +9 -0
  217. package/dist/targets/native/components/divider.js +36 -0
  218. package/dist/targets/native/components/empty-state.d.ts +13 -0
  219. package/dist/targets/native/components/empty-state.js +10 -0
  220. package/dist/targets/native/components/fab.d.ts +25 -0
  221. package/dist/targets/native/components/fab.js +112 -0
  222. package/dist/targets/native/components/filter-chip.d.ts +22 -0
  223. package/dist/targets/native/components/filter-chip.js +70 -0
  224. package/dist/targets/native/components/form-field.d.ts +13 -0
  225. package/dist/targets/native/components/form-field.js +29 -0
  226. package/dist/targets/native/components/form-screen.d.ts +19 -0
  227. package/dist/targets/native/components/form-screen.js +88 -0
  228. package/dist/targets/native/components/google-icon.d.ts +8 -0
  229. package/dist/targets/native/components/google-icon.js +32 -0
  230. package/dist/targets/native/components/icon-button.d.ts +18 -0
  231. package/dist/targets/native/components/icon-button.js +80 -0
  232. package/dist/targets/native/components/icon.d.ts +11 -0
  233. package/dist/targets/native/components/icon.js +9 -0
  234. package/dist/targets/native/components/image-upload.d.ts +12 -0
  235. package/dist/targets/native/components/image-upload.js +95 -0
  236. package/dist/targets/native/components/index.d.ts +45 -0
  237. package/dist/targets/native/components/index.js +100 -0
  238. package/dist/targets/native/components/input.d.ts +17 -0
  239. package/dist/targets/native/components/input.js +103 -0
  240. package/dist/targets/native/components/list-item.d.ts +20 -0
  241. package/dist/targets/native/components/list-item.js +71 -0
  242. package/dist/targets/native/components/menu.d.ts +24 -0
  243. package/dist/targets/native/components/menu.js +71 -0
  244. package/dist/targets/native/components/modal.d.ts +15 -0
  245. package/dist/targets/native/components/modal.js +110 -0
  246. package/dist/targets/native/components/paywall-banner.d.ts +11 -0
  247. package/dist/targets/native/components/paywall-banner.js +46 -0
  248. package/dist/targets/native/components/paywall.d.ts +18 -0
  249. package/dist/targets/native/components/paywall.js +61 -0
  250. package/dist/targets/native/components/radio.d.ts +20 -0
  251. package/dist/targets/native/components/radio.js +71 -0
  252. package/dist/targets/native/components/search.d.ts +22 -0
  253. package/dist/targets/native/components/search.js +92 -0
  254. package/dist/targets/native/components/select.d.ts +17 -0
  255. package/dist/targets/native/components/select.js +86 -0
  256. package/dist/targets/native/components/sheet.d.ts +11 -0
  257. package/dist/targets/native/components/sheet.js +51 -0
  258. package/dist/targets/native/components/skeleton.d.ts +18 -0
  259. package/dist/targets/native/components/skeleton.js +52 -0
  260. package/dist/targets/native/components/slider.d.ts +18 -0
  261. package/dist/targets/native/components/slider.js +153 -0
  262. package/dist/targets/native/components/spinner.d.ts +7 -0
  263. package/dist/targets/native/components/spinner.js +10 -0
  264. package/dist/targets/native/components/stepper.d.ts +10 -0
  265. package/dist/targets/native/components/stepper.js +33 -0
  266. package/dist/targets/native/components/switch.d.ts +20 -0
  267. package/dist/targets/native/components/switch.js +68 -0
  268. package/dist/targets/native/components/text.d.ts +12 -0
  269. package/dist/targets/native/components/text.js +31 -0
  270. package/dist/targets/native/components/textarea.d.ts +13 -0
  271. package/dist/targets/native/components/textarea.js +92 -0
  272. package/dist/targets/native/components/theme.d.ts +18 -0
  273. package/dist/targets/native/components/theme.js +20 -0
  274. package/dist/targets/native/components/toaster.d.ts +35 -0
  275. package/dist/targets/native/components/toaster.js +159 -0
  276. package/dist/targets/native/fonts.d.ts +9 -0
  277. package/dist/targets/native/fonts.js +10 -0
  278. package/dist/targets/native/icons.d.ts +1 -0
  279. package/dist/targets/native/icons.js +1 -0
  280. package/dist/targets/native/index.d.ts +3 -0
  281. package/dist/targets/native/index.js +11 -0
  282. package/dist/targets/native/preset.cjs +69 -0
  283. package/dist/targets/native/resolve.d.ts +22 -0
  284. package/dist/targets/native/resolve.js +85 -0
  285. package/dist/targets/native/theme.css +118 -0
  286. package/dist/targets/native/theme.d.ts +117 -0
  287. package/dist/targets/native/theme.js +118 -0
  288. package/package.json +194 -0
  289. package/styles/_primitives.generated.css +284 -0
  290. package/styles/base.css +950 -0
  291. package/styles/themes/rojao.css +107 -0
@@ -0,0 +1,103 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import { useState, useEffect } from "react";
4
+ import { Info, CheckCircle2, AlertTriangle, AlertOctagon, Lock, X } from "lucide-react";
5
+ const STYLES = {
6
+ critical: {
7
+ container: "border-danger-border bg-danger-soft",
8
+ title: "text-danger-text",
9
+ desc: "text-danger-text/85",
10
+ icon: "text-danger-text",
11
+ cta: "bg-danger text-fg-inverse hover:bg-danger-text",
12
+ solidCta: true
13
+ },
14
+ warning: {
15
+ container: "border-warning-border bg-warning-soft",
16
+ title: "text-warning-text",
17
+ desc: "text-warning-text/85",
18
+ icon: "text-warning-text",
19
+ cta: "bg-surface-default border border-warning-border hover:bg-warning-soft text-warning-text",
20
+ solidCta: false
21
+ },
22
+ info: {
23
+ container: "border-info-border bg-info-soft",
24
+ title: "text-info-text",
25
+ desc: "text-info-text/85",
26
+ icon: "text-info-text",
27
+ cta: "bg-surface-default border border-info-border hover:bg-info-soft text-info-text",
28
+ solidCta: false
29
+ },
30
+ success: {
31
+ container: "border-success-border bg-success-soft",
32
+ title: "text-success-text",
33
+ desc: "text-success-text/85",
34
+ icon: "text-success-text",
35
+ cta: "bg-surface-default border border-success-border hover:bg-success-soft text-success-text",
36
+ solidCta: false
37
+ }
38
+ };
39
+ function iconFor(severity, intent) {
40
+ if (intent === "commercial" && (severity === "critical" || severity === "warning")) return Lock;
41
+ switch (severity) {
42
+ case "critical":
43
+ return AlertOctagon;
44
+ case "warning":
45
+ return AlertTriangle;
46
+ case "success":
47
+ return CheckCircle2;
48
+ default:
49
+ return Info;
50
+ }
51
+ }
52
+ function DefaultLink({ href, className, children }) {
53
+ return /* @__PURE__ */ React.createElement("a", { href, className }, children);
54
+ }
55
+ function Notice({
56
+ severity,
57
+ title,
58
+ description,
59
+ intent = "operational",
60
+ cta,
61
+ linkAs: LinkAs = DefaultLink,
62
+ dismissible = false,
63
+ id,
64
+ dismissLabel = "Dispensar",
65
+ className = ""
66
+ }) {
67
+ const canDismiss = dismissible && severity !== "critical" && !!id;
68
+ const [dismissed, setDismissed] = useState(false);
69
+ useEffect(() => {
70
+ if (canDismiss && id) setDismissed(localStorage.getItem(`notice-dismissed:${id}`) === "1");
71
+ }, [canDismiss, id]);
72
+ if (dismissed) return null;
73
+ const s = STYLES[severity];
74
+ const Icon = iconFor(severity, intent);
75
+ const ctaClass = ["text-caption font-semibold px-3 py-1.5 rounded-(--radius-card) shrink-0 transition-colors", s.cta].join(" ");
76
+ function dismiss() {
77
+ if (id) localStorage.setItem(`notice-dismissed:${id}`, "1");
78
+ setDismissed(true);
79
+ }
80
+ const ctaNode = (cta == null ? void 0 : cta.href) ? /* @__PURE__ */ React.createElement(LinkAs, { href: cta.href, className: ctaClass }, cta.label, " \u2192") : (cta == null ? void 0 : cta.onClick) ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: cta.onClick, className: ctaClass }, cta.label, " \u2192") : null;
81
+ return /* @__PURE__ */ React.createElement(
82
+ "div",
83
+ {
84
+ role: severity === "critical" ? "alert" : "status",
85
+ "aria-live": severity === "critical" ? "assertive" : "polite",
86
+ className: ["rounded-(--radius-card) border px-4 py-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2", s.container, className].join(" ")
87
+ },
88
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-start gap-2.5 min-w-0" }, /* @__PURE__ */ React.createElement(Icon, { size: 18, "aria-hidden": true, className: ["shrink-0 mt-0.5", s.icon].join(" ") }), /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React.createElement("p", { className: ["text-label font-semibold", s.title].join(" ") }, title), description && /* @__PURE__ */ React.createElement("div", { className: ["text-caption mt-0.5", s.desc].join(" ") }, description))),
89
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 shrink-0 self-end sm:self-auto" }, ctaNode, canDismiss && /* @__PURE__ */ React.createElement(
90
+ "button",
91
+ {
92
+ type: "button",
93
+ onClick: dismiss,
94
+ "aria-label": dismissLabel,
95
+ className: ["transition-opacity hover:opacity-70 shrink-0", s.icon].join(" ")
96
+ },
97
+ /* @__PURE__ */ React.createElement(X, { size: 16, "aria-hidden": true })
98
+ ))
99
+ );
100
+ }
101
+ export {
102
+ Notice
103
+ };
@@ -0,0 +1,60 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, KeyboardEvent } from 'react';
3
+ import { LucideIcon } from 'lucide-react';
4
+
5
+ /**
6
+ * OptionTile / OptionTileGrid — grade de escolha única (onboarding #449).
7
+ *
8
+ * MESMA FAMÍLIA visual do ChoiceCard (os dois single-choice): casca idle suave
9
+ * (borda 1px border-default, hover border-strong) e selecionado = fill SÓLIDO
10
+ * derivado da camada semântica de seleção (--selection-fill-active). Baixa visão:
11
+ * a seleção é distinguível por mais que cor — fill de alto contraste + check
12
+ * redundante (badge on-fill, glifo fill), nunca só um realce sutil. Todo conteúdo
13
+ * sobre o fill ativo usa tokens de contraste próprios (*-active → on-fill), nunca
14
+ * os tokens de superfície clara.
15
+ *
16
+ * Acessibilidade: OptionTileGrid é um radiogroup com roving tabindex — só um tile é
17
+ * tabbable; setas movem foco E seleção (padrão ARIA), Home/End vão às pontas.
18
+ */
19
+ interface OptionTileProps {
20
+ selected: boolean;
21
+ label: string;
22
+ onClick: () => void;
23
+ icon?: ReactNode;
24
+ disabled?: boolean;
25
+ multiple?: boolean;
26
+ tabIndex?: number;
27
+ onKeyDown?: (e: KeyboardEvent<HTMLButtonElement>) => void;
28
+ buttonRef?: (el: HTMLButtonElement | null) => void;
29
+ fullSpan?: boolean;
30
+ className?: string;
31
+ }
32
+ declare function OptionTile({ selected, label, onClick, icon, disabled, multiple, tabIndex, onKeyDown, buttonRef, fullSpan, className, }: OptionTileProps): react.JSX.Element;
33
+ interface OptionTileItem<T extends string = string> {
34
+ value: T;
35
+ label: string;
36
+ icon?: LucideIcon;
37
+ fullSpan?: boolean;
38
+ }
39
+ interface OptionTileGridBase<T extends string = string> {
40
+ label?: ReactNode;
41
+ options: OptionTileItem<T>[];
42
+ /** 2 = sempre 2 colunas; 3 = 2 no mobile, 3 no >= sm. default 3. */
43
+ columns?: 2 | 3;
44
+ className?: string;
45
+ 'aria-label'?: string;
46
+ }
47
+ type OptionTileGridSingle<T extends string = string> = OptionTileGridBase<T> & {
48
+ multiple?: false;
49
+ value: T | null;
50
+ onChange: (value: T) => void;
51
+ };
52
+ type OptionTileGridMulti<T extends string = string> = OptionTileGridBase<T> & {
53
+ multiple: true;
54
+ value: T[];
55
+ onChange: (value: T[]) => void;
56
+ };
57
+ type OptionTileGridProps<T extends string = string> = OptionTileGridSingle<T> | OptionTileGridMulti<T>;
58
+ declare function OptionTileGrid<T extends string = string>(props: OptionTileGridProps<T>): react.JSX.Element;
59
+
60
+ export { OptionTile, OptionTileGrid, type OptionTileGridProps, type OptionTileItem, type OptionTileProps };
@@ -0,0 +1,144 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import { useId, useRef } from "react";
4
+ import { Check } from "lucide-react";
5
+ function OptionTile({
6
+ selected,
7
+ label,
8
+ onClick,
9
+ icon,
10
+ disabled = false,
11
+ multiple = false,
12
+ tabIndex,
13
+ onKeyDown,
14
+ buttonRef,
15
+ fullSpan = false,
16
+ className = ""
17
+ }) {
18
+ return /* @__PURE__ */ React.createElement(
19
+ "button",
20
+ {
21
+ type: "button",
22
+ role: multiple ? "checkbox" : "radio",
23
+ "aria-checked": selected,
24
+ tabIndex,
25
+ disabled,
26
+ ref: buttonRef,
27
+ onClick,
28
+ onKeyDown,
29
+ className: [
30
+ "relative flex w-full flex-col items-center justify-center gap-2 rounded-(--radius-card) border text-center transition-all",
31
+ icon ? "min-h-[88px] px-3 py-4" : "min-h-[52px] px-3 py-3",
32
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stroke-focus focus-visible:ring-offset-2",
33
+ selected ? "[border-color:var(--option-tile-border-active)] [background:var(--option-tile-bg-active)]" : "[border-color:var(--option-tile-border)] [background:var(--option-tile-bg)] hover:[border-color:var(--option-tile-border-hover)]",
34
+ disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
35
+ fullSpan ? "col-span-full" : "",
36
+ className
37
+ ].filter(Boolean).join(" ")
38
+ },
39
+ selected && /* @__PURE__ */ React.createElement("span", { className: "absolute right-2 top-2 flex size-4 items-center justify-center rounded-full [background:var(--option-tile-check)] [color:var(--option-tile-check-icon)]" }, /* @__PURE__ */ React.createElement(Check, { size: 11, strokeWidth: 3 })),
40
+ icon && /* @__PURE__ */ React.createElement(
41
+ "span",
42
+ {
43
+ className: [
44
+ "flex size-10 items-center justify-center rounded-(--radius-card) transition-colors [&_svg]:size-5",
45
+ selected ? "[background:var(--option-tile-icon-bg-active)] [color:var(--option-tile-icon-active)]" : "[background:var(--option-tile-icon-bg)] [color:var(--option-tile-icon)]"
46
+ ].join(" ")
47
+ },
48
+ icon
49
+ ),
50
+ /* @__PURE__ */ React.createElement(
51
+ "span",
52
+ {
53
+ className: [
54
+ icon ? "text-caption" : "text-label",
55
+ "font-semibold leading-tight",
56
+ selected ? "[color:var(--option-tile-label-active)]" : "[color:var(--option-tile-label)]"
57
+ ].join(" ")
58
+ },
59
+ label
60
+ )
61
+ );
62
+ }
63
+ function OptionTileGrid(props) {
64
+ const { label, options, columns = 3, className = "" } = props;
65
+ const ariaLabel = props["aria-label"];
66
+ const multiple = props.multiple === true;
67
+ const groupId = useId();
68
+ const btns = useRef([]);
69
+ const isSel = (v) => multiple ? props.value.includes(v) : props.value === v;
70
+ const activate = (v) => {
71
+ if (props.multiple) {
72
+ const cur = props.value;
73
+ props.onChange(cur.includes(v) ? cur.filter((x) => x !== v) : [...cur, v]);
74
+ } else {
75
+ props.onChange(v);
76
+ }
77
+ };
78
+ const activeIdx = multiple ? -1 : Math.max(0, options.findIndex((o) => isSel(o.value)));
79
+ const move = (i) => {
80
+ var _a;
81
+ const n = options.length;
82
+ const idx = (i % n + n) % n;
83
+ (_a = btns.current[idx]) == null ? void 0 : _a.focus();
84
+ if (!multiple) activate(options[idx].value);
85
+ };
86
+ const onKeyDown = (e, i) => {
87
+ if (multiple) return;
88
+ switch (e.key) {
89
+ case "ArrowRight":
90
+ case "ArrowDown":
91
+ e.preventDefault();
92
+ move(i + 1);
93
+ break;
94
+ case "ArrowLeft":
95
+ case "ArrowUp":
96
+ e.preventDefault();
97
+ move(i - 1);
98
+ break;
99
+ case "Home":
100
+ e.preventDefault();
101
+ move(0);
102
+ break;
103
+ case "End":
104
+ e.preventDefault();
105
+ move(options.length - 1);
106
+ break;
107
+ }
108
+ };
109
+ const grid = /* @__PURE__ */ React.createElement(
110
+ "div",
111
+ {
112
+ id: groupId,
113
+ role: multiple ? "group" : "radiogroup",
114
+ "aria-label": ariaLabel,
115
+ className: ["grid gap-2", columns === 2 ? "grid-cols-2" : "grid-cols-2 sm:grid-cols-3", label ? "" : className].join(" ")
116
+ },
117
+ options.map((opt, i) => {
118
+ const Icon = opt.icon;
119
+ return /* @__PURE__ */ React.createElement(
120
+ OptionTile,
121
+ {
122
+ key: opt.value,
123
+ multiple,
124
+ selected: isSel(opt.value),
125
+ label: opt.label,
126
+ icon: Icon ? /* @__PURE__ */ React.createElement(Icon, null) : void 0,
127
+ fullSpan: opt.fullSpan,
128
+ onClick: () => activate(opt.value),
129
+ tabIndex: multiple ? 0 : i === activeIdx ? 0 : -1,
130
+ onKeyDown: (e) => onKeyDown(e, i),
131
+ buttonRef: (el) => {
132
+ btns.current[i] = el;
133
+ }
134
+ }
135
+ );
136
+ })
137
+ );
138
+ if (!label) return grid;
139
+ return /* @__PURE__ */ React.createElement("div", { className: ["flex flex-col gap-2", className].join(" ") }, /* @__PURE__ */ React.createElement("span", { className: "text-label font-medium text-fg-primary" }, label), grid);
140
+ }
141
+ export {
142
+ OptionTile,
143
+ OptionTileGrid
144
+ };
@@ -0,0 +1,29 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ type PageShellWidth = 'narrow' | 'default' | 'wide';
5
+ interface PageShellProps {
6
+ maxWidth?: PageShellWidth;
7
+ padded?: boolean;
8
+ children: ReactNode;
9
+ className?: string;
10
+ }
11
+ declare function PageShell({ maxWidth, padded, children, className, }: PageShellProps): react.JSX.Element;
12
+ declare namespace PageShell {
13
+ var Header: typeof PageShellHeader;
14
+ var Body: typeof PageShellBody;
15
+ }
16
+ interface PageShellHeaderProps {
17
+ title: ReactNode;
18
+ eyebrow?: ReactNode;
19
+ actions?: ReactNode;
20
+ className?: string;
21
+ }
22
+ declare function PageShellHeader({ title, eyebrow, actions, className }: PageShellHeaderProps): react.JSX.Element;
23
+ interface PageShellBodyProps {
24
+ children: ReactNode;
25
+ className?: string;
26
+ }
27
+ declare function PageShellBody({ children, className }: PageShellBodyProps): react.JSX.Element;
28
+
29
+ export { PageShell, type PageShellBodyProps, type PageShellHeaderProps, type PageShellProps, type PageShellWidth };
@@ -0,0 +1,36 @@
1
+ import "../chunk-ORMEWXMH.js";
2
+ const WIDTH_CLS = {
3
+ narrow: "max-w-screen-md",
4
+ default: "max-w-screen-xl",
5
+ wide: "max-w-screen-2xl"
6
+ };
7
+ function PageShell({
8
+ maxWidth = "default",
9
+ padded = true,
10
+ children,
11
+ className = ""
12
+ }) {
13
+ return /* @__PURE__ */ React.createElement(
14
+ "div",
15
+ {
16
+ className: [
17
+ "mx-auto w-full",
18
+ WIDTH_CLS[maxWidth],
19
+ padded ? "px-4 sm:px-6 lg:px-8 py-6" : "",
20
+ className
21
+ ].filter(Boolean).join(" ")
22
+ },
23
+ children
24
+ );
25
+ }
26
+ function PageShellHeader({ title, eyebrow, actions, className = "" }) {
27
+ return /* @__PURE__ */ React.createElement("div", { className: ["flex items-start justify-between gap-4 mb-6", className].filter(Boolean).join(" ") }, /* @__PURE__ */ React.createElement("div", { className: "min-w-0" }, eyebrow && /* @__PURE__ */ React.createElement("p", { className: "text-caption font-medium text-fg-muted mb-1" }, eyebrow), /* @__PURE__ */ React.createElement("h1", { className: "text-heading-md font-semibold text-fg-primary truncate" }, title)), actions && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 shrink-0" }, actions));
28
+ }
29
+ function PageShellBody({ children, className = "" }) {
30
+ return /* @__PURE__ */ React.createElement("div", { className: ["space-y-6", className].filter(Boolean).join(" ") }, children);
31
+ }
32
+ PageShell.Header = PageShellHeader;
33
+ PageShell.Body = PageShellBody;
34
+ export {
35
+ PageShell
36
+ };
@@ -0,0 +1,35 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * PageSkeleton — loading state padrão pra páginas com tabela.
5
+ * Header (título + botão) + tabela com header + N rows.
6
+ *
7
+ * `padded` (default true) controla o padding interno. Use `padded={false}`
8
+ * quando o skeleton já vive dentro de um container que aplica padding
9
+ * (ex.: PageRoot/PageLayout), evitando padding duplicado.
10
+ *
11
+ * Uso:
12
+ * <PageSkeleton rows={8} />
13
+ * <PageSkeleton rows={8} padded={false} /> // dentro de um container
14
+ */
15
+ interface PageSkeletonProps {
16
+ rows?: number;
17
+ padded?: boolean;
18
+ }
19
+ declare function PageSkeleton({ rows, padded }: PageSkeletonProps): react.JSX.Element;
20
+ /**
21
+ * CardsSkeleton — loading state pra páginas com KPIs (4 cards no topo + 2 charts).
22
+ *
23
+ * `padded` (default true) — ver PageSkeleton.
24
+ *
25
+ * Uso:
26
+ * <CardsSkeleton count={4} />
27
+ * <CardsSkeleton count={4} padded={false} /> // dentro de um container
28
+ */
29
+ interface CardsSkeletonProps {
30
+ count?: number;
31
+ padded?: boolean;
32
+ }
33
+ declare function CardsSkeleton({ count, padded }: CardsSkeletonProps): react.JSX.Element;
34
+
35
+ export { CardsSkeleton, type CardsSkeletonProps, PageSkeleton, type PageSkeletonProps };
@@ -0,0 +1,12 @@
1
+ import "../chunk-ORMEWXMH.js";
2
+ import { Skeleton } from "./skeleton.js";
3
+ function PageSkeleton({ rows = 5, padded = true }) {
4
+ return /* @__PURE__ */ React.createElement("div", { className: `space-y-6 ${padded ? "p-6" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement(Skeleton, { className: "h-7 w-48" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-9 w-28" })), /* @__PURE__ */ React.createElement("div", { className: "border border-stroke-default rounded-(--radius-card) overflow-hidden" }, /* @__PURE__ */ React.createElement("div", { className: "bg-surface-raised border-b border-stroke-default px-4 py-3 flex gap-4" }, /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-32" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-24" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-20" })), Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "px-4 py-3.5 flex gap-4 items-center border-b border-stroke-subtle last:border-0" }, /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-40" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-28" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-16" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-20 ml-auto" })))));
5
+ }
6
+ function CardsSkeleton({ count = 4, padded = true }) {
7
+ return /* @__PURE__ */ React.createElement("div", { className: `space-y-6 ${padded ? "p-6" : ""}` }, /* @__PURE__ */ React.createElement(Skeleton, { className: "h-7 w-48" }), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-4" }, Array.from({ length: count }).map((_, i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "border border-stroke-default rounded-(--radius-card) p-4 space-y-3" }, /* @__PURE__ */ React.createElement(Skeleton, { className: "h-4 w-24" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-8 w-32" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-3 w-20" })))), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4" }, Array.from({ length: 2 }).map((_, i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "border border-stroke-default rounded-(--radius-card) p-4 space-y-3" }, /* @__PURE__ */ React.createElement(Skeleton, { className: "h-5 w-32" }), /* @__PURE__ */ React.createElement(Skeleton, { className: "h-32 w-full" })))));
8
+ }
9
+ export {
10
+ CardsSkeleton,
11
+ PageSkeleton
12
+ };
@@ -0,0 +1,35 @@
1
+ import react__default from 'react';
2
+
3
+ type PageTab = {
4
+ /** Route mode: a aba navega pra esta URL (default). */
5
+ href?: string;
6
+ /** Controlled mode: identifica a aba quando se usa value/onChange (sem navegar). */
7
+ value?: string;
8
+ label: string;
9
+ /** Show a numeric count badge */
10
+ count?: number;
11
+ /** Show a green "aberto" pill */
12
+ dot?: boolean;
13
+ /** Show an "em breve" label: tab is not yet available */
14
+ soon?: boolean;
15
+ /** Ghost route indicator (dev only) */
16
+ ghost?: boolean;
17
+ /** Match only the exact path (+ matchPaths). Use for root paths like /marketing */
18
+ exact?: boolean;
19
+ /** Extra paths that also activate this tab (prefix match) */
20
+ matchPaths?: string[];
21
+ };
22
+ interface PageTabsProps {
23
+ tabs: PageTab[];
24
+ trailing?: react__default.ReactNode;
25
+ /**
26
+ * Controlled mode: quando onChange é passado, as abas viram botões (sem
27
+ * navegação) e o ativo é tab.value === value. Use pra trocar conteúdo na
28
+ * mesma tela (ex: idiomas num form). Sem onChange, segue route-based (Link).
29
+ */
30
+ value?: string;
31
+ onChange?: (value: string) => void;
32
+ }
33
+ declare function PageTabs({ tabs, trailing, value, onChange }: PageTabsProps): react__default.JSX.Element;
34
+
35
+ export { type PageTab, PageTabs, type PageTabsProps };
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import Link from "next/link";
4
+ import { usePathname } from "next/navigation";
5
+ function isActive(tab, pathname) {
6
+ var _a, _b, _c, _d;
7
+ if (!tab.href) return false;
8
+ if (tab.exact) {
9
+ return pathname === tab.href || ((_b = (_a = tab.matchPaths) == null ? void 0 : _a.some((p) => pathname.startsWith(p))) != null ? _b : false);
10
+ }
11
+ return pathname === tab.href || pathname.startsWith(tab.href + "/") || ((_d = (_c = tab.matchPaths) == null ? void 0 : _c.some((p) => pathname.startsWith(p))) != null ? _d : false);
12
+ }
13
+ const TAB_BASE = "flex items-center gap-2 whitespace-nowrap shrink-0 px-4 sm:px-5 py-4 text-label font-medium border-b-2 transition-colors";
14
+ function tabClass(active) {
15
+ return [
16
+ TAB_BASE,
17
+ active ? "[border-color:var(--page-tabs-indicator-active)] [color:var(--page-tabs-text-active)]" : "border-transparent [color:var(--page-tabs-text)] hover:[color:var(--page-tabs-text-hover)] hover:[border-color:var(--page-tabs-indicator-hover)]"
18
+ ].join(" ");
19
+ }
20
+ function TabContent({ tab, active }) {
21
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, tab.ghost && /* @__PURE__ */ React.createElement("span", { title: "Rota fantasma em valida\xE7\xE3o" }, "\u{1F47B}"), tab.label, tab.soon && /* @__PURE__ */ React.createElement("span", { className: "text-[10px] font-medium px-1.5 py-0.5 rounded [background:var(--page-tabs-soon-bg)] [color:var(--page-tabs-soon-text)]" }, "em breve"), tab.dot && /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] font-medium leading-none [background:var(--page-tabs-dot-bg)] [color:var(--page-tabs-dot-text)]" }, /* @__PURE__ */ React.createElement("span", { className: "w-1.5 h-1.5 rounded-full shrink-0 [background:var(--page-tabs-dot-indicator)]" }), "aberto"), tab.count != null && tab.count > 0 && /* @__PURE__ */ React.createElement("span", { className: [
22
+ "inline-flex items-center justify-center min-w-[18px] h-[18px] px-1",
23
+ "rounded-full text-[11px] font-semibold",
24
+ active ? "[background:var(--page-tabs-count-bg-active)] [color:var(--page-tabs-count-text-active)]" : "[background:var(--page-tabs-count-bg)] [color:var(--page-tabs-count-text)]"
25
+ ].join(" ") }, tab.count > 99 ? "99+" : tab.count));
26
+ }
27
+ function PageTabs({ tabs, trailing, value, onChange }) {
28
+ const pathname = usePathname();
29
+ const controlled = onChange !== void 0;
30
+ return /* @__PURE__ */ React.createElement("div", { className: "flex items-center [background:var(--page-tabs-bg)] [border-bottom:1px_solid_var(--page-tabs-border)]" }, /* @__PURE__ */ React.createElement("nav", { className: "flex flex-1 overflow-x-auto scrollbar-none px-4 sm:px-6 lg:px-8", role: controlled ? "tablist" : void 0 }, tabs.map((tab) => {
31
+ var _a, _b;
32
+ const active = controlled ? tab.value === value : isActive(tab, pathname);
33
+ if (controlled) {
34
+ return /* @__PURE__ */ React.createElement(
35
+ "button",
36
+ {
37
+ key: (_a = tab.value) != null ? _a : tab.label,
38
+ type: "button",
39
+ role: "tab",
40
+ "aria-selected": active,
41
+ onClick: () => {
42
+ var _a2;
43
+ return onChange == null ? void 0 : onChange((_a2 = tab.value) != null ? _a2 : "");
44
+ },
45
+ className: tabClass(active)
46
+ },
47
+ /* @__PURE__ */ React.createElement(TabContent, { tab, active })
48
+ );
49
+ }
50
+ return /* @__PURE__ */ React.createElement(Link, { key: tab.href, href: (_b = tab.href) != null ? _b : "#", className: tabClass(active) }, /* @__PURE__ */ React.createElement(TabContent, { tab, active }));
51
+ })), trailing && /* @__PURE__ */ React.createElement("div", { className: "hidden sm:flex shrink-0 items-center pr-4 sm:pr-6 lg:pr-8" }, trailing));
52
+ }
53
+ export {
54
+ PageTabs
55
+ };
@@ -0,0 +1,31 @@
1
+ import * as react from 'react';
2
+
3
+ /**
4
+ * Pagination — navegação por páginas em listas longas.
5
+ *
6
+ * Uso:
7
+ * <Pagination
8
+ * page={2}
9
+ * totalPages={10}
10
+ * onChange={p => setPage(p)}
11
+ * />
12
+ *
13
+ * <Pagination page={1} totalPages={20} onChange={...} siblings={2} />
14
+ *
15
+ * Renderiza: Prev | 1 ... 4 5 [6] 7 8 ... 20 | Next
16
+ */
17
+ interface PaginationProps {
18
+ page: number;
19
+ totalPages: number;
20
+ onChange: (page: number) => void;
21
+ /** Páginas vizinhas a mostrar de cada lado da atual. default = 1. */
22
+ siblings?: number;
23
+ /** Páginas fixas no início e no fim (sempre visíveis). default = 1. */
24
+ boundaries?: number;
25
+ className?: string;
26
+ /** Esconde quando totalPages <= 1. default = true. */
27
+ hideOnSinglePage?: boolean;
28
+ }
29
+ declare const Pagination: react.ForwardRefExoticComponent<PaginationProps & react.RefAttributes<HTMLElement>>;
30
+
31
+ export { Pagination, type PaginationProps };
@@ -0,0 +1,80 @@
1
+ import "../chunk-ORMEWXMH.js";
2
+ import { forwardRef } from "react";
3
+ import { ChevronLeft, ChevronRight } from "lucide-react";
4
+ function range(start, end) {
5
+ return Array.from({ length: end - start + 1 }, (_, i) => start + i);
6
+ }
7
+ function getRange(page, totalPages, siblings, boundaries) {
8
+ const totalNumbers = siblings * 2 + boundaries * 2 + 3;
9
+ if (totalPages <= totalNumbers) return range(1, totalPages);
10
+ const leftSiblingStart = Math.max(page - siblings, boundaries + 2);
11
+ const rightSiblingEnd = Math.min(page + siblings, totalPages - boundaries - 1);
12
+ const showLeftEllipsis = leftSiblingStart > boundaries + 2;
13
+ const showRightEllipsis = rightSiblingEnd < totalPages - boundaries - 1;
14
+ const items = [];
15
+ items.push(...range(1, boundaries));
16
+ if (showLeftEllipsis) {
17
+ items.push("ellipsis-left");
18
+ } else if (boundaries + 1 < leftSiblingStart) {
19
+ items.push(boundaries + 1);
20
+ }
21
+ items.push(...range(leftSiblingStart, rightSiblingEnd));
22
+ if (showRightEllipsis) {
23
+ items.push("ellipsis-right");
24
+ } else if (rightSiblingEnd + 1 < totalPages - boundaries + 1) {
25
+ items.push(rightSiblingEnd + 1);
26
+ }
27
+ items.push(...range(totalPages - boundaries + 1, totalPages));
28
+ return Array.from(new Set(items.filter((i) => typeof i === "string" || i >= 1 && i <= totalPages)));
29
+ }
30
+ const baseBtn = "inline-flex items-center justify-center min-w-9 h-9 px-2 rounded-(--radius-control) text-label transition-colors disabled:cursor-not-allowed disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stroke-focus";
31
+ const Pagination = forwardRef(function Pagination2({
32
+ page,
33
+ totalPages,
34
+ onChange,
35
+ siblings = 1,
36
+ boundaries = 1,
37
+ className = "",
38
+ hideOnSinglePage = true
39
+ }, ref) {
40
+ if (hideOnSinglePage && totalPages <= 1) return null;
41
+ const items = getRange(page, totalPages, siblings, boundaries);
42
+ const canPrev = page > 1;
43
+ const canNext = page < totalPages;
44
+ return /* @__PURE__ */ React.createElement("nav", { ref, role: "navigation", "aria-label": "Pagina\xE7\xE3o", className }, /* @__PURE__ */ React.createElement("ul", { className: "flex items-center gap-1" }, /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(
45
+ "button",
46
+ {
47
+ type: "button",
48
+ disabled: !canPrev,
49
+ onClick: () => canPrev && onChange(page - 1),
50
+ "aria-label": "P\xE1gina anterior",
51
+ className: [baseBtn, "text-fg-secondary hover:bg-surface-raised"].join(" ")
52
+ },
53
+ /* @__PURE__ */ React.createElement(ChevronLeft, { size: 16 })
54
+ )), items.map((it, idx) => /* @__PURE__ */ React.createElement("li", { key: `${it}-${idx}` }, typeof it === "string" ? /* @__PURE__ */ React.createElement("span", { className: "inline-flex items-center justify-center min-w-9 h-9 text-fg-muted text-label" }, "\u2026") : /* @__PURE__ */ React.createElement(
55
+ "button",
56
+ {
57
+ type: "button",
58
+ onClick: () => onChange(it),
59
+ "aria-current": it === page ? "page" : void 0,
60
+ className: [
61
+ baseBtn,
62
+ it === page ? "bg-brand-primary text-brand-on-primary" : "text-fg-secondary hover:bg-surface-raised"
63
+ ].join(" ")
64
+ },
65
+ it
66
+ ))), /* @__PURE__ */ React.createElement("li", null, /* @__PURE__ */ React.createElement(
67
+ "button",
68
+ {
69
+ type: "button",
70
+ disabled: !canNext,
71
+ onClick: () => canNext && onChange(page + 1),
72
+ "aria-label": "Pr\xF3xima p\xE1gina",
73
+ className: [baseBtn, "text-fg-secondary hover:bg-surface-raised"].join(" ")
74
+ },
75
+ /* @__PURE__ */ React.createElement(ChevronRight, { size: 16 })
76
+ ))));
77
+ });
78
+ export {
79
+ Pagination
80
+ };
@@ -0,0 +1,37 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ComponentType } from 'react';
3
+
4
+ /**
5
+ * PaywallBanner — gate de feature/plano. Diferente do BlockerCard que sinaliza
6
+ * problema (limite, falha), o PaywallBanner aponta pra um upgrade ou ação de plano.
7
+ *
8
+ * Uso:
9
+ * <PaywallBanner
10
+ * variant="warn"
11
+ * title="Você está em 80% do limite"
12
+ * description="Faça upgrade pra continuar cadastrando produtos."
13
+ * ctaLabel="Ver planos"
14
+ * ctaHref="/billing"
15
+ * />
16
+ *
17
+ * <PaywallBanner variant="blocked" title="..." description="..." />
18
+ */
19
+ interface PaywallBannerProps {
20
+ variant: 'warn' | 'blocked';
21
+ title: string;
22
+ description: ReactNode;
23
+ ctaLabel?: string;
24
+ ctaHref?: string;
25
+ /** Handler alternativo (sem href). */
26
+ onCta?: () => void;
27
+ /** Component de link (next/link em apps Next). */
28
+ linkAs?: ComponentType<{
29
+ href: string;
30
+ className?: string;
31
+ children: ReactNode;
32
+ }>;
33
+ className?: string;
34
+ }
35
+ declare function PaywallBanner({ variant, title, description, ctaLabel, ctaHref, onCta, linkAs: LinkAs, className, }: PaywallBannerProps): react.JSX.Element;
36
+
37
+ export { PaywallBanner, type PaywallBannerProps };