@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,154 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import { useRef, useState, useEffect, useCallback } from "react";
4
+ function ChoiceCarousel({
5
+ options,
6
+ getKey,
7
+ renderCard,
8
+ value,
9
+ onChange,
10
+ multiple = false,
11
+ ariaLabel,
12
+ isDisabled
13
+ }) {
14
+ const trackRef = useRef(null);
15
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
16
+ const [canScrollRight, setCanScrollRight] = useState(false);
17
+ const valueArray = multiple ? value != null ? value : [] : [];
18
+ const valueSingle = multiple ? null : value;
19
+ const isSelected = useCallback(
20
+ (option) => {
21
+ if (multiple) return valueArray.some((v) => getKey(v) === getKey(option));
22
+ return valueSingle != null && getKey(valueSingle) === getKey(option);
23
+ },
24
+ [multiple, valueArray, valueSingle, getKey]
25
+ );
26
+ const handleSelect = useCallback(
27
+ (option) => {
28
+ if (isDisabled == null ? void 0 : isDisabled(option)) return;
29
+ if (multiple) {
30
+ const exists = valueArray.some((v) => getKey(v) === getKey(option));
31
+ const next = exists ? valueArray.filter((v) => getKey(v) !== getKey(option)) : [...valueArray, option];
32
+ onChange(next);
33
+ } else {
34
+ onChange(option);
35
+ }
36
+ },
37
+ [multiple, valueArray, onChange, getKey, isDisabled]
38
+ );
39
+ const updateScrollState = useCallback(() => {
40
+ const el = trackRef.current;
41
+ if (!el) return;
42
+ setCanScrollLeft(el.scrollLeft > 4);
43
+ setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 4);
44
+ }, []);
45
+ useEffect(() => {
46
+ updateScrollState();
47
+ const el = trackRef.current;
48
+ if (!el) return;
49
+ el.addEventListener("scroll", updateScrollState, { passive: true });
50
+ window.addEventListener("resize", updateScrollState);
51
+ return () => {
52
+ el.removeEventListener("scroll", updateScrollState);
53
+ window.removeEventListener("resize", updateScrollState);
54
+ };
55
+ }, [updateScrollState, options.length]);
56
+ const scrollByAmount = (dir) => {
57
+ var _a, _b;
58
+ const el = trackRef.current;
59
+ if (!el) return;
60
+ const cardWidth = (_b = (_a = el.firstElementChild) == null ? void 0 : _a.getBoundingClientRect().width) != null ? _b : 160;
61
+ const gap = 12;
62
+ el.scrollBy({ left: dir * (cardWidth + gap), behavior: "smooth" });
63
+ };
64
+ const handleKeyDown = (e) => {
65
+ if (e.key === "ArrowRight") {
66
+ e.preventDefault();
67
+ scrollByAmount(1);
68
+ } else if (e.key === "ArrowLeft") {
69
+ e.preventDefault();
70
+ scrollByAmount(-1);
71
+ }
72
+ };
73
+ return /* @__PURE__ */ React.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React.createElement(
74
+ "button",
75
+ {
76
+ type: "button",
77
+ "aria-label": "Anterior",
78
+ onClick: () => scrollByAmount(-1),
79
+ disabled: !canScrollLeft,
80
+ className: "hidden md:flex absolute left-0 top-1/2 -translate-y-1/2 -translate-x-1/2 z-10 w-9 h-9 rounded-full items-center justify-center transition-opacity disabled:opacity-0 disabled:pointer-events-none",
81
+ style: {
82
+ background: "var(--choice-carousel-chevron-bg)",
83
+ border: `1px solid var(--choice-carousel-chevron-border)`,
84
+ color: "var(--choice-carousel-chevron-text)",
85
+ boxShadow: "var(--shadow-md)"
86
+ }
87
+ },
88
+ /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.8" }, /* @__PURE__ */ React.createElement("path", { d: "M10 4l-4 4 4 4", strokeLinecap: "round", strokeLinejoin: "round" }))
89
+ ), /* @__PURE__ */ React.createElement(
90
+ "div",
91
+ {
92
+ ref: trackRef,
93
+ role: multiple ? "group" : "radiogroup",
94
+ "aria-label": ariaLabel,
95
+ tabIndex: 0,
96
+ onKeyDown: handleKeyDown,
97
+ className: "flex overflow-x-auto scroll-smooth focus:outline-none",
98
+ style: {
99
+ gap: "var(--choice-carousel-gap)",
100
+ scrollSnapType: "x mandatory",
101
+ scrollbarWidth: "none",
102
+ paddingBlock: 4
103
+ }
104
+ },
105
+ options.map((option) => {
106
+ var _a;
107
+ const selected = isSelected(option);
108
+ const disabled = (_a = isDisabled == null ? void 0 : isDisabled(option)) != null ? _a : false;
109
+ return /* @__PURE__ */ React.createElement(
110
+ "div",
111
+ {
112
+ key: getKey(option),
113
+ role: multiple ? "checkbox" : "radio",
114
+ "aria-checked": selected,
115
+ "aria-disabled": disabled,
116
+ tabIndex: disabled ? -1 : 0,
117
+ onClick: () => handleSelect(option),
118
+ onKeyDown: (e) => {
119
+ if (e.key === "Enter" || e.key === " ") {
120
+ e.preventDefault();
121
+ handleSelect(option);
122
+ }
123
+ },
124
+ className: "shrink-0",
125
+ style: {
126
+ width: "var(--choice-carousel-card-width-mobile)",
127
+ scrollSnapAlign: "start",
128
+ cursor: disabled ? "not-allowed" : "pointer"
129
+ }
130
+ },
131
+ renderCard(option, selected)
132
+ );
133
+ })
134
+ ), /* @__PURE__ */ React.createElement(
135
+ "button",
136
+ {
137
+ type: "button",
138
+ "aria-label": "Pr\xF3ximo",
139
+ onClick: () => scrollByAmount(1),
140
+ disabled: !canScrollRight,
141
+ className: "hidden md:flex absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 z-10 w-9 h-9 rounded-full items-center justify-center transition-opacity disabled:opacity-0 disabled:pointer-events-none",
142
+ style: {
143
+ background: "var(--choice-carousel-chevron-bg)",
144
+ border: `1px solid var(--choice-carousel-chevron-border)`,
145
+ color: "var(--choice-carousel-chevron-text)",
146
+ boxShadow: "var(--shadow-md)"
147
+ }
148
+ },
149
+ /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.8" }, /* @__PURE__ */ React.createElement("path", { d: "M6 4l4 4-4 4", strokeLinecap: "round", strokeLinejoin: "round" }))
150
+ ));
151
+ }
152
+ export {
153
+ ChoiceCarousel
154
+ };
@@ -0,0 +1,37 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * ChoicePreviewCard — cartão de seleção VERTICAL: mini-preview (mockup/swatch/imagem) em
6
+ * cima, label + descrição embaixo, check no selecionado. Para escolhas onde o rótulo não
7
+ * basta e o usuário precisa VER a opção (layout da home, tema, densidade, modo).
8
+ *
9
+ * Quando usar cada "card clicável" do DS:
10
+ * - ChoicePreviewCard — tile VERTICAL de preview selecionável (este).
11
+ * - ChoiceCard — row HORIZONTAL com radio (escolha exclusiva textual).
12
+ * - SelectableCard — clicável genérico (ação/navegação), button/a.
13
+ * - ToggleCard — toggle dentro de card.
14
+ * - MediaTile — thumbnail de navegação (sem estado selecionado).
15
+ *
16
+ * Estado selected é NEUTRO (stroke-focus), seguro no admin. A seleção única é gerenciada
17
+ * pelo consumidor (map + selected/onSelect) — sem grupo no DS de propósito.
18
+ */
19
+ interface ChoicePreviewCardProps {
20
+ selected: boolean;
21
+ onSelect: () => void;
22
+ /** Mini-mockup/swatch/imagem no topo. */
23
+ preview: ReactNode;
24
+ label: string;
25
+ description?: string;
26
+ /** Bloqueado por gate de plano — mostra cadeado; o clique segue (consumidor abre paywall). */
27
+ locked?: boolean;
28
+ /** Desabilitado (não-clicável). */
29
+ disabled?: boolean;
30
+ /** Selo no canto do preview (ex.: "PRO"). */
31
+ badge?: ReactNode;
32
+ previewAspect?: 'square' | 'video' | 'wide';
33
+ className?: string;
34
+ }
35
+ declare function ChoicePreviewCard({ selected, onSelect, preview, label, description, locked, disabled, badge, previewAspect, className, }: ChoicePreviewCardProps): react.JSX.Element;
36
+
37
+ export { ChoicePreviewCard, type ChoicePreviewCardProps };
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import { Check, Lock } from "lucide-react";
4
+ function ChoicePreviewCard({
5
+ selected,
6
+ onSelect,
7
+ preview,
8
+ label,
9
+ description,
10
+ locked = false,
11
+ disabled = false,
12
+ badge,
13
+ previewAspect = "video",
14
+ className = ""
15
+ }) {
16
+ const aspect = previewAspect === "square" ? "aspect-square" : previewAspect === "wide" ? "aspect-[2/1]" : "aspect-video";
17
+ return /* @__PURE__ */ React.createElement(
18
+ "button",
19
+ {
20
+ type: "button",
21
+ onClick: onSelect,
22
+ disabled,
23
+ "aria-pressed": selected,
24
+ className: [
25
+ "relative w-full text-left rounded-(--radius-card) border p-2 transition-all",
26
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stroke-focus",
27
+ selected ? "border-2 border-stroke-focus bg-surface-raised" : "border border-stroke-default bg-surface-default hover:border-stroke-strong",
28
+ disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer",
29
+ className
30
+ ].filter(Boolean).join(" ")
31
+ },
32
+ /* @__PURE__ */ React.createElement("div", { className: ["relative w-full overflow-hidden rounded-(--radius-card) ring-1 ring-inset ring-stroke-subtle", aspect].join(" ") }, preview, badge && /* @__PURE__ */ React.createElement("span", { className: "absolute top-1.5 left-1.5" }, badge), selected && /* @__PURE__ */ React.createElement("span", { className: "absolute bottom-1.5 right-1.5 size-5 rounded-full bg-stroke-focus text-surface-default flex items-center justify-center" }, /* @__PURE__ */ React.createElement(Check, { size: 12, strokeWidth: 3 })), locked && // TODO(DS): trocar por token de overlay (--choice-preview-overlay) — Patrick.
33
+ /* @__PURE__ */ React.createElement("span", { className: "absolute inset-0 flex items-center justify-center bg-[rgba(0,0,0,0.55)]" }, /* @__PURE__ */ React.createElement(Lock, { size: 18, className: "text-white" }))),
34
+ /* @__PURE__ */ React.createElement("div", { className: "px-1 pt-2" }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-medium text-fg-primary" }, label), description && /* @__PURE__ */ React.createElement("div", { className: "text-xs text-fg-muted mt-0.5 leading-snug" }, description))
35
+ );
36
+ }
37
+ export {
38
+ ChoicePreviewCard
39
+ };
@@ -0,0 +1,20 @@
1
+ import * as react from 'react';
2
+
3
+ interface ColorInputProps {
4
+ /** Hex #RRGGBB. */
5
+ value: string;
6
+ /** Dispara apenas com hex válido, normalizado em UPPERCASE. */
7
+ onChange: (hex: string) => void;
8
+ size?: 'sm' | 'md';
9
+ disabled?: boolean;
10
+ className?: string;
11
+ 'aria-label'?: string;
12
+ }
13
+ /**
14
+ * ColorInput — campo de cor único: swatch (abre o picker nativo) + hex
15
+ * editável, no mesmo controle. Molécula sobre os tokens do Input: mesma
16
+ * altura, borda, radius e anel de foco.
17
+ */
18
+ declare function ColorInput({ value, onChange, size, disabled, className, 'aria-label': ariaLabel, }: ColorInputProps): react.JSX.Element;
19
+
20
+ export { ColorInput, type ColorInputProps };
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import { useEffect, useRef, useState } from "react";
4
+ const HEX_RE = /^#[0-9a-fA-F]{6}$/;
5
+ function ColorInput({
6
+ value,
7
+ onChange,
8
+ size = "md",
9
+ disabled = false,
10
+ className = "",
11
+ "aria-label": ariaLabel
12
+ }) {
13
+ const [draft, setDraft] = useState(value.toUpperCase());
14
+ const inputRef = useRef(null);
15
+ useEffect(() => {
16
+ setDraft(value.toUpperCase());
17
+ }, [value]);
18
+ function commit() {
19
+ const raw = draft.trim();
20
+ const hex = raw.startsWith("#") ? raw : `#${raw}`;
21
+ if (HEX_RE.test(hex)) {
22
+ onChange(hex.toUpperCase());
23
+ setDraft(hex.toUpperCase());
24
+ } else {
25
+ setDraft(value.toUpperCase());
26
+ }
27
+ }
28
+ const h = size === "sm" ? "h-control-sm" : "h-control-md";
29
+ const swatch = size === "sm" ? "size-5" : "size-6";
30
+ return /* @__PURE__ */ React.createElement(
31
+ "div",
32
+ {
33
+ className: [
34
+ "inline-flex items-center gap-2 px-2.5 w-36",
35
+ h,
36
+ "rounded-(--radius-control) border border-stroke-control bg-surface-default",
37
+ "transition-colors focus-within:border-stroke-focus focus-within:ring-2 focus-within:ring-stroke-focus/20",
38
+ disabled ? "opacity-50 cursor-not-allowed" : "",
39
+ className
40
+ ].filter(Boolean).join(" ")
41
+ },
42
+ /* @__PURE__ */ React.createElement("span", { className: `relative shrink-0 ${swatch} rounded-(--radius-control) border border-stroke-subtle overflow-hidden` }, /* @__PURE__ */ React.createElement("span", { className: "absolute inset-0", style: { backgroundColor: value } }), /* @__PURE__ */ React.createElement(
43
+ "input",
44
+ {
45
+ type: "color",
46
+ value: HEX_RE.test(value) ? value : "#000000",
47
+ onChange: (e) => onChange(e.target.value.toUpperCase()),
48
+ disabled,
49
+ "aria-label": ariaLabel,
50
+ className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer disabled:cursor-not-allowed"
51
+ }
52
+ )),
53
+ /* @__PURE__ */ React.createElement(
54
+ "input",
55
+ {
56
+ ref: inputRef,
57
+ type: "text",
58
+ value: draft,
59
+ onChange: (e) => setDraft(e.target.value),
60
+ onBlur: commit,
61
+ onKeyDown: (e) => {
62
+ var _a;
63
+ if (e.key === "Enter") {
64
+ e.preventDefault();
65
+ commit();
66
+ (_a = inputRef.current) == null ? void 0 : _a.blur();
67
+ }
68
+ },
69
+ disabled,
70
+ maxLength: 7,
71
+ "aria-label": ariaLabel,
72
+ className: "min-w-0 flex-1 bg-transparent text-label font-mono text-fg-primary outline-none disabled:cursor-not-allowed"
73
+ }
74
+ )
75
+ );
76
+ }
77
+ export {
78
+ ColorInput
79
+ };
@@ -0,0 +1,70 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Combobox — Search + Select. Lista filtrada por digitação.
6
+ * Inspirado em shadcn Combobox / cmdk.
7
+ *
8
+ * Uso:
9
+ * <Combobox
10
+ * options={cities}
11
+ * value={selected}
12
+ * onChange={setSelected}
13
+ * placeholder="Buscar cidade..."
14
+ * />
15
+ *
16
+ * Multi-select:
17
+ * <Combobox
18
+ * multi
19
+ * options={tags}
20
+ * value={selectedTags}
21
+ * onChange={setSelectedTags}
22
+ * />
23
+ *
24
+ * Suporta filtro custom (por default usa includes case-insensitive em label).
25
+ */
26
+ interface ComboboxOption {
27
+ value: string;
28
+ label: string;
29
+ /** Subtítulo opcional (ex: email abaixo do nome). */
30
+ description?: string;
31
+ /** Ícone leading. */
32
+ icon?: ReactNode;
33
+ disabled?: boolean;
34
+ }
35
+ interface ComboboxSingleProps {
36
+ multi?: false;
37
+ value: string | null;
38
+ onChange: (value: string | null) => void;
39
+ }
40
+ interface ComboboxMultiProps {
41
+ multi: true;
42
+ value: string[];
43
+ onChange: (value: string[]) => void;
44
+ }
45
+ type ComboboxProps = (ComboboxSingleProps | ComboboxMultiProps) & {
46
+ options: ComboboxOption[];
47
+ placeholder?: string;
48
+ /** Filtro custom. default = includes case-insensitive em label/description. */
49
+ filter?: (option: ComboboxOption, query: string) => boolean;
50
+ /** Texto quando lista vazia. default = "Nada encontrado". */
51
+ emptyMessage?: string;
52
+ /** Largura. default = full. */
53
+ width?: 'full' | 'hug';
54
+ size?: 'sm' | 'md';
55
+ disabled?: boolean;
56
+ className?: string;
57
+ 'aria-label'?: string;
58
+ /** Customiza o conteúdo do trigger fechado (ex: só o ícone). default = ícone + label. */
59
+ triggerContent?: (selected: ComboboxOption | null) => ReactNode;
60
+ /** Largura mínima do dropdown em px. Útil quando o trigger é compacto (width="hug"). */
61
+ dropdownMinWidth?: number;
62
+ /** Buscar→criar: quando a busca não casa com nenhuma opção (por label), mostra uma
63
+ * linha "criar" que chama onCreate(query). Campo padrão tipo cliente/fornecedor. */
64
+ onCreate?: (query: string) => void;
65
+ /** Rótulo da linha de criar. default = `+ "<query>"`. Passe localizado. */
66
+ createLabel?: (query: string) => string;
67
+ };
68
+ declare const Combobox: react.ForwardRefExoticComponent<ComboboxProps & react.RefAttributes<HTMLButtonElement>>;
69
+
70
+ export { Combobox, type ComboboxOption, type ComboboxProps };
@@ -0,0 +1,213 @@
1
+ "use client";
2
+ import "../chunk-ORMEWXMH.js";
3
+ import { forwardRef, useState, useRef, useEffect, useId, useImperativeHandle } from "react";
4
+ import { ChevronDown, Check, Search as SearchIcon, X, Plus } from "lucide-react";
5
+ import { Popover } from "./popover.js";
6
+ const SIZE_CLASS = {
7
+ sm: "h-8 text-caption px-2.5",
8
+ md: "h-10 text-label px-3"
9
+ };
10
+ function defaultFilter(opt, q) {
11
+ var _a, _b;
12
+ if (!q) return true;
13
+ const lower = q.toLowerCase();
14
+ return opt.label.toLowerCase().includes(lower) || ((_b = (_a = opt.description) == null ? void 0 : _a.toLowerCase().includes(lower)) != null ? _b : false);
15
+ }
16
+ const Combobox = forwardRef(function Combobox2(props, ref) {
17
+ var _a;
18
+ const {
19
+ options,
20
+ placeholder = "Selecione\u2026",
21
+ filter = defaultFilter,
22
+ emptyMessage = "Nada encontrado",
23
+ width = "full",
24
+ size = "md",
25
+ disabled = false,
26
+ className = "",
27
+ triggerContent,
28
+ dropdownMinWidth,
29
+ onCreate,
30
+ createLabel = (q) => `+ "${q}"`
31
+ } = props;
32
+ const isMulti = props.multi === true;
33
+ const [open, setOpen] = useState(false);
34
+ const [query, setQuery] = useState("");
35
+ const [activeIdx, setActiveIdx] = useState(0);
36
+ const [dropdownWidth, setDropdownWidth] = useState(0);
37
+ const triggerRef = useRef(null);
38
+ const inputRef = useRef(null);
39
+ const id = useId();
40
+ useImperativeHandle(ref, () => triggerRef.current);
41
+ useEffect(() => {
42
+ if (open && triggerRef.current) {
43
+ setDropdownWidth(triggerRef.current.offsetWidth);
44
+ }
45
+ }, [open]);
46
+ useEffect(() => {
47
+ if (open) {
48
+ requestAnimationFrame(() => {
49
+ var _a2;
50
+ return (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
51
+ });
52
+ setActiveIdx(0);
53
+ } else {
54
+ setQuery("");
55
+ }
56
+ }, [open]);
57
+ const filtered = options.filter((o) => filter(o, query));
58
+ const trimmed = query.trim();
59
+ const showCreate = !!onCreate && trimmed.length > 0 && !options.some((o) => o.label.toLowerCase() === trimmed.toLowerCase());
60
+ function doCreate() {
61
+ if (!onCreate || !trimmed) return;
62
+ onCreate(trimmed);
63
+ setOpen(false);
64
+ }
65
+ function isSelected(opt) {
66
+ return isMulti ? props.value.includes(opt.value) : props.value === opt.value;
67
+ }
68
+ function selectedOption() {
69
+ var _a2;
70
+ if (isMulti) return null;
71
+ return (_a2 = options.find((o) => o.value === props.value)) != null ? _a2 : null;
72
+ }
73
+ function handleSelect(opt) {
74
+ if (opt.disabled) return;
75
+ if (isMulti) {
76
+ const current = props.value;
77
+ const next = current.includes(opt.value) ? current.filter((v) => v !== opt.value) : [...current, opt.value];
78
+ props.onChange(next);
79
+ } else {
80
+ props.onChange(opt.value);
81
+ setOpen(false);
82
+ }
83
+ }
84
+ function handleKey(e) {
85
+ if (e.key === "ArrowDown") {
86
+ e.preventDefault();
87
+ setActiveIdx((i) => Math.min(i + 1, filtered.length - 1));
88
+ } else if (e.key === "ArrowUp") {
89
+ e.preventDefault();
90
+ setActiveIdx((i) => Math.max(i - 1, 0));
91
+ } else if (e.key === "Enter") {
92
+ e.preventDefault();
93
+ const opt = filtered[activeIdx];
94
+ if (opt) handleSelect(opt);
95
+ else if (showCreate) doCreate();
96
+ } else if (e.key === "Escape") {
97
+ setOpen(false);
98
+ }
99
+ }
100
+ const sel = selectedOption();
101
+ const triggerLabel = isMulti ? props.value.length === 0 ? placeholder : `${props.value.length} selecionado(s)` : (_a = sel == null ? void 0 : sel.label) != null ? _a : placeholder;
102
+ const isPlaceholder = isMulti ? props.value.length === 0 : !sel;
103
+ const dropdown = /* @__PURE__ */ React.createElement(
104
+ "div",
105
+ {
106
+ id,
107
+ role: "listbox",
108
+ style: { width: Math.max(dropdownWidth, dropdownMinWidth != null ? dropdownMinWidth : 0) || void 0 },
109
+ className: "max-h-72 overflow-hidden rounded-(--radius-control) border border-stroke-control bg-surface-default shadow-lg flex flex-col"
110
+ },
111
+ /* @__PURE__ */ React.createElement("div", { className: "relative shrink-0 border-b border-stroke-subtle" }, /* @__PURE__ */ React.createElement(SearchIcon, { size: 14, className: "absolute left-3 top-1/2 -translate-y-1/2 text-fg-muted pointer-events-none" }), /* @__PURE__ */ React.createElement(
112
+ "input",
113
+ {
114
+ ref: inputRef,
115
+ type: "text",
116
+ value: query,
117
+ onChange: (e) => {
118
+ setQuery(e.target.value);
119
+ setActiveIdx(0);
120
+ },
121
+ onKeyDown: handleKey,
122
+ placeholder: "Buscar\u2026",
123
+ className: "w-full pl-9 pr-8 py-2 text-label bg-transparent outline-none text-fg-primary placeholder:text-fg-placeholder",
124
+ "aria-label": "Buscar op\xE7\xF5es"
125
+ }
126
+ ), query && /* @__PURE__ */ React.createElement(
127
+ "button",
128
+ {
129
+ type: "button",
130
+ onClick: () => {
131
+ var _a2;
132
+ setQuery("");
133
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
134
+ },
135
+ "aria-label": "Limpar",
136
+ className: "absolute right-2 top-1/2 -translate-y-1/2 text-fg-muted hover:text-fg-primary p-1"
137
+ },
138
+ /* @__PURE__ */ React.createElement(X, { size: 12 })
139
+ )),
140
+ /* @__PURE__ */ React.createElement("div", { className: "overflow-y-auto py-1 max-h-60" }, filtered.length === 0 && !showCreate ? /* @__PURE__ */ React.createElement("div", { className: "px-3 py-3 text-label text-fg-muted text-center" }, emptyMessage) : /* @__PURE__ */ React.createElement(React.Fragment, null, filtered.map((opt, idx) => {
141
+ const selected = isSelected(opt);
142
+ const active = idx === activeIdx;
143
+ return /* @__PURE__ */ React.createElement(
144
+ "button",
145
+ {
146
+ key: opt.value,
147
+ type: "button",
148
+ role: "option",
149
+ "aria-selected": selected,
150
+ disabled: opt.disabled,
151
+ onClick: () => handleSelect(opt),
152
+ onMouseEnter: () => setActiveIdx(idx),
153
+ className: [
154
+ "w-full flex items-start gap-2.5 px-3 py-2 text-label text-left transition-colors",
155
+ "disabled:cursor-not-allowed disabled:opacity-50",
156
+ active ? "bg-surface-raised" : "hover:bg-surface-raised"
157
+ ].join(" ")
158
+ },
159
+ opt.icon && /* @__PURE__ */ React.createElement("span", { className: "shrink-0 text-fg-muted mt-0.5 [&_svg]:size-4" }, opt.icon),
160
+ /* @__PURE__ */ React.createElement("span", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React.createElement("span", { className: "block text-fg-primary truncate" }, opt.label), opt.description && /* @__PURE__ */ React.createElement("span", { className: "block text-caption text-fg-muted truncate" }, opt.description)),
161
+ selected && /* @__PURE__ */ React.createElement(Check, { size: 14, className: "shrink-0 text-fg-muted mt-0.5" })
162
+ );
163
+ }), showCreate && /* @__PURE__ */ React.createElement(
164
+ "button",
165
+ {
166
+ type: "button",
167
+ onClick: doCreate,
168
+ className: "w-full flex items-center gap-2.5 px-3 py-2 text-label text-left text-fg-primary transition-colors hover:bg-surface-raised border-t border-stroke-subtle"
169
+ },
170
+ /* @__PURE__ */ React.createElement(Plus, { size: 14, className: "shrink-0 text-fg-muted" }),
171
+ /* @__PURE__ */ React.createElement("span", { className: "truncate" }, createLabel(trimmed))
172
+ )))
173
+ );
174
+ return /* @__PURE__ */ React.createElement("div", { className: ["relative inline-block", width === "full" ? "w-full" : "w-auto", className].join(" ") }, /* @__PURE__ */ React.createElement(
175
+ Popover,
176
+ {
177
+ trigger: /* @__PURE__ */ React.createElement(
178
+ "button",
179
+ {
180
+ ref: triggerRef,
181
+ type: "button",
182
+ onClick: (e) => {
183
+ e.stopPropagation();
184
+ if (!disabled) setOpen((o) => !o);
185
+ },
186
+ disabled,
187
+ "aria-haspopup": "listbox",
188
+ "aria-expanded": open,
189
+ "aria-controls": id,
190
+ className: [
191
+ "w-full flex items-center justify-between gap-2 rounded-(--radius-control) border border-stroke-control bg-surface-default",
192
+ "transition-colors focus:outline-none focus:border-stroke-focus focus:ring-2 focus:ring-stroke-focus/20",
193
+ "disabled:opacity-50 disabled:cursor-not-allowed",
194
+ SIZE_CLASS[size]
195
+ ].join(" ")
196
+ },
197
+ /* @__PURE__ */ React.createElement("span", { className: ["truncate inline-flex items-center", isPlaceholder ? "text-fg-placeholder" : "text-fg-primary"].join(" ") }, triggerContent ? triggerContent(sel) : /* @__PURE__ */ React.createElement(React.Fragment, null, (sel == null ? void 0 : sel.icon) && /* @__PURE__ */ React.createElement("span", { className: "inline-flex mr-2 align-middle [&_svg]:size-4" }, sel.icon), triggerLabel)),
198
+ /* @__PURE__ */ React.createElement(ChevronDown, { size: 16, className: "text-fg-muted shrink-0" })
199
+ ),
200
+ triggerClassName: width === "full" ? "block w-full" : "inline-block",
201
+ placement: "bottom-start",
202
+ offset: 4,
203
+ open,
204
+ onOpenChange: (v) => {
205
+ if (!disabled) setOpen(v);
206
+ }
207
+ },
208
+ () => dropdown
209
+ ));
210
+ });
211
+ export {
212
+ Combobox
213
+ };
@@ -0,0 +1,33 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * ContextMenu — menu acionado por clique direito (contextmenu event).
6
+ *
7
+ * Uso:
8
+ * <ContextMenu menu={({ close }) => (
9
+ * <>
10
+ * <MenuItem onClick={() => { copy(); close(); }}>Copiar</MenuItem>
11
+ * <MenuItem onClick={() => { paste(); close(); }}>Colar</MenuItem>
12
+ * </>
13
+ * )}>
14
+ * <div className="border p-4">Clique direito aqui</div>
15
+ * </ContextMenu>
16
+ *
17
+ * Posiciona no ponto exato do clique. Fecha em ESC, clique fora, scroll.
18
+ */
19
+ interface ContextMenuProps {
20
+ children: ReactNode;
21
+ /** Conteúdo do menu (mesmo formato do Menu — MenuItem, MenuSeparator, etc). */
22
+ menu: ReactNode | ((api: {
23
+ close: () => void;
24
+ }) => ReactNode);
25
+ /** Largura mínima em px. default = 180. */
26
+ minWidth?: number;
27
+ /** Desabilita o context menu. default = false. */
28
+ disabled?: boolean;
29
+ className?: string;
30
+ }
31
+ declare const ContextMenu: react.ForwardRefExoticComponent<ContextMenuProps & react.RefAttributes<HTMLDivElement>>;
32
+
33
+ export { ContextMenu, type ContextMenuProps };