trama-ui 0.1.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 (285) hide show
  1. package/CATALOG.md +1237 -0
  2. package/LICENSE +21 -0
  3. package/README.md +78 -0
  4. package/THIRD-PARTY.md +4 -0
  5. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown.png +0 -0
  6. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_inlay.png +0 -0
  7. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/brown_pressed.png +0 -0
  8. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey.png +0 -0
  9. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_inlay.png +0 -0
  10. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/grey_pressed.png +0 -0
  11. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan.png +0 -0
  12. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_inlay.png +0 -0
  13. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/tan_pressed.png +0 -0
  14. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white.png +0 -0
  15. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_inlay.png +0 -0
  16. package/assets/pixel/kenney-pixel-ui/9-slice/Ancient/white_pressed.png +0 -0
  17. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue.png +0 -0
  18. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/blue_pressed.png +0 -0
  19. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green.png +0 -0
  20. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/green_pressed.png +0 -0
  21. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey.png +0 -0
  22. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/grey_pressed.png +0 -0
  23. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red.png +0 -0
  24. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/red_pressed.png +0 -0
  25. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow.png +0 -0
  26. package/assets/pixel/kenney-pixel-ui/9-slice/Colored/yellow_pressed.png +0 -0
  27. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue.png +0 -0
  28. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/blue_pressed.png +0 -0
  29. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green.png +0 -0
  30. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/green_pressed.png +0 -0
  31. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red.png +0 -0
  32. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/red_pressed.png +0 -0
  33. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow.png +0 -0
  34. package/assets/pixel/kenney-pixel-ui/9-slice/Outline/yellow_pressed.png +0 -0
  35. package/assets/pixel/kenney-pixel-ui/9-slice/list.png +0 -0
  36. package/assets/pixel/kenney-pixel-ui/9-slice/space.png +0 -0
  37. package/assets/pixel/kenney-pixel-ui/9-slice/space_inlay.png +0 -0
  38. package/assets/pixel/kenney-pixel-ui/License.txt +23 -0
  39. package/assets/pixel/kenney-pixel-ui/spritesheet/sheet.png +0 -0
  40. package/bin/trama-ui.mjs +27 -0
  41. package/dist/components/ui/Accordion.d.ts +12 -0
  42. package/dist/components/ui/Accordion.js +12 -0
  43. package/dist/components/ui/Alert.d.ts +15 -0
  44. package/dist/components/ui/Alert.js +15 -0
  45. package/dist/components/ui/ArticlesSection.d.ts +30 -0
  46. package/dist/components/ui/ArticlesSection.js +18 -0
  47. package/dist/components/ui/AsciiBackground.d.ts +58 -0
  48. package/dist/components/ui/AsciiBackground.js +173 -0
  49. package/dist/components/ui/AsciiCard.d.ts +78 -0
  50. package/dist/components/ui/AsciiCard.js +253 -0
  51. package/dist/components/ui/AsciiChart.d.ts +18 -0
  52. package/dist/components/ui/AsciiChart.js +47 -0
  53. package/dist/components/ui/Badge.d.ts +15 -0
  54. package/dist/components/ui/Badge.js +8 -0
  55. package/dist/components/ui/BitmapText.d.ts +25 -0
  56. package/dist/components/ui/BitmapText.js +21 -0
  57. package/dist/components/ui/BlogCard.d.ts +33 -0
  58. package/dist/components/ui/BlogCard.js +13 -0
  59. package/dist/components/ui/Breadcrumbs.d.ts +9 -0
  60. package/dist/components/ui/Breadcrumbs.js +7 -0
  61. package/dist/components/ui/Button.d.ts +74 -0
  62. package/dist/components/ui/Button.js +48 -0
  63. package/dist/components/ui/CTASection.d.ts +13 -0
  64. package/dist/components/ui/CTASection.js +7 -0
  65. package/dist/components/ui/Carousel.d.ts +30 -0
  66. package/dist/components/ui/Carousel.js +80 -0
  67. package/dist/components/ui/CheckboxGroup.d.ts +11 -0
  68. package/dist/components/ui/CheckboxGroup.js +20 -0
  69. package/dist/components/ui/CodeBlock.d.ts +11 -0
  70. package/dist/components/ui/CodeBlock.js +20 -0
  71. package/dist/components/ui/ContactForm.d.ts +25 -0
  72. package/dist/components/ui/ContactForm.js +26 -0
  73. package/dist/components/ui/CrtOverlay.d.ts +10 -0
  74. package/dist/components/ui/CrtOverlay.js +5 -0
  75. package/dist/components/ui/Divider.d.ts +12 -0
  76. package/dist/components/ui/Divider.js +7 -0
  77. package/dist/components/ui/Drawer.d.ts +16 -0
  78. package/dist/components/ui/Drawer.js +25 -0
  79. package/dist/components/ui/Dropdown.d.ts +10 -0
  80. package/dist/components/ui/Dropdown.js +21 -0
  81. package/dist/components/ui/FAQSection.d.ts +14 -0
  82. package/dist/components/ui/FAQSection.js +7 -0
  83. package/dist/components/ui/FeatureGrid.d.ts +18 -0
  84. package/dist/components/ui/FeatureGrid.js +14 -0
  85. package/dist/components/ui/Footer.d.ts +14 -0
  86. package/dist/components/ui/Footer.js +16 -0
  87. package/dist/components/ui/GlyphCursor.d.ts +22 -0
  88. package/dist/components/ui/GlyphCursor.js +50 -0
  89. package/dist/components/ui/GridBackground.d.ts +15 -0
  90. package/dist/components/ui/GridBackground.js +6 -0
  91. package/dist/components/ui/Hero.d.ts +21 -0
  92. package/dist/components/ui/Hero.js +12 -0
  93. package/dist/components/ui/HoverFX.d.ts +29 -0
  94. package/dist/components/ui/HoverFX.js +75 -0
  95. package/dist/components/ui/Icon.d.ts +33 -0
  96. package/dist/components/ui/Icon.js +29 -0
  97. package/dist/components/ui/ImageGallery.d.ts +31 -0
  98. package/dist/components/ui/ImageGallery.js +79 -0
  99. package/dist/components/ui/LogoCloud.d.ts +12 -0
  100. package/dist/components/ui/LogoCloud.js +9 -0
  101. package/dist/components/ui/MagneticButton.d.ts +12 -0
  102. package/dist/components/ui/MagneticButton.js +49 -0
  103. package/dist/components/ui/Marquee.d.ts +29 -0
  104. package/dist/components/ui/Marquee.js +15 -0
  105. package/dist/components/ui/Modal.d.ts +40 -0
  106. package/dist/components/ui/Modal.js +51 -0
  107. package/dist/components/ui/NavBar.d.ts +14 -0
  108. package/dist/components/ui/NavBar.js +12 -0
  109. package/dist/components/ui/NeonSign.d.ts +14 -0
  110. package/dist/components/ui/NeonSign.js +7 -0
  111. package/dist/components/ui/OtpInput.d.ts +9 -0
  112. package/dist/components/ui/OtpInput.js +27 -0
  113. package/dist/components/ui/Pagination.d.ts +10 -0
  114. package/dist/components/ui/Pagination.js +24 -0
  115. package/dist/components/ui/Panel.d.ts +21 -0
  116. package/dist/components/ui/Panel.js +8 -0
  117. package/dist/components/ui/PixelFrame.d.ts +61 -0
  118. package/dist/components/ui/PixelFrame.js +29 -0
  119. package/dist/components/ui/Presence.d.ts +51 -0
  120. package/dist/components/ui/Presence.js +343 -0
  121. package/dist/components/ui/PricingCard.d.ts +30 -0
  122. package/dist/components/ui/PricingCard.js +21 -0
  123. package/dist/components/ui/PricingSection.d.ts +35 -0
  124. package/dist/components/ui/PricingSection.js +21 -0
  125. package/dist/components/ui/Progress.d.ts +17 -0
  126. package/dist/components/ui/Progress.js +9 -0
  127. package/dist/components/ui/RadioGroup.d.ts +10 -0
  128. package/dist/components/ui/RadioGroup.js +11 -0
  129. package/dist/components/ui/RangeSlider.d.ts +14 -0
  130. package/dist/components/ui/RangeSlider.js +11 -0
  131. package/dist/components/ui/Reveal.d.ts +48 -0
  132. package/dist/components/ui/Reveal.js +181 -0
  133. package/dist/components/ui/SceneFlash.d.ts +14 -0
  134. package/dist/components/ui/SceneFlash.js +51 -0
  135. package/dist/components/ui/ScrambleText.d.ts +31 -0
  136. package/dist/components/ui/ScrambleText.js +51 -0
  137. package/dist/components/ui/ScrollArea.d.ts +15 -0
  138. package/dist/components/ui/ScrollArea.js +18 -0
  139. package/dist/components/ui/SectionHeader.d.ts +12 -0
  140. package/dist/components/ui/SectionHeader.js +6 -0
  141. package/dist/components/ui/Select.d.ts +17 -0
  142. package/dist/components/ui/Select.js +31 -0
  143. package/dist/components/ui/Skeleton.d.ts +12 -0
  144. package/dist/components/ui/Skeleton.js +18 -0
  145. package/dist/components/ui/Spinner.d.ts +15 -0
  146. package/dist/components/ui/Spinner.js +24 -0
  147. package/dist/components/ui/Spotlight.d.ts +16 -0
  148. package/dist/components/ui/Spotlight.js +16 -0
  149. package/dist/components/ui/Sprite.d.ts +27 -0
  150. package/dist/components/ui/Sprite.js +19 -0
  151. package/dist/components/ui/StatCounter.d.ts +24 -0
  152. package/dist/components/ui/StatCounter.js +70 -0
  153. package/dist/components/ui/StatsSection.d.ts +14 -0
  154. package/dist/components/ui/StatsSection.js +13 -0
  155. package/dist/components/ui/Stepper.d.ts +10 -0
  156. package/dist/components/ui/Stepper.js +7 -0
  157. package/dist/components/ui/Table.d.ts +10 -0
  158. package/dist/components/ui/Table.js +8 -0
  159. package/dist/components/ui/Tabs.d.ts +12 -0
  160. package/dist/components/ui/Tabs.js +18 -0
  161. package/dist/components/ui/TeamSection.d.ts +18 -0
  162. package/dist/components/ui/TeamSection.js +19 -0
  163. package/dist/components/ui/TerminalTyper.d.ts +20 -0
  164. package/dist/components/ui/TerminalTyper.js +45 -0
  165. package/dist/components/ui/Testimonial.d.ts +19 -0
  166. package/dist/components/ui/Testimonial.js +9 -0
  167. package/dist/components/ui/TestimonialSection.d.ts +20 -0
  168. package/dist/components/ui/TestimonialSection.js +15 -0
  169. package/dist/components/ui/TextField.d.ts +26 -0
  170. package/dist/components/ui/TextField.js +13 -0
  171. package/dist/components/ui/TextmodeBackground.d.ts +52 -0
  172. package/dist/components/ui/TextmodeBackground.js +83 -0
  173. package/dist/components/ui/Timeline.d.ts +13 -0
  174. package/dist/components/ui/Timeline.js +8 -0
  175. package/dist/components/ui/Toast.d.ts +30 -0
  176. package/dist/components/ui/Toast.js +47 -0
  177. package/dist/components/ui/Toggle.d.ts +13 -0
  178. package/dist/components/ui/Toggle.js +10 -0
  179. package/dist/components/ui/Tooltip.d.ts +20 -0
  180. package/dist/components/ui/Tooltip.js +20 -0
  181. package/dist/components/ui/Typewriter.d.ts +20 -0
  182. package/dist/components/ui/Typewriter.js +46 -0
  183. package/dist/components/ui/VideoPlayer.d.ts +81 -0
  184. package/dist/components/ui/VideoPlayer.js +379 -0
  185. package/dist/components/ui/fill.d.ts +32 -0
  186. package/dist/components/ui/fill.js +27 -0
  187. package/dist/components/ui/intent.d.ts +20 -0
  188. package/dist/components/ui/intent.js +23 -0
  189. package/dist/components/ui/styles/kit.css +31 -0
  190. package/dist/components/ui/styles/ui-carousel.css +113 -0
  191. package/dist/components/ui/styles/ui-kit.css +1135 -0
  192. package/dist/components/ui/styles/ui-video.css +75 -0
  193. package/dist/components/ui/swiperCss.d.ts +15 -0
  194. package/dist/components/ui/swiperCss.js +15 -0
  195. package/dist/components/ui/variants.d.ts +12 -0
  196. package/dist/components/ui/variants.js +10 -0
  197. package/dist/index.d.ts +77 -0
  198. package/dist/index.js +77 -0
  199. package/dist/lib/asciify/painters.d.ts +25 -0
  200. package/dist/lib/asciify/painters.js +604 -0
  201. package/dist/lib/asciify/pointer.d.ts +12 -0
  202. package/dist/lib/asciify/pointer.js +54 -0
  203. package/dist/lib/asciify/shape-painters.d.ts +44 -0
  204. package/dist/lib/asciify/shape-painters.js +335 -0
  205. package/dist/lib/asciify/theme-painters.d.ts +8 -0
  206. package/dist/lib/asciify/theme-painters.js +437 -0
  207. package/dist/lib/cells.d.ts +46 -0
  208. package/dist/lib/cells.js +114 -0
  209. package/dist/lib/scroll/store.d.ts +21 -0
  210. package/dist/lib/scroll/store.js +25 -0
  211. package/dist/lib/sketches/aurora.d.ts +3 -0
  212. package/dist/lib/sketches/aurora.js +42 -0
  213. package/dist/lib/sketches/fire.d.ts +3 -0
  214. package/dist/lib/sketches/fire.js +80 -0
  215. package/dist/lib/sketches/flow.d.ts +3 -0
  216. package/dist/lib/sketches/flow.js +70 -0
  217. package/dist/lib/sketches/life.d.ts +3 -0
  218. package/dist/lib/sketches/life.js +107 -0
  219. package/dist/lib/sketches/matrix.d.ts +3 -0
  220. package/dist/lib/sketches/matrix.js +64 -0
  221. package/dist/lib/sketches/ocean.d.ts +3 -0
  222. package/dist/lib/sketches/ocean.js +56 -0
  223. package/dist/lib/sketches/plasma.d.ts +3 -0
  224. package/dist/lib/sketches/plasma.js +29 -0
  225. package/dist/lib/sketches/registry.d.ts +6 -0
  226. package/dist/lib/sketches/registry.js +60 -0
  227. package/dist/lib/sketches/ripples.d.ts +3 -0
  228. package/dist/lib/sketches/ripples.js +84 -0
  229. package/dist/lib/sketches/scroll-assemble.d.ts +8 -0
  230. package/dist/lib/sketches/scroll-assemble.js +90 -0
  231. package/dist/lib/sketches/scroll-parallax.d.ts +6 -0
  232. package/dist/lib/sketches/scroll-parallax.js +76 -0
  233. package/dist/lib/sketches/scroll-rain.d.ts +6 -0
  234. package/dist/lib/sketches/scroll-rain.js +82 -0
  235. package/dist/lib/sketches/scroll-stats.d.ts +7 -0
  236. package/dist/lib/sketches/scroll-stats.js +81 -0
  237. package/dist/lib/sketches/scroll-terminal.d.ts +6 -0
  238. package/dist/lib/sketches/scroll-terminal.js +87 -0
  239. package/dist/lib/sketches/scroll-zoom.d.ts +7 -0
  240. package/dist/lib/sketches/scroll-zoom.js +49 -0
  241. package/dist/lib/sketches/starfield.d.ts +3 -0
  242. package/dist/lib/sketches/starfield.js +54 -0
  243. package/dist/lib/sketches/text-3d.d.ts +6 -0
  244. package/dist/lib/sketches/text-3d.js +62 -0
  245. package/dist/lib/sketches/text-combo-figlet.d.ts +6 -0
  246. package/dist/lib/sketches/text-combo-figlet.js +91 -0
  247. package/dist/lib/sketches/text-glitch.d.ts +3 -0
  248. package/dist/lib/sketches/text-glitch.js +57 -0
  249. package/dist/lib/sketches/text-marquee.d.ts +3 -0
  250. package/dist/lib/sketches/text-marquee.js +35 -0
  251. package/dist/lib/sketches/text-particles.d.ts +9 -0
  252. package/dist/lib/sketches/text-particles.js +91 -0
  253. package/dist/lib/sketches/text-rain.d.ts +3 -0
  254. package/dist/lib/sketches/text-rain.js +73 -0
  255. package/dist/lib/sketches/text-scramble.d.ts +6 -0
  256. package/dist/lib/sketches/text-scramble.js +49 -0
  257. package/dist/lib/sketches/text-wave.d.ts +3 -0
  258. package/dist/lib/sketches/text-wave.js +43 -0
  259. package/dist/lib/sketches/theme-orbit.d.ts +6 -0
  260. package/dist/lib/sketches/theme-orbit.js +119 -0
  261. package/dist/lib/sketches/theme-pulse.d.ts +7 -0
  262. package/dist/lib/sketches/theme-pulse.js +84 -0
  263. package/dist/lib/sketches/tunnel.d.ts +3 -0
  264. package/dist/lib/sketches/tunnel.js +28 -0
  265. package/dist/lib/text/banner.d.ts +5 -0
  266. package/dist/lib/text/banner.js +35 -0
  267. package/dist/lib/text/mask.d.ts +40 -0
  268. package/dist/lib/text/mask.js +143 -0
  269. package/dist/lib/text/store.d.ts +23 -0
  270. package/dist/lib/text/store.js +86 -0
  271. package/dist/lib/ui/card-fields.d.ts +52 -0
  272. package/dist/lib/ui/card-fields.js +193 -0
  273. package/dist/lib/ui/fonts.d.ts +56 -0
  274. package/dist/lib/ui/fonts.js +94 -0
  275. package/dist/lib/ui/fx.d.ts +42 -0
  276. package/dist/lib/ui/fx.js +162 -0
  277. package/dist/lib/ui/icons.d.ts +437 -0
  278. package/dist/lib/ui/icons.js +314 -0
  279. package/dist/lib/ui/placeholder.d.ts +11 -0
  280. package/dist/lib/ui/placeholder.js +65 -0
  281. package/dist/lib/ui/tokens.d.ts +49 -0
  282. package/dist/lib/ui/tokens.js +129 -0
  283. package/dist/lib/ui/useTokens.d.ts +10 -0
  284. package/dist/lib/ui/useTokens.js +32 -0
  285. package/package.json +80 -0
