@proyecto-viviana/ui 0.1.1 → 0.1.3

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 (66) hide show
  1. package/dist/index.d.ts +649 -51
  2. package/dist/index.js +232 -185
  3. package/dist/index.jsx +1533 -0
  4. package/package.json +9 -7
  5. package/dist/alert/index.d.ts +0 -12
  6. package/dist/alert/index.d.ts.map +0 -1
  7. package/dist/avatar/index.d.ts +0 -17
  8. package/dist/avatar/index.d.ts.map +0 -1
  9. package/dist/badge/index.d.ts +0 -12
  10. package/dist/badge/index.d.ts.map +0 -1
  11. package/dist/button/Button.d.ts +0 -19
  12. package/dist/button/Button.d.ts.map +0 -1
  13. package/dist/button/index.d.ts +0 -3
  14. package/dist/button/index.d.ts.map +0 -1
  15. package/dist/button/types.d.ts +0 -23
  16. package/dist/button/types.d.ts.map +0 -1
  17. package/dist/checkbox/index.d.ts +0 -43
  18. package/dist/checkbox/index.d.ts.map +0 -1
  19. package/dist/custom/calendar-card/index.d.ts +0 -12
  20. package/dist/custom/calendar-card/index.d.ts.map +0 -1
  21. package/dist/custom/chip/index.d.ts +0 -11
  22. package/dist/custom/chip/index.d.ts.map +0 -1
  23. package/dist/custom/conversation/index.d.ts +0 -32
  24. package/dist/custom/conversation/index.d.ts.map +0 -1
  25. package/dist/custom/event-card/index.d.ts +0 -26
  26. package/dist/custom/event-card/index.d.ts.map +0 -1
  27. package/dist/custom/lateral-nav/index.d.ts +0 -32
  28. package/dist/custom/lateral-nav/index.d.ts.map +0 -1
  29. package/dist/custom/logo/index.d.ts +0 -26
  30. package/dist/custom/logo/index.d.ts.map +0 -1
  31. package/dist/custom/nav-header/index.d.ts +0 -12
  32. package/dist/custom/nav-header/index.d.ts.map +0 -1
  33. package/dist/custom/page-layout/index.d.ts +0 -11
  34. package/dist/custom/page-layout/index.d.ts.map +0 -1
  35. package/dist/custom/profile-card/index.d.ts +0 -12
  36. package/dist/custom/profile-card/index.d.ts.map +0 -1
  37. package/dist/custom/project-card/index.d.ts +0 -24
  38. package/dist/custom/project-card/index.d.ts.map +0 -1
  39. package/dist/custom/timeline-item/index.d.ts +0 -18
  40. package/dist/custom/timeline-item/index.d.ts.map +0 -1
  41. package/dist/dialog/index.d.ts +0 -18
  42. package/dist/dialog/index.d.ts.map +0 -1
  43. package/dist/icon/icons/GitHubIcon.d.ts +0 -7
  44. package/dist/icon/icons/GitHubIcon.d.ts.map +0 -1
  45. package/dist/icon/index.d.ts +0 -28
  46. package/dist/icon/index.d.ts.map +0 -1
  47. package/dist/index.d.ts.map +0 -1
  48. package/dist/index.js.map +0 -1
  49. package/dist/link/index.d.ts +0 -39
  50. package/dist/link/index.d.ts.map +0 -1
  51. package/dist/progress-bar/index.d.ts +0 -49
  52. package/dist/progress-bar/index.d.ts.map +0 -1
  53. package/dist/radio/index.d.ts +0 -43
  54. package/dist/radio/index.d.ts.map +0 -1
  55. package/dist/separator/index.d.ts +0 -42
  56. package/dist/separator/index.d.ts.map +0 -1
  57. package/dist/switch/ToggleSwitch.d.ts +0 -28
  58. package/dist/switch/ToggleSwitch.d.ts.map +0 -1
  59. package/dist/switch/index.d.ts +0 -26
  60. package/dist/switch/index.d.ts.map +0 -1
  61. package/dist/test-utils/index.d.ts +0 -44
  62. package/dist/test-utils/index.d.ts.map +0 -1
  63. package/dist/textfield/index.d.ts +0 -31
  64. package/dist/textfield/index.d.ts.map +0 -1
  65. package/dist/tooltip/index.d.ts +0 -17
  66. package/dist/tooltip/index.d.ts.map +0 -1