@@ -0,0 +1,35 @@
1
+ import { type PricingCardProps } from "./PricingCard.js";
2
+ import type { CardFill } from "./fill.js";
3
+ import { type Variant } from "./variants.js";
4
+ export type PricingSectionProps = {
5
+ kicker?: string;
6
+ title?: string;
7
+ subtitle?: string;
8
+ /**
9
+ * un plan por línea: «nombre|precio|periodo|destacado(si/no)|características separadas por ;|descripción|precio anual».
10
+ * Las dos últimas son opcionales; una característica que empieza por «-» se muestra como no incluida. Si algún plan
11
+ * trae precio anual, aparece el selector Mensual / Anual.
12
+ */
13
+ plans?: string;
14
+ /** grid = rejilla · list = una columna de tarjetas horizontales · carousel = carrusel de Swiper */
15
+ layout?: "grid" | "list" | "carousel";
16
+ /** maquetación de cada tarjeta en `grid` y `carousel` */
17
+ cardLayout?: "classic" | "compact";
18
+ /** cómo se destaca el plan marcado (ver PricingCard) */
19
+ highlight?: PricingCardProps["highlight"];
20
+ /** fondo de las tarjetas no destacadas (ver fill.ts) */
21
+ fill?: CardFill;
22
+ /** texto del periodo cuando se elige facturación anual */
23
+ yearlyPeriod?: string;
24
+ /** etiqueta del ahorro junto a «Anual» (vacío = sin etiqueta) */
25
+ yearlyNote?: string;
26
+ /** con layout="carousel": tarjetas visibles en pantallas anchas */
27
+ perView?: number;
28
+ /** con layout="carousel": segundos entre tarjetas (0 = manual) */
29
+ autoplay?: number;
30
+ variant?: Variant;
31
+ className?: string;
32
+ };
33
+ export declare const DEFAULT_PLANS: string;
34
+ /** Cabecera + planes (`PricingCard`), con selector mensual/anual opcional: la sección de precios lista para pegar. */
35
+ export default function PricingSection({ kicker, title, subtitle, plans, layout, cardLayout, highlight, fill, yearlyPeriod, yearlyNote, perView, autoplay, variant, className, }: PricingSectionProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from "react";
4
+ import PricingCard from "./PricingCard.js";
5
+ import Carousel from "./Carousel.js";
6
+ import SectionHeader from "./SectionHeader.js";
7
+ import { vcls } from "./variants.js";
8
+ export const DEFAULT_PLANS = "Básico|9 €|/ mes|no|Un proyecto;Exportación PNG;Soporte por email;-Dominio propio|Para probar sin compromiso|90 €\n" +
9
+ "Pro|29 €|/ mes|si|Proyectos ilimitados;Soporte prioritario;Exportación a PNG y SVG;Dominio propio|Para quien publica cada semana|290 €\n" +
10
+ "Equipo|79 €|/ mes|no|Todo lo de Pro;5 puestos;Panel de administración;Facturación centralizada|Para estudios y agencias|790 €";
11
+ /** Cabecera + planes (`PricingCard`), con selector mensual/anual opcional: la sección de precios lista para pegar. */
12
+ export default function PricingSection({ kicker = "Precios", title = "Un plan para cada tamaño", subtitle = "Cambia o cancela cuando quieras.", plans = DEFAULT_PLANS, layout = "grid", cardLayout = "classic", highlight = "invert", fill = "surface", yearlyPeriod = "/ año", yearlyNote = "2 meses gratis", perView = 3, autoplay = 0, variant = "glass", className = "", }) {
13
+ const rows = useMemo(() => plans.split("\n").map((l) => l.split("|").map((x) => x.trim())).filter((r) => r[0]), [plans]);
14
+ const hasYearly = rows.some((r) => r[6]);
15
+ const [yearly, setYearly] = useState(false);
16
+ const cards = rows.map(([plan, price, period, hl, features, description, yearlyPrice], i) => {
17
+ const useYear = yearly && !!yearlyPrice;
18
+ return (_jsx(PricingCard, { plan: plan, description: description, price: useYear ? yearlyPrice : price, period: useYear ? yearlyPeriod : period, note: useYear ? `equivale a ${price} ${period}`.replace(/\s+/g, " ") : "", highlighted: hl === "si", highlight: highlight, features: (features ?? "").split(";").filter(Boolean).join("\n"), layout: layout === "list" ? "horizontal" : cardLayout, fill: fill, variant: variant }, plan + i));
19
+ });
20
+ return (_jsxs("section", { className: `ui-pricing ${className}`, children: [_jsx(SectionHeader, { kicker: kicker, title: title, subtitle: subtitle, align: "center", variant: "minimal" }), hasYearly && (_jsxs("div", { className: `ui-billing ui-surface ${vcls(variant)}`, role: "group", "aria-label": "Periodo de facturaci\u00F3n", children: [_jsx("button", { type: "button", "aria-pressed": !yearly, onClick: () => setYearly(false), children: "Mensual" }), _jsxs("button", { type: "button", "aria-pressed": yearly, onClick: () => setYearly(true), children: ["Anual", yearlyNote && _jsx("span", { className: "ui-billing__note", children: yearlyNote })] })] })), layout === "carousel" ? (_jsx(Carousel, { perView: perView, autoplay: autoplay, variant: variant, label: title, children: cards })) : (_jsx("div", { className: `ui-pricing__grid ui-pricing__grid--${layout}`, children: cards }))] }));
21
+ }
@@ -0,0 +1,17 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type ProgressProps = {
3
+ value?: number;
4
+ label?: string;
5
+ showValue?: boolean;
6
+ variant?: Variant;
7
+ /** franjas animadas en la barra */
8
+ striped?: boolean;
9
+ /** dibuja la barra con caracteres: [#####-----] */
10
+ ascii?: boolean;
11
+ /** caracteres del modo ascii: lleno y vacío */
12
+ fillChar?: string;
13
+ emptyChar?: string;
14
+ className?: string;
15
+ };
16
+ /** Barra de progreso. `ascii` la convierte en texto (por defecto ya lo es en retro y terminal cuando se activa). */
17
+ export default function Progress({ value, label, showValue, variant, striped, ascii, fillChar, emptyChar, className }: ProgressProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { vcls } from "./variants.js";
3
+ /** Barra de progreso. `ascii` la convierte en texto (por defecto ya lo es en retro y terminal cuando se activa). */
4
+ export default function Progress({ value = 62, label = "Subiendo", showValue = true, variant = "glass", striped = false, ascii = false, fillChar = "#", emptyChar = "-", className = "" }) {
5
+ const v = Math.max(0, Math.min(100, value));
6
+ const cells = 24;
7
+ const filled = Math.round((v / 100) * cells);
8
+ return (_jsxs("div", { className: `ui-progress ${striped ? "ui-progress--striped" : ""} ${vcls(variant)} ${className}`, style: { "--v": `${v}%` }, children: [_jsxs("div", { className: "ui-progress__head", children: [_jsx("span", { children: label }), showValue && _jsxs("output", { children: [Math.round(v), "%"] })] }), ascii ? (_jsxs("pre", { className: "ui-progress__ascii", role: "progressbar", "aria-valuenow": v, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": label, children: ["[", fillChar.repeat(filled), emptyChar.repeat(cells - filled), "]"] })) : (_jsx("div", { className: "ui-progress__track", role: "progressbar", "aria-valuenow": v, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": label, children: _jsx("div", { className: "ui-progress__fill" }) }))] }));
9
+ }
@@ -0,0 +1,10 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type RadioGroupProps = {
3
+ label?: string;
4
+ options?: string;
5
+ defaultValue?: string;
6
+ variant?: Variant;
7
+ className?: string;
8
+ };
9
+ /** Selección única entre varias opciones. Retro y terminal las muestran como texto `(•)`/`( )`. */
10
+ export default function RadioGroup({ label, options, defaultValue, variant, className }: RadioGroupProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useState } from "react";
4
+ import { vcls } from "./variants.js";
5
+ /** Selección única entre varias opciones. Retro y terminal las muestran como texto `(•)`/`( )`. */
6
+ export default function RadioGroup({ label = "Plan", options = "Mensual, Anual", defaultValue = "Mensual", variant = "glass", className = "" }) {
7
+ const list = useMemo(() => options.split(",").map((s) => s.trim()).filter(Boolean), [options]);
8
+ const [value, setValue] = useState(defaultValue);
9
+ useEffect(() => setValue(defaultValue), [defaultValue]);
10
+ return (_jsxs("fieldset", { className: `ui-rgroup ${vcls(variant)} ${className}`, children: [label && _jsx("legend", { children: label }), list.map((o) => (_jsxs("label", { className: "ui-rgroup__item", children: [_jsx("button", { type: "button", role: "radio", "aria-checked": o === value, className: `ui-rgroup__dot ${o === value ? "is-on" : ""}`, onClick: () => setValue(o) }), o] }, o)))] }));
11
+ }
@@ -0,0 +1,14 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type RangeSliderProps = {
3
+ label?: string;
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ defaultValue?: number;
8
+ unit?: string;
9
+ showValue?: boolean;
10
+ variant?: Variant;
11
+ className?: string;
12
+ };
13
+ /** Deslizador nativo con pista y pulgar adaptados a cada variante. */
14
+ export default function RangeSlider({ label, min, max, step, defaultValue, unit, showValue, variant, className }: RangeSliderProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { vcls } from "./variants.js";
5
+ /** Deslizador nativo con pista y pulgar adaptados a cada variante. */
6
+ export default function RangeSlider({ label = "Intensidad", min = 0, max = 100, step = 1, defaultValue = 40, unit = "%", showValue = true, variant = "glass", className = "" }) {
7
+ const [v, setV] = useState(defaultValue);
8
+ useEffect(() => setV(defaultValue), [defaultValue]);
9
+ const pct = ((v - min) / Math.max(1, max - min)) * 100;
10
+ return (_jsxs("div", { className: `ui-range ${vcls(variant)} ${className}`, style: { "--pct": `${pct}%` }, children: [_jsxs("div", { className: "ui-range__head", children: [_jsx("span", { children: label }), showValue && (_jsxs("output", { children: [v, unit] }))] }), _jsx("input", { type: "range", min: min, max: max, step: step, value: v, onChange: (e) => setV(parseFloat(e.target.value)), "aria-label": label })] }));
11
+ }
@@ -0,0 +1,48 @@
1
+ import { type CSSProperties, type ReactNode } from "react";
2
+ export declare const REVEAL_KINDS: readonly ["fade", "slide", "scale", "blur", "wipe", "flip", "iris"];
3
+ export type RevealKind = (typeof REVEAL_KINDS)[number];
4
+ export type RevealDirection = "up" | "down" | "left" | "right";
5
+ export type RevealEasing = "out" | "in-out" | "back" | "linear";
6
+ export type RevealProps = {
7
+ /** efecto; `rise` (= slide hacia arriba) y `stagger` (= slide + escalonado 0,12 s) se mantienen por compatibilidad */
8
+ kind?: RevealKind | "rise" | "stagger";
9
+ /** hacia dónde se mueve el contenido al entrar (slide, wipe, flip) */
10
+ direction?: RevealDirection;
11
+ /** distancia en px de slide (y del ligero desplazamiento de blur) */
12
+ distance?: number;
13
+ duration?: number;
14
+ delay?: number;
15
+ easing?: RevealEasing;
16
+ /** segundos entre hijos directos (0 = todo el bloque a la vez) */
17
+ stagger?: number;
18
+ /** trocea un texto plano en palabras o letras y las escalona (solo si `children` es texto) */
19
+ split?: "none" | "words" | "chars";
20
+ /** `mount`: al montar · `inview`: al entrar en pantalla · `scroll`: el progreso sigue al scroll (scrub) */
21
+ trigger?: "mount" | "inview" | "scroll";
22
+ /** con `inview`: solo la primera vez (true) o cada vez que vuelve a entrar (false) */
23
+ once?: boolean;
24
+ /** con `inview`: fracción visible (0–1) necesaria para disparar */
25
+ threshold?: number;
26
+ /** con `inview`: margen del viewport (rootMargin); el valor por defecto dispara un poco antes del borde inferior */
27
+ margin?: string;
28
+ /** etiqueta del contenedor */
29
+ as?: "div" | "section" | "article" | "header" | "footer" | "ul" | "ol" | "li" | "p" | "span" | "h1" | "h2" | "h3" | "h4";
30
+ /** cambia para volver a lanzar la animación desde fuera */
31
+ playKey?: number;
32
+ onReveal?: () => void;
33
+ children: ReactNode;
34
+ className?: string;
35
+ style?: CSSProperties;
36
+ };
37
+ /**
38
+ * Revela su contenido al montar, al entrar en pantalla o siguiendo el scroll. Siete efectos × cuatro direcciones,
39
+ * escalonado de hijos o de palabras/letras, y repetición al volver a entrar.
40
+ *
41
+ * Todo lo mueve una variable CSS registrada (`--rv-p`, 0 → 1, `@property` en ui-kit.css) que cada efecto traduce
42
+ * a opacidad, desplazamiento, recorte… El estado oculto viene ya en el HTML del servidor (sin parpadeo antes de
43
+ * hidratar); la animación es de la Web Animations API sobre `--rv-p` (sin GSAP, y sin tocar el `transition` de los
44
+ * hijos); el modo scroll reutiliza las mismas fórmulas. Sin JS (`<noscript>`) o con movimiento reducido el contenido
45
+ * se ve tal cual. Al terminar, las reglas del efecto dejan de aplicarse: no queda transform/filter/clip-path que
46
+ * atrape a un `Modal`/`Drawer` (position: fixed) de dentro.
47
+ */
48
+ export default function Reveal({ kind, direction, distance, duration, delay, easing, stagger, split, trigger, once, threshold, margin, as: Tag, playKey, onReveal, children, className, style, }: RevealProps): import("react").JSX.Element;
@@ -0,0 +1,181 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Children, useEffect, useRef } from "react";
4
+ export const REVEAL_KINDS = ["fade", "slide", "scale", "blur", "wipe", "flip", "iris"];
5
+ const EASE = {
6
+ out: "cubic-bezier(0.22, 1, 0.36, 1)",
7
+ "in-out": "cubic-bezier(0.65, 0, 0.35, 1)",
8
+ back: "cubic-bezier(0.34, 1.56, 0.64, 1)",
9
+ linear: "linear",
10
+ };
11
+ /** Desplazamiento inicial, lado recortado y giro de cada dirección («up» = entra subiendo). */
12
+ const DIR = {
13
+ up: { dx: 0, dy: 1, clip: "--rv-ct", rot: ["--rv-rx", "55deg"], origin: "50% 100%" },
14
+ down: { dx: 0, dy: -1, clip: "--rv-cb", rot: ["--rv-rx", "-55deg"], origin: "50% 0%" },
15
+ left: { dx: 1, dy: 0, clip: "--rv-cl", rot: ["--rv-ry", "-55deg"], origin: "100% 50%" },
16
+ right: { dx: -1, dy: 0, clip: "--rv-cr", rot: ["--rv-ry", "55deg"], origin: "0% 50%" },
17
+ };
18
+ /** Parte un texto en unidades animables sin tocar el DOM de React (a diferencia de SplitText, guía §10). */
19
+ function splitText(text, mode) {
20
+ return text.split(/(\s+)/).map((part, i) => {
21
+ if (!part)
22
+ return null;
23
+ if (/^\s+$/.test(part))
24
+ return part;
25
+ if (mode === "words")
26
+ return _jsx("span", { className: "ui-rv__u", "aria-hidden": true, children: part }, i);
27
+ return (_jsx("span", { className: "ui-rv__word", "aria-hidden": true, children: Array.from(part).map((c, j) => _jsx("span", { className: "ui-rv__u", children: c }, j)) }, i));
28
+ });
29
+ }
30
+ /**
31
+ * Revela su contenido al montar, al entrar en pantalla o siguiendo el scroll. Siete efectos × cuatro direcciones,
32
+ * escalonado de hijos o de palabras/letras, y repetición al volver a entrar.
33
+ *
34
+ * Todo lo mueve una variable CSS registrada (`--rv-p`, 0 → 1, `@property` en ui-kit.css) que cada efecto traduce
35
+ * a opacidad, desplazamiento, recorte… El estado oculto viene ya en el HTML del servidor (sin parpadeo antes de
36
+ * hidratar); la animación es de la Web Animations API sobre `--rv-p` (sin GSAP, y sin tocar el `transition` de los
37
+ * hijos); el modo scroll reutiliza las mismas fórmulas. Sin JS (`<noscript>`) o con movimiento reducido el contenido
38
+ * se ve tal cual. Al terminar, las reglas del efecto dejan de aplicarse: no queda transform/filter/clip-path que
39
+ * atrape a un `Modal`/`Drawer` (position: fixed) de dentro.
40
+ */
41
+ export default function Reveal({ kind = "slide", direction = "up", distance = 40, duration = 0.8, delay = 0, easing = "out", stagger, split = "none", trigger = "inview", once = true, threshold = 0.2, margin = "0px 0px -10% 0px", as: Tag = "div", playKey = 0, onReveal, children, className = "", style, }) {
42
+ const ref = useRef(null);
43
+ const onRevealRef = useRef(onReveal);
44
+ onRevealRef.current = onReveal;
45
+ const effect = kind === "rise" || kind === "stagger" ? "slide" : kind;
46
+ const step = stagger ?? (kind === "stagger" ? 0.12 : split !== "none" ? (split === "chars" ? 0.03 : 0.08) : 0);
47
+ const text = typeof children === "string" ? children : Children.toArray(children).every((c) => typeof c === "string" || typeof c === "number") ? Children.toArray(children).join("") : null;
48
+ const splitMode = split !== "none" && text !== null ? split : "none";
49
+ const mode = splitMode === "chars" ? "chars" : splitMode === "words" || step > 0 ? "each" : "self";
50
+ const d = DIR[direction];
51
+ const count = Children.count(children);
52
+ useEffect(() => {
53
+ const el = ref.current;
54
+ if (!el)
55
+ return;
56
+ // Unidades animadas: el propio contenedor, sus hijos directos o las palabras/letras
57
+ const units = () => mode === "self" ? [el] : Array.from(el.querySelectorAll(mode === "chars" ? ":scope > .ui-rv__word > .ui-rv__u" : ":scope > :not(.ui-rv__sr)"));
58
+ const items = units();
59
+ items.forEach((it, i) => it.style.setProperty("--rv-i", String(i)));
60
+ el.style.setProperty("--rv-n", String(items.length));
61
+ // En modo scroll el escalonado se reparte dentro del recorrido: la última unidad empieza al 40 %
62
+ el.style.setProperty("--rv-k", trigger === "scroll" && step > 0 && items.length > 1 ? String(0.6 / (items.length - 1)) : "0");
63
+ let fired = false;
64
+ let anims = [];
65
+ const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
66
+ const reveal = () => {
67
+ if (fired)
68
+ return;
69
+ fired = true;
70
+ onRevealRef.current?.();
71
+ };
72
+ const stop = () => {
73
+ anims.forEach((a) => a.cancel());
74
+ anims = [];
75
+ };
76
+ // Estado final estático (data-rv="1") + una animación de --rv-p 0 → 1 por unidad con su retardo; `fill:
77
+ // backwards` mantiene el 0 durante el retardo. Al acabar todas, data-rv-done retira las reglas del efecto.
78
+ const show = () => {
79
+ stop();
80
+ el.removeAttribute("data-rv-done");
81
+ el.dataset.rv = "1";
82
+ reveal();
83
+ if (reduced || typeof el.animate !== "function")
84
+ return el.setAttribute("data-rv-done", "");
85
+ anims = items.map((it, i) => it.animate([{ "--rv-p": "0" }, { "--rv-p": "1" }], {
86
+ duration: duration * 1000,
87
+ delay: (delay + i * step) * 1000,
88
+ easing: EASE[easing],
89
+ fill: "backwards",
90
+ }));
91
+ const current = anims;
92
+ Promise.all(current.map((a) => a.finished)).then(() => current === anims && el.setAttribute("data-rv-done", ""), () => { });
93
+ };
94
+ const hide = () => {
95
+ stop();
96
+ el.removeAttribute("data-rv-done");
97
+ el.dataset.rv = "0";
98
+ };
99
+ const cleanups = [stop];
100
+ if (trigger === "scroll") {
101
+ stop();
102
+ el.removeAttribute("data-rv-done");
103
+ el.dataset.rv = "scroll";
104
+ let raf = 0;
105
+ const update = () => {
106
+ raf = 0;
107
+ const r = el.getBoundingClientRect();
108
+ const vh = window.innerHeight;
109
+ // 0 cuando el borde superior asoma por abajo, 1 cuando llega al 25 % superior de la pantalla
110
+ const p = Math.min(1, Math.max(0, (vh - r.top) / (vh * 0.75)));
111
+ el.style.setProperty("--rv-t", p.toFixed(4));
112
+ if (p >= 1)
113
+ reveal();
114
+ };
115
+ const onScroll = () => (raf ||= requestAnimationFrame(update));
116
+ update();
117
+ // captura: también los scroll de contenedores internos (p. ej. el escenario del catálogo)
118
+ window.addEventListener("scroll", onScroll, { capture: true, passive: true });
119
+ window.addEventListener("resize", onScroll);
120
+ cleanups.push(() => {
121
+ cancelAnimationFrame(raf);
122
+ window.removeEventListener("scroll", onScroll, { capture: true });
123
+ window.removeEventListener("resize", onScroll);
124
+ el.style.removeProperty("--rv-t");
125
+ });
126
+ }
127
+ else if (trigger === "inview") {
128
+ hide();
129
+ const showNow = () => {
130
+ stop();
131
+ el.dataset.rv = "1";
132
+ el.setAttribute("data-rv-done", "");
133
+ reveal();
134
+ };
135
+ const io = new IntersectionObserver(([e]) => {
136
+ if (e.isIntersecting) {
137
+ show();
138
+ if (once)
139
+ disconnect();
140
+ }
141
+ else if (!once && el.dataset.rv === "1")
142
+ hide();
143
+ }, { threshold, rootMargin: margin });
144
+ // Si queda por encima sin haberse visto (recarga a mitad de página, enlace con #ancla, salto de scroll), el
145
+ // observer principal no avisa: no cruza ningún umbral. Este segundo, con el viewport extendido hacia arriba,
146
+ // sí; se muestra sin animación para no encontrarse huecos vacíos al subir.
147
+ const above = new IntersectionObserver(([e]) => {
148
+ if (e.isIntersecting && e.boundingClientRect.bottom <= 0 && el.dataset.rv !== "1") {
149
+ showNow();
150
+ if (once)
151
+ disconnect();
152
+ }
153
+ }, { rootMargin: "100000px 0px 0px 0px" });
154
+ const disconnect = () => {
155
+ io.disconnect();
156
+ above.disconnect();
157
+ };
158
+ io.observe(el);
159
+ above.observe(el);
160
+ cleanups.push(disconnect);
161
+ }
162
+ else {
163
+ show();
164
+ }
165
+ return () => cleanups.forEach((f) => f());
166
+ }, [effect, direction, distance, mode, trigger, once, threshold, margin, step, delay, duration, easing, playKey, text, count]);
167
+ const vars = {
168
+ "--rv-dur": `${duration}s`,
169
+ "--rv-delay": `${delay}s`,
170
+ "--rv-stagger": `${step}s`,
171
+ "--rv-ease": EASE[easing],
172
+ "--rv-dx": `${d.dx * distance}px`,
173
+ "--rv-dy": `${d.dy * distance}px`,
174
+ [d.clip]: "100%",
175
+ [d.rot[0]]: d.rot[1],
176
+ "--rv-origin": d.origin,
177
+ ...style,
178
+ };
179
+ const content = splitMode !== "none" && text !== null ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "ui-rv__sr", children: text }), splitText(text, splitMode)] })) : (children);
180
+ return (_jsxs(_Fragment, { children: [_jsx("noscript", { dangerouslySetInnerHTML: { __html: "<style>.ui-rv{--rv-t:1!important}</style>" } }), _jsx(Tag, { ref: ref, "data-rv": "0", className: `ui-rv ui-rv--${effect} ui-rv--${mode} ${className}`, style: vars, children: content })] }));
181
+ }
@@ -0,0 +1,14 @@
1
+ export type SceneFlashProps = {
2
+ kind?: "sweep" | "iris" | "slices" | "blinds" | "pixels";
3
+ duration?: number;
4
+ /** lanza la transición en bucle cada pocos segundos */
5
+ loop?: boolean;
6
+ /** cambia el número para lanzarla desde fuera (p. ej. al cruzar de sección) */
7
+ playKey?: number;
8
+ className?: string;
9
+ };
10
+ /**
11
+ * Transición breve para cambios de escena. Ocupa el padre (que debe tener `position: relative`) y
12
+ * no bloquea el puntero. Usa `--acc` y `--acc2`. `blinds` (persianas) y `pixels` (mosaico) animan celdas escalonadas.
13
+ */
14
+ export default function SceneFlash({ kind, duration, loop, playKey, className }: SceneFlashProps): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
+ const CELLS = { blinds: 10, pixels: 96 };
5
+ /**
6
+ * Transición breve para cambios de escena. Ocupa el padre (que debe tener `position: relative`) y
7
+ * no bloquea el puntero. Usa `--acc` y `--acc2`. `blinds` (persianas) y `pixels` (mosaico) animan celdas escalonadas.
8
+ */
9
+ export default function SceneFlash({ kind = "sweep", duration = 1, loop = false, playKey = 0, className = "" }) {
10
+ const el = useRef(null);
11
+ useEffect(() => {
12
+ const node = el.current;
13
+ if (!node)
14
+ return;
15
+ let disposed = false;
16
+ const cleanups = [];
17
+ const cells = kind === "blinds" || kind === "pixels" ? Array.from(node.children) : [];
18
+ (async () => {
19
+ const { gsap } = await import("gsap");
20
+ if (disposed)
21
+ return;
22
+ gsap.set(cells.length ? cells : node, { autoAlpha: 0 });
23
+ if (!cells.length)
24
+ gsap.set(node, { autoAlpha: 0 });
25
+ const run = () => {
26
+ gsap.killTweensOf(cells.length ? cells : node);
27
+ if (kind === "sweep")
28
+ gsap.fromTo(node, { yPercent: 100, autoAlpha: 1 }, { yPercent: -110, duration, ease: "power2.inOut", onComplete: () => void gsap.set(node, { autoAlpha: 0 }) });
29
+ else if (kind === "iris")
30
+ gsap.fromTo(node, { scale: 0, autoAlpha: 0.85 }, { scale: 3.2, autoAlpha: 0, duration: duration * 1.1, ease: "power2.out" });
31
+ else if (kind === "slices")
32
+ gsap.fromTo(node, { xPercent: -100, autoAlpha: 1, skewX: -18 }, { xPercent: 100, duration: duration * 0.55, ease: "power3.inOut", onComplete: () => void gsap.set(node, { autoAlpha: 0 }) });
33
+ else
34
+ gsap.fromTo(cells, { autoAlpha: 0 }, { autoAlpha: 1, duration: duration * 0.35, ease: "none", stagger: { each: (duration * 0.65) / cells.length, from: kind === "pixels" ? "random" : "start" }, yoyo: true, repeat: 1 });
35
+ };
36
+ if (playKey > 0)
37
+ run();
38
+ if (loop) {
39
+ const id = setInterval(run, (duration + 1.4) * 1000);
40
+ cleanups.push(() => clearInterval(id));
41
+ }
42
+ cleanups.push(() => gsap.killTweensOf(cells.length ? cells : node));
43
+ })();
44
+ return () => {
45
+ disposed = true;
46
+ cleanups.reverse().forEach((f) => f());
47
+ };
48
+ }, [kind, duration, loop, playKey]);
49
+ const count = kind === "blinds" ? CELLS.blinds : kind === "pixels" ? CELLS.pixels : 0;
50
+ return (_jsx("div", { ref: el, className: `ui-flash ui-flash--${kind} ${className}`, "aria-hidden": true, children: Array.from({ length: count }, (_, i) => (_jsx("i", { className: "ui-flash__cell" }, i))) }, kind));
51
+ }
@@ -0,0 +1,31 @@
1
+ import { type Tone } from "./variants.js";
2
+ export type ScrambleTextProps = {
3
+ text: string;
4
+ /** caracteres que se ven mientras se descifra */
5
+ chars?: string;
6
+ duration?: number;
7
+ /** velocidad de cambio de los glifos (0.2 lento – 2 rápido) */
8
+ speed?: number;
9
+ /** cuándo se lanza: al montar, al pasar el ratón o en bucle */
10
+ trigger?: "mount" | "hover" | "loop";
11
+ as?: "h1" | "h2" | "h3" | "p" | "span";
12
+ /** tamaño de letra en px */
13
+ fontSize?: number;
14
+ /** @deprecated usa `fontSize` */
15
+ size?: number;
16
+ weight?: number;
17
+ /** color: un token, o un degradado entre --acc y --acc2 */
18
+ tone?: Tone | "gradient";
19
+ align?: "left" | "center" | "right";
20
+ uppercase?: boolean;
21
+ /** espaciado entre letras en em (−0.05 – 0.3) */
22
+ tracking?: number;
23
+ /** cambia el número para repetir la animación desde fuera */
24
+ playKey?: number;
25
+ className?: string;
26
+ };
27
+ /**
28
+ * Texto que se "descifra" con ScrambleText de GSAP. El texto real siempre está en el DOM
29
+ * (legible sin JS); el efecto solo lo anima.
30
+ */
31
+ export default function ScrambleText({ text, chars, duration, speed, trigger, as: Tag, fontSize, size: legacySize, weight, tone, align, uppercase, tracking, playKey, className, }: ScrambleTextProps): import("react").JSX.Element;
@@ -0,0 +1,51 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef } from "react";
4
+ import { tcls } from "./variants.js";
5
+ /**
6
+ * Texto que se "descifra" con ScrambleText de GSAP. El texto real siempre está en el DOM
7
+ * (legible sin JS); el efecto solo lo anima.
8
+ */
9
+ export default function ScrambleText({ text, chars = "!<>-_\\/[]{}=+*^?#", duration = 1.2, speed = 0.6, trigger = "mount", as: Tag = "h2", fontSize, size: legacySize, weight = 800, tone = "fg", align = "center", uppercase = false, tracking = -0.02, playKey = 0, className = "", }) {
10
+ const size = fontSize ?? legacySize ?? 40;
11
+ const inner = useRef(null);
12
+ useEffect(() => {
13
+ const el = inner.current;
14
+ if (!el)
15
+ return;
16
+ let disposed = false;
17
+ const cleanups = [];
18
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
19
+ (async () => {
20
+ const { gsap } = await import("gsap");
21
+ const { ScrambleTextPlugin } = await import("gsap/ScrambleTextPlugin");
22
+ if (disposed)
23
+ return;
24
+ gsap.registerPlugin(ScrambleTextPlugin);
25
+ const run = () => gsap.to(el, { duration, scrambleText: { text, chars, revealDelay: 0.15, speed }, ease: "none", overwrite: true });
26
+ if (!reduce) {
27
+ if (trigger === "hover") {
28
+ el.parentElement?.addEventListener("mouseenter", run);
29
+ cleanups.push(() => el.parentElement?.removeEventListener("mouseenter", run));
30
+ }
31
+ else {
32
+ el.textContent = " ";
33
+ run();
34
+ if (trigger === "loop") {
35
+ const id = setInterval(run, (duration + 1.6) * 1000);
36
+ cleanups.push(() => clearInterval(id));
37
+ }
38
+ }
39
+ }
40
+ cleanups.push(() => {
41
+ gsap.killTweensOf(el);
42
+ el.textContent = text;
43
+ });
44
+ })();
45
+ return () => {
46
+ disposed = true;
47
+ cleanups.reverse().forEach((f) => f());
48
+ };
49
+ }, [text, chars, duration, speed, trigger, playKey]);
50
+ return (_jsx(Tag, { className: `ui-scramble ${tone === "gradient" ? "ui-scramble--gradient" : tcls(tone)} ${uppercase ? "ui-scramble--upper" : ""} ${className}`, style: { fontSize: size, fontWeight: weight, textAlign: align, letterSpacing: `${tracking}em` }, children: _jsx("span", { ref: inner, children: text }, text) }));
51
+ }
@@ -0,0 +1,15 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type ScrollAreaProps = {
3
+ /** un párrafo por línea */
4
+ content?: string;
5
+ height?: number;
6
+ variant?: Variant;
7
+ className?: string;
8
+ };
9
+ /**
10
+ * Caja de contenido largo (registro de cambios, comentarios, salida de terminal…) con su propia
11
+ * barra de desplazamiento — ver la sección "Scrollbars personalizadas" en `components/ui/styles/ui-kit.css`: la
12
+ * barra la hereda automáticamente cualquier superficie `ui-s--<variante>`, este componente es solo
13
+ * el envoltorio con una altura fija para demostrarla y para usarla directamente en un proyecto.
14
+ */
15
+ export default function ScrollArea({ content, height, variant, className }: ScrollAreaProps): import("react").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { vcls } from "./variants.js";
3
+ const DEFAULT_CONTENT = "Registro de cambios\n" +
4
+ "v1.4.0 — Panel de tokens con selector de tipografía y fuentes de Google autoalojadas.\n" +
5
+ "v1.3.0 — Categoría «Secciones»: hero, contacto, precios, FAQ y más, listos para pegar.\n" +
6
+ "v1.2.0 — Entrada, salida y hover por composición (Presence y HoverFX) para todo el catálogo.\n" +
7
+ "v1.1.0 — Comparador de los 7 estilos y vista «en caso real» por categoría.\n" +
8
+ "v1.0.0 — Primera versión del catálogo: tokens, sistema de estilos y 20 componentes.";
9
+ /**
10
+ * Caja de contenido largo (registro de cambios, comentarios, salida de terminal…) con su propia
11
+ * barra de desplazamiento — ver la sección "Scrollbars personalizadas" en `components/ui/styles/ui-kit.css`: la
12
+ * barra la hereda automáticamente cualquier superficie `ui-s--<variante>`, este componente es solo
13
+ * el envoltorio con una altura fija para demostrarla y para usarla directamente en un proyecto.
14
+ */
15
+ export default function ScrollArea({ content = DEFAULT_CONTENT, height = 220, variant = "glass", className = "" }) {
16
+ const lines = content.split("\n").filter((l) => l.trim());
17
+ return (_jsx("div", { className: `ui-scroll ui-surface ${vcls(variant)} ${className}`, style: { height }, children: lines.map((l, i) => (_jsx("p", { className: i === 0 ? "ui-scroll__head" : undefined, children: l }, i))) }));
18
+ }
@@ -0,0 +1,12 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type SectionHeaderProps = {
3
+ kicker?: string;
4
+ title?: string;
5
+ subtitle?: string;
6
+ align?: "left" | "center";
7
+ rule?: "none" | "line" | "ascii" | "dots";
8
+ variant?: Variant;
9
+ className?: string;
10
+ };
11
+ /** Cabecera de sección: sobretítulo, titular, subtítulo y regla decorativa. */
12
+ export default function SectionHeader({ kicker, title, subtitle, align, rule, variant, className }: SectionHeaderProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { vcls } from "./variants.js";
3
+ /** Cabecera de sección: sobretítulo, titular, subtítulo y regla decorativa. */
4
+ export default function SectionHeader({ kicker = "Cómo funciona", title = "Tres pasos y estás en el agua", subtitle = "Reserva, elige tu horario y nosotros nos ocupamos del resto.", align = "left", rule = "line", variant = "minimal", className = "" }) {
5
+ return (_jsxs("header", { className: `ui-sh ${align === "center" ? "ui-sh--center" : ""} ${vcls(variant)} ${className}`, children: [kicker && _jsx("span", { className: "ui-sh__kicker", children: kicker }), _jsx("h2", { children: title }), subtitle && _jsx("p", { children: subtitle }), rule === "line" && _jsx("span", { className: "ui-sh__line" }), rule === "ascii" && _jsx("span", { className: "ui-sh__rule", "aria-hidden": true, children: "=".repeat(60) }), rule === "dots" && _jsx("span", { className: "ui-sh__rule", "aria-hidden": true, children: ". ".repeat(30) })] }));
6
+ }
@@ -0,0 +1,17 @@
1
+ import { type Variant } from "./variants.js";
2
+ export type SelectProps = {
3
+ label?: string;
4
+ options?: string;
5
+ defaultValue?: string;
6
+ placeholder?: string;
7
+ size?: "sm" | "md" | "lg";
8
+ variant?: Variant;
9
+ className?: string;
10
+ };
11
+ /**
12
+ * Desplegable con estilo propio, no un `<select>` nativo: un `<select>` real no se puede reskinar por
13
+ * dentro (la lista abierta la pinta el sistema operativo), así que esto es un botón + una lista
14
+ * absoluta, igual de accesible (`aria-haspopup`, `role="listbox"`) pero con los mismos 7 estilos que
15
+ * el resto del kit, abierto o cerrado.
16
+ */
17
+ export default function Select({ label, options, defaultValue, placeholder, size, variant, className, }: SelectProps): import("react").JSX.Element;