package/dist/index.jsx ADDED
@@ -0,0 +1,1533 @@
1
+ // src/button/Button.tsx
2
+ import { splitProps, mergeProps as solidMergeProps } from "solid-js";
3
+ import { Button as HeadlessButton } from "@proyecto-viviana/solidaria-components";
4
+ function Button(props) {
5
+ const defaultProps = {
6
+ variant: "primary",
7
+ buttonStyle: "fill",
8
+ size: "md"
9
+ };
10
+ const merged = solidMergeProps(defaultProps, props);
11
+ const [local, headlessProps] = splitProps(merged, [
12
+ "children",
13
+ "variant",
14
+ "buttonStyle",
15
+ "size",
16
+ "fullWidth",
17
+ "staticColor",
18
+ "class"
19
+ ]);
20
+ const getClassName = (renderProps) => {
21
+ const classList = [
22
+ "vui-button",
23
+ `vui-button--${local.buttonStyle}`,
24
+ `vui-button--${local.variant}`,
25
+ `vui-button--${local.size}`
26
+ ];
27
+ if (renderProps.isPressed) {
28
+ classList.push("is-pressed");
29
+ }
30
+ if (local.fullWidth) {
31
+ classList.push("vui-button--full-width");
32
+ }
33
+ if (local.class) {
34
+ classList.push(local.class);
35
+ }
36
+ return classList.join(" ");
37
+ };
38
+ return <HeadlessButton
39
+ {...headlessProps}
40
+ class={getClassName}
41
+ data-variant={local.variant}
42
+ data-style={local.buttonStyle}
43
+ data-static-color={local.staticColor || void 0}
44
+ >
45
+ {local.children}
46
+ </HeadlessButton>;
47
+ }
48
+
49
+ // src/badge/index.tsx
50
+ import { Show } from "solid-js";
51
+ var variantStyles = {
52
+ primary: "bg-primary-500 text-white",
53
+ secondary: "bg-bg-300 text-primary-300",
54
+ accent: "bg-accent-300 text-black",
55
+ success: "bg-success-400 text-white",
56
+ warning: "bg-warning-400 text-black",
57
+ danger: "bg-danger-400 text-white"
58
+ };
59
+ var sizeStyles = {
60
+ sm: "w-5 h-5 text-xs",
61
+ md: "w-7 h-7 text-xs",
62
+ lg: "w-9 h-9 text-sm"
63
+ };
64
+ function Badge(props) {
65
+ const variant = () => props.variant ?? "accent";
66
+ const size = () => props.size ?? "md";
67
+ return <div
68
+ class={`flex items-center justify-center rounded-full border-b border-white font-semibold ${variantStyles[variant()]} ${sizeStyles[size()]} ${props.class ?? ""}`}
69
+ >
70
+ <Show when={props.count !== void 0} fallback={props.children}>
71
+ <span>{props.count}</span>
72
+ </Show>
73
+ </div>;
74
+ }
75
+
76
+ // src/alert/index.tsx
77
+ import { Show as Show2 } from "solid-js";
78
+ var variantStyles2 = {
79
+ info: "bg-primary-700 text-primary-200 border border-primary-500",
80
+ success: "bg-success-600 text-success-100 border border-success-400",
81
+ warning: "bg-warning-600 text-warning-100 border border-warning-400",
82
+ error: "bg-danger-600 text-danger-100 border border-danger-400"
83
+ };
84
+ function Alert(props) {
85
+ const variant = () => props.variant ?? "info";
86
+ return <div
87
+ class={`flex items-center min-h-[50px] font-normal rounded-lg px-4 py-2 ${variantStyles2[variant()]} ${props.class ?? ""}`}
88
+ role="alert"
89
+ >
90
+ <div class="flex items-center gap-3 flex-1">
91
+ <Show2 when={props.title}>
92
+ <span class="font-semibold font-jost">{props.title}</span>
93
+ <span class="opacity-50">|</span>
94
+ </Show2>
95
+ <div class="flex-1">{props.children}</div>
96
+ <Show2 when={props.dismissible}>
97
+ <button
98
+ class="hover:opacity-70 transition-opacity ml-2"
99
+ onClick={props.onDismiss}
100
+ aria-label="Dismiss"
101
+ >
102
+ ✕
103
+ </button>
104
+ </Show2>
105
+ </div>
106
+ </div>;
107
+ }
108
+
109
+ // src/avatar/index.tsx
110
+ import { Show as Show3 } from "solid-js";
111
+ var sizeStyles2 = {
112
+ xs: { container: "w-6 h-6", text: "text-xs", indicator: "w-1.5 h-1.5" },
113
+ sm: { container: "w-8 h-8", text: "text-sm", indicator: "w-2 h-2" },
114
+ md: { container: "w-10 h-10", text: "text-base", indicator: "w-2.5 h-2.5" },
115
+ lg: { container: "w-14 h-14", text: "text-lg", indicator: "w-3 h-3" },
116
+ xl: { container: "w-20 h-20", text: "text-xl", indicator: "w-4 h-4" }
117
+ };
118
+ function Avatar(props) {
119
+ const size = () => props.size ?? "md";
120
+ const styles = () => sizeStyles2[size()];
121
+ const initials = () => {
122
+ if (props.fallback) return props.fallback.slice(0, 2).toUpperCase();
123
+ if (props.alt) return props.alt.slice(0, 2).toUpperCase();
124
+ return "?";
125
+ };
126
+ return <div class={`relative inline-block ${props.class ?? ""}`}>
127
+ <div
128
+ class={`${styles().container} rounded-full overflow-hidden bg-bg-200 flex items-center justify-center ring-2 ring-accent/50`}
129
+ >
130
+ <Show3
131
+ when={props.src}
132
+ fallback={<span class={`${styles().text} font-medium text-primary-300`}>
133
+ {initials()}
134
+ </span>}
135
+ >
136
+ <img
137
+ src={props.src}
138
+ alt={props.alt ?? "Avatar"}
139
+ class="w-full h-full object-cover"
140
+ />
141
+ </Show3>
142
+ </div>
143
+ <Show3 when={props.online !== void 0}>
144
+ <span
145
+ class={`absolute bottom-0 right-0 ${styles().indicator} rounded-full ring-2 ring-bg-400 ${props.online ? "bg-success-400" : "bg-bg-light"}`}
146
+ />
147
+ </Show3>
148
+ </div>;
149
+ }
150
+ function AvatarGroup(props) {
151
+ return <div class="flex -space-x-2">
152
+ {props.children}
153
+ </div>;
154
+ }
155
+
156
+ // src/switch/index.tsx
157
+ import { createSignal, createEffect } from "solid-js";
158
+
159
+ // src/switch/ToggleSwitch.tsx
160
+ import { splitProps as splitProps2, mergeProps as solidMergeProps2 } from "solid-js";
161
+ import { ToggleSwitch as HeadlessToggleSwitch } from "@proyecto-viviana/solidaria-components";
162
+ var sizeStyles3 = {
163
+ sm: {
164
+ track: "h-5 w-9",
165
+ thumb: "h-4 w-4",
166
+ translate: "translate-x-4"
167
+ },
168
+ md: {
169
+ track: "h-6 w-11",
170
+ thumb: "h-5 w-5",
171
+ translate: "translate-x-5"
172
+ },
173
+ lg: {
174
+ track: "h-7 w-14",
175
+ thumb: "h-6 w-6",
176
+ translate: "translate-x-7"
177
+ }
178
+ };
179
+ function ToggleSwitch(props) {
180
+ const defaultProps = {
181
+ size: "md"
182
+ };
183
+ const merged = solidMergeProps2(defaultProps, props);
184
+ const [local, headlessProps] = splitProps2(merged, [
185
+ "size",
186
+ "class",
187
+ "children"
188
+ ]);
189
+ const size = () => sizeStyles3[local.size];
190
+ const getClassName = (renderProps) => {
191
+ const base = "inline-flex items-center gap-2 cursor-pointer";
192
+ const disabledClass = renderProps.isDisabled ? "cursor-not-allowed opacity-50" : "";
193
+ const custom = local.class || "";
194
+ return [base, disabledClass, custom].filter(Boolean).join(" ");
195
+ };
196
+ return <HeadlessToggleSwitch
197
+ {...headlessProps}
198
+ class={getClassName}
199
+ >
200
+ {(renderProps) => <>
201
+ <span
202
+ class={[
203
+ "relative rounded-full transition-colors duration-200",
204
+ "focus-within:ring-2 focus-within:ring-accent-300 focus-within:ring-offset-2 focus-within:ring-offset-bg-400",
205
+ size().track,
206
+ renderProps.isSelected ? "bg-accent" : "bg-bg-300",
207
+ renderProps.isDisabled ? "cursor-not-allowed" : "cursor-pointer"
208
+ ].join(" ")}
209
+ >
210
+ <span
211
+ class={[
212
+ "absolute top-0.5 left-0.5 rounded-full bg-white shadow transition-transform duration-200",
213
+ size().thumb,
214
+ renderProps.isSelected ? size().translate : "translate-x-0"
215
+ ].join(" ")}
216
+ />
217
+ </span>
218
+ {local.children && <span class="text-primary-200">{local.children}</span>}
219
+ </>}
220
+ </HeadlessToggleSwitch>;
221
+ }
222
+
223
+ // src/switch/index.tsx
224
+ function TabSwitch(props) {
225
+ const [leftSelected, setLeftSelected] = createSignal(
226
+ props.value ? props.value === props.options[0]?.value : true
227
+ );
228
+ createEffect(() => {
229
+ if (props.value !== void 0) {
230
+ setLeftSelected(props.value === props.options[0]?.value);
231
+ }
232
+ });
233
+ const toggle = () => {
234
+ const newLeftSelected = !leftSelected();
235
+ setLeftSelected(newLeftSelected);
236
+ const newValue = newLeftSelected ? props.options[0]?.value : props.options[1]?.value;
237
+ if (newValue) {
238
+ props.onChange?.(newValue);
239
+ }
240
+ };
241
+ const leftSelectedStyle = "left-0 w-[142px] border-l-2";
242
+ const rightSelectedStyle = "left-[142px] w-[108px] border-r-2";
243
+ const textSelected = "font-extrabold text-primary-300";
244
+ const textUnselected = "font-medium text-primary-600 tracking-wider";
245
+ return <div
246
+ onClick={toggle}
247
+ class={`relative bg-bg-400 rounded-full w-[250px] cursor-pointer ${props.class ?? ""}`}
248
+ >
249
+ <div
250
+ class={`${leftSelected() ? leftSelectedStyle : rightSelectedStyle} transition-all duration-500 ease-in-out z-0 absolute bg-primary-600 rounded-full h-8 border-accent-300`}
251
+ />
252
+ <div class="flex z-10 h-8 justify-around">
253
+ <button
254
+ type="button"
255
+ class={`${leftSelected() ? textSelected : textUnselected} transition-all ease-in-out duration-500 z-10 text-lg flex justify-center items-center pointer-events-none`}
256
+ >
257
+ <span>{props.options[0]?.label ?? "TENDENCIAS"}</span>
258
+ </button>
259
+ <button
260
+ type="button"
261
+ class={`${leftSelected() ? textUnselected : textSelected} transition-all ease-in-out duration-500 z-10 text-lg flex justify-center items-center pointer-events-none`}
262
+ >
263
+ <span>{props.options[1]?.label ?? "\xDALTIMOS"}</span>
264
+ </button>
265
+ </div>
266
+ </div>;
267
+ }
268
+
269
+ // src/checkbox/index.tsx
270
+ import { splitProps as splitProps3, mergeProps as solidMergeProps3, Show as Show4 } from "solid-js";
271
+ import {
272
+ Checkbox as HeadlessCheckbox,
273
+ CheckboxGroup as HeadlessCheckboxGroup
274
+ } from "@proyecto-viviana/solidaria-components";
275
+ var sizeStyles4 = {
276
+ sm: {
277
+ box: "h-4 w-4",
278
+ icon: "h-3 w-3",
279
+ label: "text-sm"
280
+ },
281
+ md: {
282
+ box: "h-5 w-5",
283
+ icon: "h-3.5 w-3.5",
284
+ label: "text-base"
285
+ },
286
+ lg: {
287
+ box: "h-6 w-6",
288
+ icon: "h-4 w-4",
289
+ label: "text-lg"
290
+ }
291
+ };
292
+ function CheckIcon(props) {
293
+ return <svg
294
+ class={props.class}
295
+ viewBox="0 0 12 10"
296
+ fill="none"
297
+ xmlns="http://www.w3.org/2000/svg"
298
+ >
299
+ <path
300
+ d="M1 5L4.5 8.5L11 1"
301
+ stroke="currentColor"
302
+ stroke-width="2"
303
+ stroke-linecap="round"
304
+ stroke-linejoin="round"
305
+ />
306
+ </svg>;
307
+ }
308
+ function IndeterminateIcon(props) {
309
+ return <svg
310
+ class={props.class}
311
+ viewBox="0 0 12 2"
312
+ fill="none"
313
+ xmlns="http://www.w3.org/2000/svg"
314
+ >
315
+ <path
316
+ d="M1 1H11"
317
+ stroke="currentColor"
318
+ stroke-width="2"
319
+ stroke-linecap="round"
320
+ />
321
+ </svg>;
322
+ }
323
+ function Checkbox(props) {
324
+ const defaultProps = {
325
+ size: "md"
326
+ };
327
+ const merged = solidMergeProps3(defaultProps, props);
328
+ const [local, headlessProps] = splitProps3(merged, [
329
+ "size",
330
+ "class",
331
+ "children"
332
+ ]);
333
+ const size = () => sizeStyles4[local.size];
334
+ const getClassName = (renderProps) => {
335
+ const base = "inline-flex items-center gap-2 cursor-pointer";
336
+ const disabledClass = renderProps.isDisabled ? "cursor-not-allowed opacity-50" : "";
337
+ const custom = local.class || "";
338
+ return [base, disabledClass, custom].filter(Boolean).join(" ");
339
+ };
340
+ return <HeadlessCheckbox
341
+ {...headlessProps}
342
+ class={getClassName}
343
+ >
344
+ {(renderProps) => {
345
+ const boxClasses = () => {
346
+ const base = "relative flex items-center justify-center rounded border-2 transition-all duration-200";
347
+ const sizeClass = size().box;
348
+ let colorClass;
349
+ if (renderProps.isDisabled) {
350
+ colorClass = "border-bg-300 bg-bg-200";
351
+ } else if (renderProps.isSelected || renderProps.isIndeterminate) {
352
+ colorClass = "border-accent bg-accent";
353
+ } else {
354
+ colorClass = "border-primary-600 bg-transparent hover:border-accent-300";
355
+ }
356
+ const focusClass = renderProps.isFocusVisible ? "ring-2 ring-accent-300 ring-offset-2 ring-offset-bg-400" : "";
357
+ const cursorClass = renderProps.isDisabled ? "cursor-not-allowed" : "cursor-pointer";
358
+ return [base, sizeClass, colorClass, focusClass, cursorClass].filter(Boolean).join(" ");
359
+ };
360
+ const iconClasses = () => {
361
+ const base = "text-white transition-opacity duration-200";
362
+ const sizeClass = size().icon;
363
+ const visibilityClass = renderProps.isSelected || renderProps.isIndeterminate ? "opacity-100" : "opacity-0";
364
+ return [base, sizeClass, visibilityClass].filter(Boolean).join(" ");
365
+ };
366
+ const labelClasses = () => {
367
+ const base = "text-primary-200";
368
+ const sizeClass = size().label;
369
+ const disabledClass = renderProps.isDisabled ? "opacity-50" : "";
370
+ return [base, sizeClass, disabledClass].filter(Boolean).join(" ");
371
+ };
372
+ return <>
373
+ <span class={boxClasses()}>
374
+ <Show4
375
+ when={!renderProps.isIndeterminate}
376
+ fallback={<IndeterminateIcon class={iconClasses()} />}
377
+ >
378
+ <CheckIcon class={iconClasses()} />
379
+ </Show4>
380
+ </span>
381
+ <Show4 when={local.children}>
382
+ <span class={labelClasses()}>{local.children}</span>
383
+ </Show4>
384
+ </>;
385
+ }}
386
+ </HeadlessCheckbox>;
387
+ }
388
+ function CheckboxGroup(props) {
389
+ const [local, headlessProps] = splitProps3(props, [
390
+ "class",
391
+ "children",
392
+ "label",
393
+ "description",
394
+ "errorMessage"
395
+ ]);
396
+ const getClassName = (renderProps) => {
397
+ const base = "flex flex-col gap-2";
398
+ const disabledClass = renderProps.isDisabled ? "opacity-50" : "";
399
+ const custom = local.class || "";
400
+ return [base, disabledClass, custom].filter(Boolean).join(" ");
401
+ };
402
+ const renderChildren = (renderProps) => <>
403
+ <Show4 when={local.label}>
404
+ <span class="text-sm font-medium text-primary-200">{local.label}</span>
405
+ </Show4>
406
+ <div class="flex flex-col gap-2">
407
+ {local.children}
408
+ </div>
409
+ <Show4 when={local.description && !renderProps.isInvalid}>
410
+ <span class="text-sm text-primary-400">{local.description}</span>
411
+ </Show4>
412
+ <Show4 when={local.errorMessage && renderProps.isInvalid}>
413
+ <span class="text-sm text-danger-400">{local.errorMessage}</span>
414
+ </Show4>
415
+ </>;
416
+ return <HeadlessCheckboxGroup
417
+ {...headlessProps}
418
+ class={getClassName}
419
+ children={renderChildren}
420
+ />;
421
+ }
422
+
423
+ // src/radio/index.tsx
424
+ import { Show as Show5, createContext, useContext, splitProps as splitProps4 } from "solid-js";
425
+ import {
426
+ RadioGroup as HeadlessRadioGroup,
427
+ Radio as HeadlessRadio
428
+ } from "@proyecto-viviana/solidaria-components";
429
+ var RadioSizeContext = createContext("md");
430
+ var sizeStyles5 = {
431
+ sm: {
432
+ circle: "h-4 w-4",
433
+ dot: "h-2 w-2",
434
+ label: "text-sm"
435
+ },
436
+ md: {
437
+ circle: "h-5 w-5",
438
+ dot: "h-2.5 w-2.5",
439
+ label: "text-base"
440
+ },
441
+ lg: {
442
+ circle: "h-6 w-6",
443
+ dot: "h-3 w-3",
444
+ label: "text-lg"
445
+ }
446
+ };
447
+ function RadioGroup(props) {
448
+ const [local, headlessProps] = splitProps4(props, [
449
+ "size",
450
+ "class",
451
+ "label",
452
+ "description",
453
+ "errorMessage",
454
+ "children"
455
+ ]);
456
+ const size = local.size ?? "md";
457
+ const customClass = local.class ?? "";
458
+ const getClassName = (renderProps) => {
459
+ const base = "flex gap-2";
460
+ const orientationClass = renderProps.orientation === "horizontal" ? "flex-row flex-wrap" : "flex-col";
461
+ const disabledClass = renderProps.isDisabled ? "opacity-50" : "";
462
+ return [base, orientationClass, disabledClass, customClass].filter(Boolean).join(" ");
463
+ };
464
+ const renderChildren = (renderProps) => <>
465
+ <Show5 when={local.label}>
466
+ <span class="text-primary-200 font-medium mb-1">{local.label}</span>
467
+ </Show5>
468
+ {typeof local.children === "function" ? local.children(renderProps) : local.children}
469
+ <Show5 when={local.description && !renderProps.isInvalid}>
470
+ <span class="text-primary-400 text-sm">{local.description}</span>
471
+ </Show5>
472
+ <Show5 when={local.errorMessage && renderProps.isInvalid}>
473
+ <span class="text-danger-400 text-sm">{local.errorMessage}</span>
474
+ </Show5>
475
+ </>;
476
+ return <RadioSizeContext.Provider value={size}>
477
+ <HeadlessRadioGroup
478
+ {...headlessProps}
479
+ class={getClassName}
480
+ data-size={size}
481
+ children={renderChildren}
482
+ />
483
+ </RadioSizeContext.Provider>;
484
+ }
485
+ function Radio(props) {
486
+ const sizeFromContext = useContext(RadioSizeContext);
487
+ const size = () => sizeStyles5[sizeFromContext];
488
+ const customClass = props.class ?? "";
489
+ const getClassName = (renderProps) => {
490
+ const base = "inline-flex items-center gap-2 cursor-pointer";
491
+ const disabledClass = renderProps.isDisabled ? "cursor-not-allowed opacity-50" : "";
492
+ return [base, disabledClass, customClass].filter(Boolean).join(" ");
493
+ };
494
+ const { class: _, children, ...headlessProps } = props;
495
+ return <HeadlessRadio
496
+ {...headlessProps}
497
+ class={getClassName}
498
+ >
499
+ {(renderProps) => {
500
+ const circleClasses = () => {
501
+ const base = "relative flex items-center justify-center rounded-full border-2 transition-all duration-200";
502
+ const sizeClass = size().circle;
503
+ let colorClass;
504
+ if (renderProps.isDisabled) {
505
+ colorClass = "border-bg-300 bg-bg-200";
506
+ } else if (renderProps.isSelected) {
507
+ colorClass = "border-accent bg-transparent";
508
+ } else {
509
+ colorClass = "border-primary-600 bg-transparent hover:border-accent-300";
510
+ }
511
+ const focusClass = renderProps.isFocusVisible ? "ring-2 ring-accent-300 ring-offset-2 ring-offset-bg-400" : "";
512
+ const cursorClass = renderProps.isDisabled ? "cursor-not-allowed" : "cursor-pointer";
513
+ return [base, sizeClass, colorClass, focusClass, cursorClass].filter(Boolean).join(" ");
514
+ };
515
+ const dotClasses = () => {
516
+ const base = "rounded-full bg-accent transition-all duration-200";
517
+ const sizeClass = size().dot;
518
+ const visibilityClass = renderProps.isSelected ? "scale-100 opacity-100" : "scale-0 opacity-0";
519
+ return [base, sizeClass, visibilityClass].filter(Boolean).join(" ");
520
+ };
521
+ const labelClasses = () => {
522
+ const base = "text-primary-200";
523
+ const sizeClass = size().label;
524
+ const disabledClass = renderProps.isDisabled ? "opacity-50" : "";
525
+ return [base, sizeClass, disabledClass].filter(Boolean).join(" ");
526
+ };
527
+ const resolvedChildren = typeof children === "function" ? children(renderProps) : children;
528
+ return <>
529
+ <span class={circleClasses()}>
530
+ <span class={dotClasses()} />
531
+ </span>
532
+ <Show5 when={resolvedChildren}>
533
+ <span class={labelClasses()}>{resolvedChildren}</span>
534
+ </Show5>
535
+ </>;
536
+ }}
537
+ </HeadlessRadio>;
538
+ }
539
+
540
+ // src/dialog/index.tsx
541
+ import { Show as Show6 } from "solid-js";
542
+
543
+ // src/custom/chip/index.tsx
544
+ var variantStyles3 = {
545
+ primary: "bg-primary-700 text-primary-200 shadow-primary-chip",
546
+ secondary: "bg-primary-600 text-primary-100 hover:bg-primary-500",
547
+ accent: "bg-accent text-white",
548
+ outline: "bg-transparent border border-primary-500 text-primary-300"
549
+ };
550
+ function Chip(props) {
551
+ const variant = () => props.variant ?? "primary";
552
+ return <button
553
+ class={`flex justify-center items-center h-6 w-auto rounded-full px-4 py-1 font-medium text-sm tracking-wide transition-colors ${variantStyles3[variant()]} ${props.class ?? ""}`}
554
+ onClick={props.onClick}
555
+ >
556
+ {props.icon && <span class="mr-1.5">{props.icon}</span>}
557
+ <span>{props.text}</span>
558
+ </button>;
559
+ }
560
+
561
+ // src/dialog/index.tsx
562
+ function Dialog(props) {
563
+ return <div class={`flex flex-col w-full max-w-[540px] ${props.class ?? ""}`}>
564
+ <span class="relative left-8 top-4 bg-gradient-to-b from-primary-100 to-primary-500 bg-clip-text text-4xl font-extrabold text-transparent">
565
+ {props.title}
566
+ </span>
567
+ <div class="flex h-[50px] flex-col justify-end rounded-t-[26px] bg-primary-800">
568
+ <div class="flex h-[40px] flex-col justify-end rounded-t-[30px] bg-primary-600">
569
+ <div class="flex h-[20px] items-center justify-end rounded-t-[100px] bg-primary-700 pr-4">
570
+ <Show6 when={props.subtitle}>
571
+ <span class="text-base font-normal text-primary-300">
572
+ {props.subtitle}
573
+ </span>
574
+ </Show6>
575
+ </div>
576
+ </div>
577
+ </div>
578
+ <div class="mt-1 flex min-h-[200px] flex-col rounded-b-[26px] rounded-t-lg border-b border-accent-500 bg-bg-300">
579
+ <div class="flex flex-1 flex-col p-4">
580
+ <div class="text-lg text-gray-100">{props.children}</div>
581
+ <Show6 when={props.primaryAction || props.secondaryAction}>
582
+ <div class="flex flex-1 flex-row items-end justify-center gap-5 py-5">
583
+ <Show6 when={props.secondaryAction}>
584
+ <Button variant="primary" buttonStyle="outline" onPress={props.secondaryAction.onClick}>
585
+ {props.secondaryAction.label}
586
+ </Button>
587
+ </Show6>
588
+ <Show6 when={props.primaryAction}>
589
+ <Button variant="primary" onPress={props.primaryAction.onClick}>
590
+ {props.primaryAction.label}
591
+ </Button>
592
+ </Show6>
593
+ </div>
594
+ </Show6>
595
+ </div>
596
+ </div>
597
+ <Show6 when={props.tags && props.tags.length > 0}>
598
+ <div class="relative bottom-4 right-4 w-full">
599
+ <div class="flex flex-row justify-end gap-2 pr-4">
600
+ {props.tags.map((tag) => <Chip variant="primary" text={tag} />)}
601
+ </div>
602
+ </div>
603
+ </Show6>
604
+ </div>;
605
+ }
606
+
607
+ // src/icon/icons/GitHubIcon.tsx
608
+ function GitHubIcon(props) {
609
+ return <svg
610
+ viewBox="0 0 24 24"
611
+ width={props.size}
612
+ height={props.size}
613
+ fill={props.color}
614
+ aria-hidden="true"
615
+ >
616
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
617
+ </svg>;
618
+ }
619
+
620
+ // src/icon/index.tsx
621
+ function Icon(props) {
622
+ const size = () => props.size ?? 24;
623
+ const color = () => props.color ?? "var(--color-primary-500)";
624
+ const IconComponent = props.icon;
625
+ return <div
626
+ class={`vui-icon ${props.withShadow ? "vui-icon--with-shadow" : ""} ${props.class ?? ""}`}
627
+ onClick={props.onClick}
628
+ >
629
+ {props.withShadow && <div class="vui-icon__shadow">
630
+ <IconComponent size={size()} color="var(--color-accent)" />
631
+ </div>}
632
+ <div class="vui-icon__main">
633
+ <IconComponent size={size()} color={color()} />
634
+ </div>
635
+ </div>;
636
+ }
637
+
638
+ // src/tooltip/index.tsx
639
+ function Tooltip(props) {
640
+ const position = () => props.position ?? "bottom";
641
+ return <div class={`vui-tooltip ${props.class ?? ""}`}>
642
+ <div class="vui-tooltip__trigger">
643
+ {props.children}
644
+ </div>
645
+ <div class={`vui-tooltip__content vui-tooltip__content--${position()}`}>
646
+ <span>{props.label}</span>
647
+ </div>
648
+ </div>;
649
+ }
650
+
651
+ // src/textfield/index.tsx
652
+ import { splitProps as splitProps5, mergeProps as solidMergeProps4, Show as Show7 } from "solid-js";
653
+ import {
654
+ createTextField,
655
+ createFocusRing
656
+ } from "@proyecto-viviana/solidaria";
657
+
658
+ // ../solid-stately/dist/index.js
659
+ import { createSignal as createSignal2, createUniqueId, untrack } from "solid-js";
660
+ import { isServer } from "solid-js/web";
661
+ function access(value) {
662
+ return typeof value === "function" ? value() : value;
663
+ }
664
+ function createTextFieldState(props = {}) {
665
+ const getProps = () => access(props);
666
+ const initialProps = getProps();
667
+ const initialValue = initialProps.value ?? initialProps.defaultValue ?? "";
668
+ const [internalValue, setInternalValue] = createSignal2(initialValue);
669
+ const isControlled = () => getProps().value !== void 0;
670
+ const value = () => {
671
+ const p = getProps();
672
+ return isControlled() ? p.value ?? "" : internalValue();
673
+ };
674
+ function setValue(newValue) {
675
+ const p = getProps();
676
+ if (!isControlled()) {
677
+ setInternalValue(newValue);
678
+ }
679
+ p.onChange?.(newValue);
680
+ }
681
+ return {
682
+ value,
683
+ setValue
684
+ };
685
+ }
686
+
687
+ // src/textfield/index.tsx
688
+ var sizeStyles6 = {
689
+ sm: {
690
+ input: "h-8 px-2 text-sm",
691
+ label: "text-sm",
692
+ description: "text-xs"
693
+ },
694
+ md: {
695
+ input: "h-10 px-3 text-base",
696
+ label: "text-sm",
697
+ description: "text-sm"
698
+ },
699
+ lg: {
700
+ input: "h-12 px-4 text-lg",
701
+ label: "text-base",
702
+ description: "text-sm"
703
+ }
704
+ };
705
+ function TextField(props) {
706
+ const defaultProps = {
707
+ size: "md",
708
+ variant: "outline"
709
+ };
710
+ const merged = solidMergeProps4(defaultProps, props);
711
+ const [local, ariaProps] = splitProps5(merged, [
712
+ "size",
713
+ "variant",
714
+ "class",
715
+ "label",
716
+ "description",
717
+ "errorMessage"
718
+ ]);
719
+ const size = () => sizeStyles6[local.size];
720
+ const state = createTextFieldState(() => ({
721
+ value: ariaProps.value,
722
+ defaultValue: ariaProps.defaultValue,
723
+ onChange: ariaProps.onChange
724
+ }));
725
+ const textFieldAria = createTextField(() => ({
726
+ ...ariaProps,
727
+ value: state.value(),
728
+ onChange: state.setValue
729
+ }));
730
+ const { isFocused, isFocusVisible, focusProps } = createFocusRing();
731
+ const containerClasses = () => {
732
+ const base = "flex flex-col";
733
+ const disabledClass = ariaProps.isDisabled ? "opacity-60" : "";
734
+ const custom = local.class || "";
735
+ return [base, disabledClass, custom].filter(Boolean).join(" ");
736
+ };
737
+ const inputClasses = () => {
738
+ const base = "w-full rounded-md transition-all duration-200 outline-none";
739
+ const sizeClass = size().input;
740
+ let variantClass;
741
+ if (local.variant === "filled") {
742
+ variantClass = "bg-bg-200 border border-transparent";
743
+ } else {
744
+ variantClass = "bg-transparent border border-bg-400";
745
+ }
746
+ let stateClass;
747
+ if (ariaProps.isDisabled) {
748
+ stateClass = "bg-bg-200 text-primary-500 cursor-not-allowed";
749
+ } else if (textFieldAria.isInvalid) {
750
+ stateClass = "border-danger-500 focus:border-danger-400 focus:ring-2 focus:ring-danger-400/20";
751
+ } else {
752
+ stateClass = "text-primary-100 placeholder:text-primary-500 focus:border-accent focus:ring-2 focus:ring-accent/20";
753
+ }
754
+ const hoverClass = ariaProps.isDisabled ? "" : "hover:border-accent-300";
755
+ return [base, sizeClass, variantClass, stateClass, hoverClass].filter(Boolean).join(" ");
756
+ };
757
+ const labelClasses = () => {
758
+ const base = "block font-medium text-primary-200 mb-1";
759
+ const sizeClass = size().label;
760
+ return [base, sizeClass].filter(Boolean).join(" ");
761
+ };
762
+ const descriptionClasses = () => {
763
+ const base = "mt-1 text-primary-400";
764
+ const sizeClass = size().description;
765
+ return [base, sizeClass].filter(Boolean).join(" ");
766
+ };
767
+ const errorClasses = () => {
768
+ const base = "mt-1 text-danger-400";
769
+ const sizeClass = size().description;
770
+ return [base, sizeClass].filter(Boolean).join(" ");
771
+ };
772
+ const cleanLabelProps = () => {
773
+ const { ref: _ref, ...rest } = textFieldAria.labelProps;
774
+ return rest;
775
+ };
776
+ const cleanInputProps = () => {
777
+ const { ref: _ref1, ...rest } = textFieldAria.inputProps;
778
+ const { ref: _ref2, ...focusRest } = focusProps;
779
+ return { ...rest, ...focusRest };
780
+ };
781
+ const cleanDescriptionProps = () => {
782
+ const { ref: _ref, ...rest } = textFieldAria.descriptionProps;
783
+ return rest;
784
+ };
785
+ const cleanErrorMessageProps = () => {
786
+ const { ref: _ref, ...rest } = textFieldAria.errorMessageProps;
787
+ return rest;
788
+ };
789
+ return <div
790
+ class={containerClasses()}
791
+ data-disabled={ariaProps.isDisabled || void 0}
792
+ data-invalid={textFieldAria.isInvalid || void 0}
793
+ data-readonly={ariaProps.isReadOnly || void 0}
794
+ data-required={ariaProps.isRequired || void 0}
795
+ data-focused={isFocused() || void 0}
796
+ data-focus-visible={isFocusVisible() || void 0}
797
+ >
798
+ <Show7 when={local.label}>
799
+ <label {...cleanLabelProps()} class={labelClasses()}>
800
+ {local.label}
801
+ <Show7 when={ariaProps.isRequired}>
802
+ <span class="text-danger-400 ml-0.5">*</span>
803
+ </Show7>
804
+ </label>
805
+ </Show7>
806
+
807
+ <input {...cleanInputProps()} class={inputClasses()} />
808
+
809
+ <Show7 when={local.description && !textFieldAria.isInvalid}>
810
+ <p {...cleanDescriptionProps()} class={descriptionClasses()}>
811
+ {local.description}
812
+ </p>
813
+ </Show7>
814
+
815
+ <Show7 when={local.errorMessage && textFieldAria.isInvalid}>
816
+ <p {...cleanErrorMessageProps()} class={errorClasses()}>
817
+ {local.errorMessage}
818
+ </p>
819
+ </Show7>
820
+ </div>;
821
+ }
822
+
823
+ // src/link/index.tsx
824
+ import { splitProps as splitProps6 } from "solid-js";
825
+ import {
826
+ Link as HeadlessLink
827
+ } from "@proyecto-viviana/solidaria-components";
828
+ var variantStyles4 = {
829
+ primary: "text-accent hover:text-accent-300",
830
+ secondary: "text-primary-300 hover:text-primary-200",
831
+ subtle: "text-primary-400 hover:text-primary-300"
832
+ };
833
+ function Link(props) {
834
+ const [local, headlessProps] = splitProps6(props, [
835
+ "variant",
836
+ "isStandalone",
837
+ "isQuiet",
838
+ "class",
839
+ "children"
840
+ ]);
841
+ const variant = local.variant ?? "primary";
842
+ const customClass = local.class ?? "";
843
+ const getClassName = (renderProps) => {
844
+ const base = "transition-colors duration-200 cursor-pointer rounded-sm outline-none";
845
+ const variantClass = variantStyles4[variant];
846
+ let underlineClass;
847
+ if (local.isStandalone && local.isQuiet) {
848
+ underlineClass = renderProps.isHovered || renderProps.isFocusVisible ? "underline" : "no-underline";
849
+ } else {
850
+ underlineClass = "underline";
851
+ }
852
+ const weightClass = local.isStandalone ? "font-medium" : "";
853
+ const focusClass = renderProps.isFocusVisible ? "ring-2 ring-accent-300 ring-offset-2 ring-offset-bg-400" : "";
854
+ const disabledClass = renderProps.isDisabled ? "opacity-50 cursor-not-allowed" : "";
855
+ const pressedClass = renderProps.isPressed ? "opacity-80" : "";
856
+ return [
857
+ base,
858
+ variantClass,
859
+ underlineClass,
860
+ weightClass,
861
+ focusClass,
862
+ disabledClass,
863
+ pressedClass,
864
+ customClass
865
+ ].filter(Boolean).join(" ");
866
+ };
867
+ return <HeadlessLink
868
+ {...headlessProps}
869
+ class={getClassName}
870
+ >
871
+ {local.children}
872
+ </HeadlessLink>;
873
+ }
874
+
875
+ // src/progress-bar/index.tsx
876
+ import { splitProps as splitProps7, Show as Show8, createMemo } from "solid-js";
877
+ import { createProgressBar } from "@proyecto-viviana/solidaria";
878
+ var sizeStyles7 = {
879
+ sm: {
880
+ track: "h-1",
881
+ text: "text-xs"
882
+ },
883
+ md: {
884
+ track: "h-2",
885
+ text: "text-sm"
886
+ },
887
+ lg: {
888
+ track: "h-3",
889
+ text: "text-base"
890
+ }
891
+ };
892
+ var variantStyles5 = {
893
+ primary: "bg-primary-500",
894
+ accent: "bg-accent",
895
+ success: "bg-green-500",
896
+ warning: "bg-yellow-500",
897
+ danger: "bg-red-500"
898
+ };
899
+ function clamp(value, min, max) {
900
+ return Math.min(Math.max(value, min), max);
901
+ }
902
+ function ProgressBar(props) {
903
+ const [local, ariaProps] = splitProps7(props, [
904
+ "size",
905
+ "variant",
906
+ "label",
907
+ "showValueLabel",
908
+ "class"
909
+ ]);
910
+ const size = () => local.size ?? "md";
911
+ const variant = () => local.variant ?? "primary";
912
+ const isIndeterminate = () => ariaProps.isIndeterminate ?? false;
913
+ const showValueLabel = () => local.showValueLabel ?? !isIndeterminate();
914
+ const progressAria = createProgressBar({
915
+ get value() {
916
+ return ariaProps.value;
917
+ },
918
+ get minValue() {
919
+ return ariaProps.minValue;
920
+ },
921
+ get maxValue() {
922
+ return ariaProps.maxValue;
923
+ },
924
+ get valueLabel() {
925
+ return ariaProps.valueLabel;
926
+ },
927
+ get isIndeterminate() {
928
+ return ariaProps.isIndeterminate;
929
+ },
930
+ get label() {
931
+ return local.label;
932
+ },
933
+ get "aria-label"() {
934
+ return ariaProps["aria-label"];
935
+ }
936
+ });
937
+ const percentage = createMemo(() => {
938
+ if (isIndeterminate()) {
939
+ return void 0;
940
+ }
941
+ const value = ariaProps.value ?? 0;
942
+ const minValue = ariaProps.minValue ?? 0;
943
+ const maxValue = ariaProps.maxValue ?? 100;
944
+ const clampedValue = clamp(value, minValue, maxValue);
945
+ return (clampedValue - minValue) / (maxValue - minValue) * 100;
946
+ });
947
+ const valueText = () => progressAria.progressBarProps["aria-valuetext"];
948
+ const sizeConfig = () => sizeStyles7[size()];
949
+ return <div
950
+ {...progressAria.progressBarProps}
951
+ class={`w-full ${local.class ?? ""}`}
952
+ >
953
+ {
954
+ /* Label and value row */
955
+ }
956
+ <Show8 when={local.label || showValueLabel()}>
957
+ <div class={`flex justify-between items-center mb-1 ${sizeConfig().text}`}>
958
+ <Show8 when={local.label}>
959
+ <span class="text-primary-200 font-medium">{local.label}</span>
960
+ </Show8>
961
+ <Show8 when={showValueLabel() && !isIndeterminate()}>
962
+ <span class="text-primary-300">{valueText()}</span>
963
+ </Show8>
964
+ </div>
965
+ </Show8>
966
+
967
+ {
968
+ /* Track */
969
+ }
970
+ <div class={`w-full ${sizeConfig().track} bg-bg-300 rounded-full overflow-hidden`}>
971
+ {
972
+ /* Fill */
973
+ }
974
+ <div
975
+ class={`h-full rounded-full transition-all duration-300 ${variantStyles5[variant()]} ${isIndeterminate() ? "animate-progress-indeterminate" : ""}`}
976
+ style={{
977
+ width: isIndeterminate() ? "30%" : `${percentage()}%`
978
+ }}
979
+ />
980
+ </div>
981
+ </div>;
982
+ }
983
+
984
+ // src/separator/index.tsx
985
+ import { splitProps as splitProps8, createMemo as createMemo2, Show as Show9 } from "solid-js";
986
+ import { createSeparator } from "@proyecto-viviana/solidaria";
987
+ var variantStyles6 = {
988
+ default: "bg-bg-100",
989
+ subtle: "bg-bg-200",
990
+ strong: "bg-primary-600"
991
+ };
992
+ var horizontalSizeStyles = {
993
+ sm: "h-px",
994
+ md: "h-0.5",
995
+ lg: "h-1"
996
+ };
997
+ var verticalSizeStyles = {
998
+ sm: "w-px",
999
+ md: "w-0.5",
1000
+ lg: "w-1"
1001
+ };
1002
+ function Separator(props) {
1003
+ const [local, ariaProps] = splitProps8(props, [
1004
+ "orientation",
1005
+ "variant",
1006
+ "size",
1007
+ "class"
1008
+ ]);
1009
+ const orientation = () => local.orientation ?? "horizontal";
1010
+ const variant = () => local.variant ?? "default";
1011
+ const size = () => local.size ?? "md";
1012
+ const elementType = createMemo2(() => {
1013
+ if (orientation() === "vertical") {
1014
+ return "div";
1015
+ }
1016
+ return "hr";
1017
+ });
1018
+ const separatorAria = createSeparator({
1019
+ get orientation() {
1020
+ return orientation();
1021
+ },
1022
+ get elementType() {
1023
+ return elementType();
1024
+ },
1025
+ get "aria-label"() {
1026
+ return ariaProps["aria-label"];
1027
+ }
1028
+ });
1029
+ const className = createMemo2(() => {
1030
+ const isVertical = orientation() === "vertical";
1031
+ const sizeStyles8 = isVertical ? verticalSizeStyles : horizontalSizeStyles;
1032
+ const base = [
1033
+ variantStyles6[variant()],
1034
+ sizeStyles8[size()],
1035
+ isVertical ? "h-full self-stretch" : "w-full",
1036
+ "border-0",
1037
+ // Reset hr default border
1038
+ local.class ?? ""
1039
+ ];
1040
+ return base.filter(Boolean).join(" ");
1041
+ });
1042
+ const getAriaProps = () => {
1043
+ const { ref: _, ...props2 } = separatorAria.separatorProps;
1044
+ return props2;
1045
+ };
1046
+ return <Show9
1047
+ when={orientation() === "vertical"}
1048
+ fallback={<hr
1049
+ {...getAriaProps()}
1050
+ class={className()}
1051
+ />}
1052
+ >
1053
+ <div
1054
+ {...getAriaProps()}
1055
+ class={className()}
1056
+ />
1057
+ </Show9>;
1058
+ }
1059
+
1060
+ // src/custom/nav-header/index.tsx
1061
+ import { Show as Show10 } from "solid-js";
1062
+ function NavHeader(props) {
1063
+ return <nav class={`flex items-center bg-bg-400 h-[70px] border-b-4 border-accent-500 ${props.class ?? ""}`}>
1064
+ <div class="pl-1 md:pl-8 flex items-center">
1065
+ <Show10 when={props.logo} fallback={<Show10 when={props.logoText}>
1066
+ <span class="text-[34px] font-light text-primary-700 flex items-center">
1067
+ {props.logoText}
1068
+ </span>
1069
+ </Show10>}>
1070
+ <img src={props.logo} alt={props.logoAlt ?? "Logo"} class="h-[42px] w-auto" />
1071
+ </Show10>
1072
+ </div>
1073
+
1074
+ <div class="flex-1 flex justify-end items-center pr-1 md:pr-8">
1075
+ {props.children}
1076
+ <Show10 when={props.menuIcon}>
1077
+ <button
1078
+ class="md:hidden flex items-center justify-center"
1079
+ onClick={props.onMenuClick}
1080
+ >
1081
+ {props.menuIcon}
1082
+ </button>
1083
+ </Show10>
1084
+ </div>
1085
+ </nav>;
1086
+ }
1087
+
1088
+ // src/custom/lateral-nav/index.tsx
1089
+ import { Show as Show11, For } from "solid-js";
1090
+ function NavItem(props) {
1091
+ return <li class={`flex items-center ${props.class ?? ""}`}>
1092
+ <span class="text-lg font-bold text-primary-200">{props.title}</span>
1093
+ {props.children}
1094
+ </li>;
1095
+ }
1096
+ function NavLink(props) {
1097
+ const activeStyles = "font-medium text-primary-300 underline underline-offset-4";
1098
+ const inactiveStyles = "font-normal text-gray-200 underline-offset-4 hover:text-gray-100 hover:underline";
1099
+ return <a
1100
+ href={props.href}
1101
+ class={`${props.active ? activeStyles : inactiveStyles} ${props.class ?? ""}`}
1102
+ >
1103
+ {props.children}
1104
+ </a>;
1105
+ }
1106
+ function NavSection(props) {
1107
+ return <div class={props.class ?? ""}>
1108
+ <NavItem title={props.title} />
1109
+ <div class="flex h-full">
1110
+ <div class="h-5 w-1 bg-accent-300" />
1111
+ <ul class="flex h-full flex-1 flex-col gap-1 pl-4">
1112
+ <Show11 when={props.links}>
1113
+ <For each={props.links}>
1114
+ {(link) => <li>
1115
+ <NavLink href={link.href} active={link.active}>
1116
+ {link.label}
1117
+ </NavLink>
1118
+ </li>}
1119
+ </For>
1120
+ </Show11>
1121
+ {props.children}
1122
+ </ul>
1123
+ </div>
1124
+ </div>;
1125
+ }
1126
+ function LateralNav(props) {
1127
+ const bgColor = () => props.transparent ? "" : "bg-bg-200";
1128
+ return <div
1129
+ class={`hidden w-[300px] md:block ${bgColor()} m-0 border-r border-primary-600 p-3 ${props.class ?? ""}`}
1130
+ >
1131
+ {props.children}
1132
+ </div>;
1133
+ }
1134
+
1135
+ // src/custom/timeline-item/index.tsx
1136
+ import { Show as Show12 } from "solid-js";
1137
+ var eventMessages = {
1138
+ follow: (left, right) => <>
1139
+ <span class="font-semibold text-accent-200">{left}</span>
1140
+ {" ha empezado a seguir a "}
1141
+ <span class="font-semibold text-accent-200">{right}</span>
1142
+ </>,
1143
+ like: (left, right) => <>
1144
+ <span class="font-semibold text-accent-200">{left}</span>
1145
+ {" le ha dado like a "}
1146
+ <span class="font-semibold text-accent-200">{right}</span>
1147
+ </>,
1148
+ comment: (left, right) => <>
1149
+ <span class="font-semibold text-accent-200">{left}</span>
1150
+ {" ha comentado en "}
1151
+ <span class="font-semibold text-accent-200">{right}</span>
1152
+ </>,
1153
+ event: (left, right) => <>
1154
+ <span class="font-semibold text-accent-200">{left}</span>
1155
+ {" asistir\xE1 al evento de "}
1156
+ <span class="font-semibold text-accent-200">{right}</span>
1157
+ </>,
1158
+ custom: () => null
1159
+ };
1160
+ function TimelineItem(props) {
1161
+ const type = () => props.type ?? "follow";
1162
+ const leftName = () => props.leftUser?.name ?? "";
1163
+ const rightName = () => props.rightUser?.name ?? "";
1164
+ return <div class={`inline-flex w-auto flex-col gap-5 rounded-2xl border border-primary-700 bg-bg-200 p-5 hover:bg-bg-300 transition-colors ${props.class ?? ""}`}>
1165
+ <div class="flex items-center justify-around gap-3">
1166
+ <Show12 when={props.leftUser}>
1167
+ <Avatar src={props.leftUser.avatar} alt={props.leftUser.name} />
1168
+ </Show12>
1169
+ <Show12 when={props.icon}>
1170
+ {props.icon}
1171
+ </Show12>
1172
+ <Show12 when={props.rightUser}>
1173
+ <Avatar src={props.rightUser.avatar} alt={props.rightUser.name} />
1174
+ </Show12>
1175
+ </div>
1176
+ <div class="flex items-center justify-center gap-3 text-center">
1177
+ <span class="font-light text-primary-300">
1178
+ <Show12 when={props.message} fallback={eventMessages[type()](leftName(), rightName())}>
1179
+ {props.message}
1180
+ </Show12>
1181
+ </span>
1182
+ </div>
1183
+ </div>;
1184
+ }
1185
+
1186
+ // src/custom/conversation/index.tsx
1187
+ import { Show as Show13, For as For2 } from "solid-js";
1188
+ function ConversationPreview(props) {
1189
+ return <button
1190
+ class={`flex w-full items-center gap-3 rounded-xl p-3 hover:bg-bg-300 transition-colors text-left ${props.class ?? ""}`}
1191
+ onClick={props.onClick}
1192
+ >
1193
+ <Avatar
1194
+ src={props.user.avatar}
1195
+ alt={props.user.name}
1196
+ online={props.user.online}
1197
+ size="md"
1198
+ />
1199
+ <div class="flex-1 min-w-0">
1200
+ <div class="flex items-center justify-between">
1201
+ <span class="font-semibold text-primary-100 truncate">{props.user.name}</span>
1202
+ <Show13 when={props.timestamp}>
1203
+ <span class="text-xs text-primary-500">{props.timestamp}</span>
1204
+ </Show13>
1205
+ </div>
1206
+ <Show13 when={props.lastMessage}>
1207
+ <p class="text-sm text-primary-400 truncate">{props.lastMessage}</p>
1208
+ </Show13>
1209
+ </div>
1210
+ <Show13 when={props.unreadCount && props.unreadCount > 0}>
1211
+ <span class="flex h-5 w-5 items-center justify-center rounded-full bg-accent text-xs font-bold text-white">
1212
+ {props.unreadCount}
1213
+ </span>
1214
+ </Show13>
1215
+ </button>;
1216
+ }
1217
+ function ConversationBubble(props) {
1218
+ const isUser = () => props.sender === "user";
1219
+ return <div class={`flex ${isUser() ? "justify-end" : "justify-start"} ${props.class ?? ""}`}>
1220
+ <div
1221
+ class={`max-w-[70%] rounded-2xl px-4 py-2 ${isUser() ? "bg-accent text-white rounded-br-sm" : "bg-bg-300 text-primary-100 rounded-bl-sm"}`}
1222
+ >
1223
+ <p>{props.content}</p>
1224
+ <Show13 when={props.timestamp}>
1225
+ <span class={`text-xs ${isUser() ? "text-white/70" : "text-primary-500"}`}>
1226
+ {props.timestamp}
1227
+ </span>
1228
+ </Show13>
1229
+ </div>
1230
+ </div>;
1231
+ }
1232
+ function Conversation(props) {
1233
+ return <div class={`flex flex-col gap-2 p-4 ${props.class ?? ""}`}>
1234
+ <For2 each={props.messages}>
1235
+ {(message) => <ConversationBubble
1236
+ content={message.content}
1237
+ sender={message.sender}
1238
+ timestamp={message.timestamp}
1239
+ />}
1240
+ </For2>
1241
+ </div>;
1242
+ }
1243
+
1244
+ // src/custom/profile-card/index.tsx
1245
+ import { Show as Show14 } from "solid-js";
1246
+ function ProfileCard(props) {
1247
+ const formatNumber = (num) => {
1248
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
1249
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
1250
+ return num.toString();
1251
+ };
1252
+ return <div class={`bg-bg-200 rounded-xl p-4 ${props.class ?? ""}`}>
1253
+ <div class="flex items-start gap-4">
1254
+ <Avatar src={props.avatar} alt={props.username} size="lg" />
1255
+ <div class="flex-1 min-w-0">
1256
+ <h3 class="font-semibold text-primary-100 text-lg truncate">
1257
+ {props.username}
1258
+ </h3>
1259
+ <Show14 when={props.bio}>
1260
+ <p class="text-primary-300 text-sm mt-1 line-clamp-2">{props.bio}</p>
1261
+ </Show14>
1262
+ <div class="flex gap-4 mt-2 text-sm">
1263
+ <Show14 when={props.followers !== void 0}>
1264
+ <span class="text-primary-300">
1265
+ <span class="font-semibold text-primary-100">
1266
+ {formatNumber(props.followers)}
1267
+ </span>{" "}
1268
+ seguidores
1269
+ </span>
1270
+ </Show14>
1271
+ <Show14 when={props.following !== void 0}>
1272
+ <span class="text-primary-300">
1273
+ <span class="font-semibold text-primary-100">
1274
+ {formatNumber(props.following)}
1275
+ </span>{" "}
1276
+ siguiendo
1277
+ </span>
1278
+ </Show14>
1279
+ </div>
1280
+ </div>
1281
+ </div>
1282
+ <Show14 when={props.actions}>
1283
+ <div class="mt-4 flex gap-2">{props.actions}</div>
1284
+ </Show14>
1285
+ </div>;
1286
+ }
1287
+
1288
+ // src/custom/event-card/index.tsx
1289
+ import { Show as Show15, For as For3 } from "solid-js";
1290
+ function EventCard(props) {
1291
+ const displayedAttendees = () => props.attendees?.slice(0, 3) ?? [];
1292
+ const remainingCount = () => {
1293
+ const total = props.attendeeCount ?? props.attendees?.length ?? 0;
1294
+ const displayed = displayedAttendees().length;
1295
+ return total - displayed;
1296
+ };
1297
+ return <div class={`relative bg-bg-200 rounded-3xl overflow-hidden ${props.class ?? ""}`}>
1298
+ {
1299
+ /* Decoration image (fire gif, etc) */
1300
+ }
1301
+ <Show15 when={props.decorationImage}>
1302
+ <div class="absolute -top-2 -right-2 z-10 flex flex-col gap-1">
1303
+ <img src={props.decorationImage} alt="" class="w-8 h-8 object-contain" />
1304
+ <img src={props.decorationImage} alt="" class="w-6 h-6 object-contain ml-2" />
1305
+ <img src={props.decorationImage} alt="" class="w-5 h-5 object-contain" />
1306
+ </div>
1307
+ </Show15>
1308
+
1309
+ <Show15 when={props.image}>
1310
+ <div class="relative h-32 w-full">
1311
+ <img
1312
+ src={props.image}
1313
+ alt={props.title}
1314
+ class="w-full h-full object-cover"
1315
+ />
1316
+ <div class="absolute inset-0 bg-gradient-to-t from-bg-200 via-transparent to-transparent" />
1317
+ </div>
1318
+ </Show15>
1319
+
1320
+ <div class="p-4 pt-2">
1321
+ <h3 class="font-bold text-xl leading-tight bg-gradient-to-r from-accent to-accent-300 bg-clip-text text-transparent">
1322
+ {props.title}
1323
+ </h3>
1324
+
1325
+ <Show15 when={props.date || props.author}>
1326
+ <div class="flex items-center gap-4 mt-3 text-sm text-primary-300">
1327
+ <Show15 when={props.author}>
1328
+ <div class="flex items-center gap-1.5">
1329
+ <span class="text-accent">@</span>
1330
+ <span>{props.author}</span>
1331
+ </div>
1332
+ </Show15>
1333
+ <Show15 when={props.date}>
1334
+ <div class="flex items-center gap-1.5">
1335
+ <span class="text-accent">⏱</span>
1336
+ <span>{props.date}</span>
1337
+ </div>
1338
+ </Show15>
1339
+ </div>
1340
+ </Show15>
1341
+
1342
+ <Show15 when={displayedAttendees().length > 0}>
1343
+ <div class="flex items-center mt-3">
1344
+ <div class="flex -space-x-2">
1345
+ <For3 each={displayedAttendees()}>
1346
+ {(attendee) => <Avatar src={attendee.avatar} alt={attendee.name} size="sm" />}
1347
+ </For3>
1348
+ </div>
1349
+ <Show15 when={remainingCount() > 0}>
1350
+ <span class="ml-2 text-sm text-primary-300">
1351
+ +{remainingCount()} más
1352
+ </span>
1353
+ </Show15>
1354
+ </div>
1355
+ </Show15>
1356
+
1357
+ <Show15 when={props.actions}>
1358
+ <div class="mt-4 flex gap-2">{props.actions}</div>
1359
+ </Show15>
1360
+ </div>
1361
+ </div>;
1362
+ }
1363
+ function EventListItem(props) {
1364
+ return <button
1365
+ class={`flex items-center gap-3 w-full p-2 rounded-lg hover:bg-bg-300 transition-colors text-left ${props.class ?? ""}`}
1366
+ onClick={props.onClick}
1367
+ >
1368
+ <Show15 when={props.image}>
1369
+ <div class="w-12 h-12 rounded-lg overflow-hidden flex-shrink-0">
1370
+ <img
1371
+ src={props.image}
1372
+ alt={props.title}
1373
+ class="w-full h-full object-cover"
1374
+ />
1375
+ </div>
1376
+ </Show15>
1377
+ <div class="flex-1 min-w-0">
1378
+ <h4 class="font-medium text-primary-100 truncate">{props.title}</h4>
1379
+ <Show15 when={props.subtitle}>
1380
+ <p class="text-sm text-primary-300 truncate">{props.subtitle}</p>
1381
+ </Show15>
1382
+ </div>
1383
+ </button>;
1384
+ }
1385
+
1386
+ // src/custom/calendar-card/index.tsx
1387
+ import { Show as Show16, For as For4 } from "solid-js";
1388
+ function CalendarCard(props) {
1389
+ const displayedFollowers = () => props.followers?.slice(0, 2) ?? [];
1390
+ const remainingCount = () => {
1391
+ const total = props.followerCount ?? props.followers?.length ?? 0;
1392
+ const displayed = displayedFollowers().length;
1393
+ return total - displayed;
1394
+ };
1395
+ return <div class={`flex h-[100px] w-full max-w-[500px] items-center rounded-xl border border-primary-600 border-b-accent-500 bg-bg-300 p-2 ${props.class ?? ""}`}>
1396
+ <Show16 when={props.image}>
1397
+ <div class="relative h-[80px] w-[80px] flex-shrink-0 overflow-hidden rounded-xl border-2 border-accent-200">
1398
+ <img src={props.image} alt={props.title} class="h-full w-full object-cover" />
1399
+ </div>
1400
+ </Show16>
1401
+ <div class="relative h-full flex-1 flex-col pl-3">
1402
+ <Show16 when={props.tags && props.tags.length > 0}>
1403
+ <div class="absolute bottom-[-20px] h-[30px] w-full">
1404
+ <div class="flex h-full w-full flex-1 justify-end gap-1">
1405
+ <For4 each={props.tags}>
1406
+ {(tag) => <Chip text={tag} variant="primary" />}
1407
+ </For4>
1408
+ </div>
1409
+ </div>
1410
+ </Show16>
1411
+ <div class="flex h-full flex-col items-end justify-center pb-3 pr-5 pt-2">
1412
+ <div class="flex flex-1">
1413
+ <span class="text-lg font-semibold text-white drop-shadow-sm">
1414
+ {props.title}
1415
+ </span>
1416
+ </div>
1417
+ <Show16 when={displayedFollowers().length > 0}>
1418
+ <div class="flex flex-1">
1419
+ <span class="text-base font-normal text-primary-500">
1420
+ seguida por{" "}
1421
+ <For4 each={displayedFollowers()}>
1422
+ {(follower, index) => <>
1423
+ <span class="font-semibold text-accent-200">{follower.name}</span>
1424
+ {index() < displayedFollowers().length - 1 && ", "}
1425
+ </>}
1426
+ </For4>
1427
+ <Show16 when={remainingCount() > 0}>
1428
+ {" "}y <span class="font-semibold text-accent-200">{remainingCount()} más</span>
1429
+ </Show16>
1430
+ </span>
1431
+ </div>
1432
+ </Show16>
1433
+ </div>
1434
+ </div>
1435
+ </div>;
1436
+ }
1437
+
1438
+ // src/custom/logo/index.tsx
1439
+ function Logo(props) {
1440
+ const sizeClass = () => {
1441
+ switch (props.size) {
1442
+ case "sm":
1443
+ return "vui-logo--sm";
1444
+ case "lg":
1445
+ return "vui-logo--lg";
1446
+ case "xl":
1447
+ return "vui-logo--xl";
1448
+ case "md":
1449
+ default:
1450
+ return "vui-logo--md";
1451
+ }
1452
+ };
1453
+ const firstWord = () => props.firstWord ?? "Proyecto";
1454
+ const secondWord = () => props.secondWord ?? "Viviana";
1455
+ return <span class={`vui-logo ${sizeClass()} ${props.class ?? ""}`}>
1456
+ <span class="vui-logo__first">{firstWord()}</span>
1457
+ <span class="vui-logo__second" data-text={secondWord()}>
1458
+ {secondWord()}
1459
+ </span>
1460
+ </span>;
1461
+ }
1462
+
1463
+ // src/custom/project-card/index.tsx
1464
+ function ProjectCard(props) {
1465
+ const size = () => props.size ?? "sm";
1466
+ const inactive = () => props.inactive ?? false;
1467
+ const cardContent = () => <>
1468
+ <div class="vui-project-card__tooltip">
1469
+ <span>{props.name}</span>
1470
+ </div>
1471
+ <img
1472
+ class="vui-project-card__image"
1473
+ src={props.imageSrc}
1474
+ alt={props.imageAlt ?? props.name}
1475
+ />
1476
+ </>;
1477
+ const cardClasses = () => `vui-project-card vui-project-card--${size()} ${inactive() ? "vui-project-card--inactive" : ""} ${props.class ?? ""}`;
1478
+ if (props.href) {
1479
+ return <a href={props.href} target="_blank" rel="noopener noreferrer" class={cardClasses()}>
1480
+ {cardContent()}
1481
+ </a>;
1482
+ }
1483
+ return <div class={cardClasses()}>
1484
+ {cardContent()}
1485
+ </div>;
1486
+ }
1487
+
1488
+ // src/custom/page-layout/index.tsx
1489
+ import { splitProps as splitProps9 } from "solid-js";
1490
+ function PageLayout(props) {
1491
+ const [local, rest] = splitProps9(props, ["children", "class"]);
1492
+ return <div class={`vui-page ${local.class ?? ""}`} {...rest}>
1493
+ {local.children}
1494
+ </div>;
1495
+ }
1496
+ export {
1497
+ Alert,
1498
+ Avatar,
1499
+ AvatarGroup,
1500
+ Badge,
1501
+ Button,
1502
+ CalendarCard,
1503
+ Checkbox,
1504
+ CheckboxGroup,
1505
+ Chip,
1506
+ Conversation,
1507
+ ConversationBubble,
1508
+ ConversationPreview,
1509
+ Dialog,
1510
+ EventCard,
1511
+ EventListItem,
1512
+ GitHubIcon,
1513
+ Icon,
1514
+ LateralNav,
1515
+ Link,
1516
+ Logo,
1517
+ NavHeader,
1518
+ NavItem,
1519
+ NavLink,
1520
+ NavSection,
1521
+ PageLayout,
1522
+ ProfileCard,
1523
+ ProgressBar,
1524
+ ProjectCard,
1525
+ Radio,
1526
+ RadioGroup,
1527
+ Separator,
1528
+ TabSwitch,
1529
+ TextField,
1530
+ TimelineItem,
1531
+ ToggleSwitch,
1532
+ Tooltip
1533
+ };