@proyecto-viviana/ui 0.2.5 → 0.3.1
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.
- package/dist/index.js +210 -557
- package/dist/index.js.map +7 -1
- package/dist/index.ssr.js +42 -399
- package/dist/index.ssr.js.map +7 -1
- package/dist/radio/index.d.ts +27 -12
- package/dist/radio/index.d.ts.map +1 -1
- package/package.json +11 -12
- package/src/alert/index.tsx +0 -48
- package/src/assets/favicon.png +0 -0
- package/src/assets/fire.gif +0 -0
- package/src/autocomplete/index.tsx +0 -313
- package/src/avatar/index.tsx +0 -75
- package/src/badge/index.tsx +0 -43
- package/src/breadcrumbs/index.tsx +0 -207
- package/src/button/Button.tsx +0 -74
- package/src/button/index.ts +0 -2
- package/src/button/types.ts +0 -24
- package/src/calendar/DateField.tsx +0 -200
- package/src/calendar/DatePicker.tsx +0 -298
- package/src/calendar/RangeCalendar.tsx +0 -236
- package/src/calendar/TimeField.tsx +0 -196
- package/src/calendar/index.tsx +0 -223
- package/src/checkbox/index.tsx +0 -257
- package/src/color/index.tsx +0 -687
- package/src/combobox/index.tsx +0 -383
- package/src/components.css +0 -1077
- package/src/custom/calendar-card/index.tsx +0 -66
- package/src/custom/chip/index.tsx +0 -46
- package/src/custom/conversation/index.tsx +0 -105
- package/src/custom/event-card/index.tsx +0 -132
- package/src/custom/header/index.tsx +0 -33
- package/src/custom/lateral-nav/index.tsx +0 -88
- package/src/custom/logo/index.tsx +0 -58
- package/src/custom/nav-header/index.tsx +0 -42
- package/src/custom/page-layout/index.tsx +0 -29
- package/src/custom/profile-card/index.tsx +0 -64
- package/src/custom/project-card/index.tsx +0 -59
- package/src/custom/timeline-item/index.tsx +0 -105
- package/src/dialog/Dialog.tsx +0 -260
- package/src/dialog/index.tsx +0 -3
- package/src/disclosure/index.tsx +0 -307
- package/src/gridlist/index.tsx +0 -403
- package/src/icon/icons/GitHubIcon.tsx +0 -20
- package/src/icon/index.tsx +0 -48
- package/src/index.ts +0 -322
- package/src/landmark/index.tsx +0 -231
- package/src/link/index.tsx +0 -130
- package/src/listbox/index.tsx +0 -231
- package/src/menu/index.tsx +0 -297
- package/src/meter/index.tsx +0 -163
- package/src/numberfield/index.tsx +0 -482
- package/src/popover/index.tsx +0 -260
- package/src/progress-bar/index.tsx +0 -169
- package/src/radio/index.tsx +0 -173
- package/src/searchfield/index.tsx +0 -453
- package/src/select/index.tsx +0 -349
- package/src/separator/index.tsx +0 -141
- package/src/slider/index.tsx +0 -382
- package/src/styles.css +0 -450
- package/src/switch/ToggleSwitch.tsx +0 -112
- package/src/switch/index.tsx +0 -90
- package/src/table/index.tsx +0 -531
- package/src/tabs/index.tsx +0 -273
- package/src/tag-group/index.tsx +0 -240
- package/src/test-utils/index.ts +0 -32
- package/src/textfield/index.tsx +0 -211
- package/src/theme.css +0 -101
- package/src/toast/index.tsx +0 -324
- package/src/toolbar/index.tsx +0 -108
- package/src/tooltip/index.tsx +0 -197
- package/src/tree/index.tsx +0 -494
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { Show, For } from 'solid-js'
|
|
2
|
-
import { Chip } from '../chip'
|
|
3
|
-
|
|
4
|
-
export interface CalendarCardProps {
|
|
5
|
-
title: string
|
|
6
|
-
image?: string
|
|
7
|
-
tags?: string[]
|
|
8
|
-
followers?: { name: string }[]
|
|
9
|
-
followerCount?: number
|
|
10
|
-
class?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function CalendarCard(props: CalendarCardProps) {
|
|
14
|
-
const displayedFollowers = () => props.followers?.slice(0, 2) ?? []
|
|
15
|
-
const remainingCount = () => {
|
|
16
|
-
const total = props.followerCount ?? props.followers?.length ?? 0
|
|
17
|
-
const displayed = displayedFollowers().length
|
|
18
|
-
return total - displayed
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<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 ?? ''}`}>
|
|
23
|
-
<Show when={props.image}>
|
|
24
|
-
<div class="relative h-[80px] w-[80px] flex-shrink-0 overflow-hidden rounded-xl border-2 border-accent-200">
|
|
25
|
-
<img src={props.image} alt={props.title} class="h-full w-full object-cover" />
|
|
26
|
-
</div>
|
|
27
|
-
</Show>
|
|
28
|
-
<div class="relative h-full flex-1 flex-col pl-3">
|
|
29
|
-
<Show when={props.tags && props.tags.length > 0}>
|
|
30
|
-
<div class="absolute bottom-[-20px] h-[30px] w-full">
|
|
31
|
-
<div class="flex h-full w-full flex-1 justify-end gap-1">
|
|
32
|
-
<For each={props.tags}>
|
|
33
|
-
{(tag) => <Chip text={tag} variant="primary" />}
|
|
34
|
-
</For>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</Show>
|
|
38
|
-
<div class="flex h-full flex-col items-end justify-center pb-3 pr-5 pt-2">
|
|
39
|
-
<div class="flex flex-1">
|
|
40
|
-
<span class="text-lg font-semibold text-white drop-shadow-sm">
|
|
41
|
-
{props.title}
|
|
42
|
-
</span>
|
|
43
|
-
</div>
|
|
44
|
-
<Show when={displayedFollowers().length > 0}>
|
|
45
|
-
<div class="flex flex-1">
|
|
46
|
-
<span class="text-base font-normal text-primary-500">
|
|
47
|
-
seguida por{' '}
|
|
48
|
-
<For each={displayedFollowers()}>
|
|
49
|
-
{(follower, index) => (
|
|
50
|
-
<>
|
|
51
|
-
<span class="font-semibold text-accent-200">{follower.name}</span>
|
|
52
|
-
{index() < displayedFollowers().length - 1 && ', '}
|
|
53
|
-
</>
|
|
54
|
-
)}
|
|
55
|
-
</For>
|
|
56
|
-
<Show when={remainingCount() > 0}>
|
|
57
|
-
{' '}y <span class="font-semibold text-accent-200">{remainingCount()} más</span>
|
|
58
|
-
</Show>
|
|
59
|
-
</span>
|
|
60
|
-
</div>
|
|
61
|
-
</Show>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { type JSX, Show } from 'solid-js'
|
|
2
|
-
|
|
3
|
-
export type ChipVariant = 'primary' | 'secondary' | 'accent' | 'outline'
|
|
4
|
-
|
|
5
|
-
export interface ChipProps {
|
|
6
|
-
text: string
|
|
7
|
-
variant?: ChipVariant
|
|
8
|
-
onClick?: () => void
|
|
9
|
-
/**
|
|
10
|
-
* Icon to display before the text.
|
|
11
|
-
* Use a function returning JSX for SSR compatibility: `icon={() => <MyIcon />}`
|
|
12
|
-
* Or pass a simple string for text-based icons: `icon="★"`
|
|
13
|
-
*/
|
|
14
|
-
icon?: string | (() => JSX.Element)
|
|
15
|
-
class?: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const variantStyles: Record<ChipVariant, string> = {
|
|
19
|
-
primary: 'bg-primary-700 text-primary-200 shadow-primary-chip',
|
|
20
|
-
secondary: 'bg-primary-600 text-primary-100 hover:bg-primary-500',
|
|
21
|
-
accent: 'bg-accent text-white',
|
|
22
|
-
outline: 'bg-transparent border border-primary-500 text-primary-300',
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function Chip(props: ChipProps) {
|
|
26
|
-
const variant = () => props.variant ?? 'primary'
|
|
27
|
-
|
|
28
|
-
const renderIcon = () => {
|
|
29
|
-
const icon = props.icon
|
|
30
|
-
if (!icon) return null
|
|
31
|
-
if (typeof icon === 'string') return icon
|
|
32
|
-
return icon()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<button
|
|
37
|
-
class={`flex justify-center items-center h-6 w-auto rounded-full px-4 py-1 font-medium text-sm tracking-wide transition-colors ${variantStyles[variant()]} ${props.class ?? ''}`}
|
|
38
|
-
onClick={props.onClick}
|
|
39
|
-
>
|
|
40
|
-
<Show when={props.icon}>
|
|
41
|
-
<span class="mr-1.5">{renderIcon()}</span>
|
|
42
|
-
</Show>
|
|
43
|
-
<span>{props.text}</span>
|
|
44
|
-
</button>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Show, For } from 'solid-js'
|
|
2
|
-
import { Avatar } from '../../avatar'
|
|
3
|
-
|
|
4
|
-
export interface Message {
|
|
5
|
-
id: string
|
|
6
|
-
content: string
|
|
7
|
-
sender: 'user' | 'other'
|
|
8
|
-
timestamp?: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ConversationPreviewProps {
|
|
12
|
-
user: {
|
|
13
|
-
name: string
|
|
14
|
-
avatar?: string
|
|
15
|
-
online?: boolean
|
|
16
|
-
}
|
|
17
|
-
lastMessage?: string
|
|
18
|
-
unreadCount?: number
|
|
19
|
-
timestamp?: string
|
|
20
|
-
onClick?: () => void
|
|
21
|
-
class?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function ConversationPreview(props: ConversationPreviewProps) {
|
|
25
|
-
return (
|
|
26
|
-
<button
|
|
27
|
-
class={`flex w-full items-center gap-3 rounded-xl p-3 hover:bg-bg-300 transition-colors text-left ${props.class ?? ''}`}
|
|
28
|
-
onClick={props.onClick}
|
|
29
|
-
>
|
|
30
|
-
<Avatar
|
|
31
|
-
src={props.user.avatar}
|
|
32
|
-
alt={props.user.name}
|
|
33
|
-
online={props.user.online}
|
|
34
|
-
size="md"
|
|
35
|
-
/>
|
|
36
|
-
<div class="flex-1 min-w-0">
|
|
37
|
-
<div class="flex items-center justify-between">
|
|
38
|
-
<span class="font-semibold text-primary-100 truncate">{props.user.name}</span>
|
|
39
|
-
<Show when={props.timestamp}>
|
|
40
|
-
<span class="text-xs text-primary-500">{props.timestamp}</span>
|
|
41
|
-
</Show>
|
|
42
|
-
</div>
|
|
43
|
-
<Show when={props.lastMessage}>
|
|
44
|
-
<p class="text-sm text-primary-400 truncate">{props.lastMessage}</p>
|
|
45
|
-
</Show>
|
|
46
|
-
</div>
|
|
47
|
-
<Show when={props.unreadCount && props.unreadCount > 0}>
|
|
48
|
-
<span class="flex h-5 w-5 items-center justify-center rounded-full bg-accent text-xs font-bold text-white">
|
|
49
|
-
{props.unreadCount}
|
|
50
|
-
</span>
|
|
51
|
-
</Show>
|
|
52
|
-
</button>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface ConversationBubbleProps {
|
|
57
|
-
content: string
|
|
58
|
-
sender: 'user' | 'other'
|
|
59
|
-
timestamp?: string
|
|
60
|
-
class?: string
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function ConversationBubble(props: ConversationBubbleProps) {
|
|
64
|
-
const isUser = () => props.sender === 'user'
|
|
65
|
-
|
|
66
|
-
return (
|
|
67
|
-
<div class={`flex ${isUser() ? 'justify-end' : 'justify-start'} ${props.class ?? ''}`}>
|
|
68
|
-
<div
|
|
69
|
-
class={`max-w-[70%] rounded-2xl px-4 py-2 ${
|
|
70
|
-
isUser()
|
|
71
|
-
? 'bg-accent text-white rounded-br-sm'
|
|
72
|
-
: 'bg-bg-300 text-primary-100 rounded-bl-sm'
|
|
73
|
-
}`}
|
|
74
|
-
>
|
|
75
|
-
<p>{props.content}</p>
|
|
76
|
-
<Show when={props.timestamp}>
|
|
77
|
-
<span class={`text-xs ${isUser() ? 'text-white/70' : 'text-primary-500'}`}>
|
|
78
|
-
{props.timestamp}
|
|
79
|
-
</span>
|
|
80
|
-
</Show>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface ConversationProps {
|
|
87
|
-
messages: Message[]
|
|
88
|
-
class?: string
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function Conversation(props: ConversationProps) {
|
|
92
|
-
return (
|
|
93
|
-
<div class={`flex flex-col gap-2 p-4 ${props.class ?? ''}`}>
|
|
94
|
-
<For each={props.messages}>
|
|
95
|
-
{(message) => (
|
|
96
|
-
<ConversationBubble
|
|
97
|
-
content={message.content}
|
|
98
|
-
sender={message.sender}
|
|
99
|
-
timestamp={message.timestamp}
|
|
100
|
-
/>
|
|
101
|
-
)}
|
|
102
|
-
</For>
|
|
103
|
-
</div>
|
|
104
|
-
)
|
|
105
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { JSX } from 'solid-js'
|
|
2
|
-
import { Show, For } from 'solid-js'
|
|
3
|
-
import { Avatar } from '../../avatar'
|
|
4
|
-
|
|
5
|
-
export interface EventCardProps {
|
|
6
|
-
title: string
|
|
7
|
-
image?: string
|
|
8
|
-
date?: string
|
|
9
|
-
author?: string
|
|
10
|
-
authorAvatar?: string
|
|
11
|
-
attendees?: { avatar?: string; name: string }[]
|
|
12
|
-
attendeeCount?: number
|
|
13
|
-
decorationImage?: string
|
|
14
|
-
/**
|
|
15
|
-
* Actions to display below the event.
|
|
16
|
-
* Use a function returning JSX for SSR compatibility: `actions={() => <Button>...</Button>}`
|
|
17
|
-
*/
|
|
18
|
-
actions?: JSX.Element | (() => JSX.Element)
|
|
19
|
-
class?: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function EventCard(props: EventCardProps) {
|
|
23
|
-
const displayedAttendees = () => props.attendees?.slice(0, 3) ?? []
|
|
24
|
-
const remainingCount = () => {
|
|
25
|
-
const total = props.attendeeCount ?? props.attendees?.length ?? 0
|
|
26
|
-
const displayed = displayedAttendees().length
|
|
27
|
-
return total - displayed
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<div class={`relative bg-bg-200 rounded-3xl overflow-hidden ${props.class ?? ''}`}>
|
|
32
|
-
{/* Decoration image (fire gif, etc) */}
|
|
33
|
-
<Show when={props.decorationImage}>
|
|
34
|
-
<div class="absolute -top-2 -right-2 z-10 flex flex-col gap-1">
|
|
35
|
-
<img src={props.decorationImage} alt="" class="w-8 h-8 object-contain" />
|
|
36
|
-
<img src={props.decorationImage} alt="" class="w-6 h-6 object-contain ml-2" />
|
|
37
|
-
<img src={props.decorationImage} alt="" class="w-5 h-5 object-contain" />
|
|
38
|
-
</div>
|
|
39
|
-
</Show>
|
|
40
|
-
|
|
41
|
-
<Show when={props.image}>
|
|
42
|
-
<div class="relative h-32 w-full">
|
|
43
|
-
<img
|
|
44
|
-
src={props.image}
|
|
45
|
-
alt={props.title}
|
|
46
|
-
class="w-full h-full object-cover"
|
|
47
|
-
/>
|
|
48
|
-
<div class="absolute inset-0 bg-gradient-to-t from-bg-200 via-transparent to-transparent" />
|
|
49
|
-
</div>
|
|
50
|
-
</Show>
|
|
51
|
-
|
|
52
|
-
<div class="p-4 pt-2">
|
|
53
|
-
<h3 class="font-bold text-xl leading-tight bg-gradient-to-r from-accent to-accent-300 bg-clip-text text-transparent">
|
|
54
|
-
{props.title}
|
|
55
|
-
</h3>
|
|
56
|
-
|
|
57
|
-
<Show when={props.date || props.author}>
|
|
58
|
-
<div class="flex items-center gap-4 mt-3 text-sm text-primary-300">
|
|
59
|
-
<Show when={props.author}>
|
|
60
|
-
<div class="flex items-center gap-1.5">
|
|
61
|
-
<span class="text-accent">@</span>
|
|
62
|
-
<span>{props.author}</span>
|
|
63
|
-
</div>
|
|
64
|
-
</Show>
|
|
65
|
-
<Show when={props.date}>
|
|
66
|
-
<div class="flex items-center gap-1.5">
|
|
67
|
-
<span class="text-accent">⏱</span>
|
|
68
|
-
<span>{props.date}</span>
|
|
69
|
-
</div>
|
|
70
|
-
</Show>
|
|
71
|
-
</div>
|
|
72
|
-
</Show>
|
|
73
|
-
|
|
74
|
-
<Show when={displayedAttendees().length > 0}>
|
|
75
|
-
<div class="flex items-center mt-3">
|
|
76
|
-
<div class="flex -space-x-2">
|
|
77
|
-
<For each={displayedAttendees()}>
|
|
78
|
-
{(attendee) => (
|
|
79
|
-
<Avatar src={attendee.avatar} alt={attendee.name} size="sm" />
|
|
80
|
-
)}
|
|
81
|
-
</For>
|
|
82
|
-
</div>
|
|
83
|
-
<Show when={remainingCount() > 0}>
|
|
84
|
-
<span class="ml-2 text-sm text-primary-300">
|
|
85
|
-
+{remainingCount()} más
|
|
86
|
-
</span>
|
|
87
|
-
</Show>
|
|
88
|
-
</div>
|
|
89
|
-
</Show>
|
|
90
|
-
|
|
91
|
-
<Show when={props.actions}>
|
|
92
|
-
<div class="mt-4 flex gap-2">
|
|
93
|
-
{typeof props.actions === 'function' ? props.actions() : props.actions}
|
|
94
|
-
</div>
|
|
95
|
-
</Show>
|
|
96
|
-
</div>
|
|
97
|
-
</div>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export interface EventListItemProps {
|
|
102
|
-
title: string
|
|
103
|
-
image?: string
|
|
104
|
-
subtitle?: string
|
|
105
|
-
onClick?: () => void
|
|
106
|
-
class?: string
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function EventListItem(props: EventListItemProps) {
|
|
110
|
-
return (
|
|
111
|
-
<button
|
|
112
|
-
class={`flex items-center gap-3 w-full p-2 rounded-lg hover:bg-bg-300 transition-colors text-left ${props.class ?? ''}`}
|
|
113
|
-
onClick={props.onClick}
|
|
114
|
-
>
|
|
115
|
-
<Show when={props.image}>
|
|
116
|
-
<div class="w-12 h-12 rounded-lg overflow-hidden flex-shrink-0">
|
|
117
|
-
<img
|
|
118
|
-
src={props.image}
|
|
119
|
-
alt={props.title}
|
|
120
|
-
class="w-full h-full object-cover"
|
|
121
|
-
/>
|
|
122
|
-
</div>
|
|
123
|
-
</Show>
|
|
124
|
-
<div class="flex-1 min-w-0">
|
|
125
|
-
<h4 class="font-medium text-primary-100 truncate">{props.title}</h4>
|
|
126
|
-
<Show when={props.subtitle}>
|
|
127
|
-
<p class="text-sm text-primary-300 truncate">{props.subtitle}</p>
|
|
128
|
-
</Show>
|
|
129
|
-
</div>
|
|
130
|
-
</button>
|
|
131
|
-
)
|
|
132
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { JSX } from 'solid-js'
|
|
2
|
-
import { Logo, type LogoProps } from '../logo'
|
|
3
|
-
|
|
4
|
-
export interface HeaderProps {
|
|
5
|
-
/** Image element to show to the left of the text logo */
|
|
6
|
-
logoImage?: JSX.Element
|
|
7
|
-
/** Props to pass to the Logo component (firstWord, secondWord, size, inverted). Pass null to hide the text logo. */
|
|
8
|
-
logoProps?: LogoProps | null
|
|
9
|
-
/** Custom logo element - replaces the default Logo component entirely */
|
|
10
|
-
logo?: JSX.Element
|
|
11
|
-
/** Navigation items to display on the right side */
|
|
12
|
-
children?: JSX.Element
|
|
13
|
-
/** Additional CSS classes */
|
|
14
|
-
class?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function Header(props: HeaderProps) {
|
|
18
|
-
const showTextLogo = () => props.logo !== undefined || props.logoProps !== null
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<header class={`vui-header ${props.class ?? ''}`}>
|
|
22
|
-
<div class="vui-header__container">
|
|
23
|
-
<div class="flex items-center gap-3">
|
|
24
|
-
{props.logoImage}
|
|
25
|
-
{showTextLogo() && (props.logo ?? <Logo size="lg" {...(props.logoProps ?? {})} />)}
|
|
26
|
-
</div>
|
|
27
|
-
<nav class="vui-header__nav">
|
|
28
|
-
{props.children}
|
|
29
|
-
</nav>
|
|
30
|
-
</div>
|
|
31
|
-
</header>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { JSX } from 'solid-js'
|
|
2
|
-
import { Show, For } from 'solid-js'
|
|
3
|
-
|
|
4
|
-
export interface NavItemProps {
|
|
5
|
-
title: string
|
|
6
|
-
children?: JSX.Element
|
|
7
|
-
class?: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function NavItem(props: NavItemProps) {
|
|
11
|
-
return (
|
|
12
|
-
<li class={`flex items-center ${props.class ?? ''}`}>
|
|
13
|
-
<span class="text-lg font-bold text-primary-200">{props.title}</span>
|
|
14
|
-
{props.children}
|
|
15
|
-
</li>
|
|
16
|
-
)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface NavLinkProps {
|
|
20
|
-
href: string
|
|
21
|
-
children: JSX.Element
|
|
22
|
-
active?: boolean
|
|
23
|
-
class?: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function NavLink(props: NavLinkProps) {
|
|
27
|
-
const activeStyles = 'font-medium text-primary-300 underline underline-offset-4'
|
|
28
|
-
const inactiveStyles = 'font-normal text-gray-200 underline-offset-4 hover:text-gray-100 hover:underline'
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<a
|
|
32
|
-
href={props.href}
|
|
33
|
-
class={`${props.active ? activeStyles : inactiveStyles} ${props.class ?? ''}`}
|
|
34
|
-
>
|
|
35
|
-
{props.children}
|
|
36
|
-
</a>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface NavSectionProps {
|
|
41
|
-
title: string
|
|
42
|
-
links?: { href: string; label: string; active?: boolean }[]
|
|
43
|
-
children?: JSX.Element
|
|
44
|
-
class?: string
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function NavSection(props: NavSectionProps) {
|
|
48
|
-
return (
|
|
49
|
-
<div class={props.class ?? ''}>
|
|
50
|
-
<NavItem title={props.title} />
|
|
51
|
-
<div class="flex h-full">
|
|
52
|
-
<div class="h-5 w-1 bg-accent-300" />
|
|
53
|
-
<ul class="flex h-full flex-1 flex-col gap-1 pl-4">
|
|
54
|
-
<Show when={props.links}>
|
|
55
|
-
<For each={props.links}>
|
|
56
|
-
{(link) => (
|
|
57
|
-
<li>
|
|
58
|
-
<NavLink href={link.href} active={link.active}>
|
|
59
|
-
{link.label}
|
|
60
|
-
</NavLink>
|
|
61
|
-
</li>
|
|
62
|
-
)}
|
|
63
|
-
</For>
|
|
64
|
-
</Show>
|
|
65
|
-
{props.children}
|
|
66
|
-
</ul>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface LateralNavProps {
|
|
73
|
-
transparent?: boolean
|
|
74
|
-
children?: JSX.Element
|
|
75
|
-
class?: string
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function LateralNav(props: LateralNavProps) {
|
|
79
|
-
const bgColor = () => (props.transparent ? '' : 'bg-bg-200')
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<div
|
|
83
|
-
class={`hidden w-[300px] md:block ${bgColor()} m-0 border-r border-primary-600 p-3 ${props.class ?? ''}`}
|
|
84
|
-
>
|
|
85
|
-
{props.children}
|
|
86
|
-
</div>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { JSX } from "solid-js";
|
|
2
|
-
|
|
3
|
-
export type LogoSize = "sm" | "md" | "lg" | "xl";
|
|
4
|
-
|
|
5
|
-
export interface LogoProps {
|
|
6
|
-
/** First word (light weight, muted color) */
|
|
7
|
-
firstWord?: string;
|
|
8
|
-
/** Second word (bold, with 3D effect) */
|
|
9
|
-
secondWord?: string;
|
|
10
|
-
/** Size variant of the logo */
|
|
11
|
-
size?: LogoSize;
|
|
12
|
-
/** Invert the styles (first word gets 3D effect, second word is muted) */
|
|
13
|
-
inverted?: boolean;
|
|
14
|
-
/** Additional CSS classes */
|
|
15
|
-
class?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Two-word logo with retro synthwave 3D effect.
|
|
20
|
-
* First word is light/muted, second word has bold styling with pink 3D shadow.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* // Default usage
|
|
24
|
-
* <Logo />
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* // Custom text
|
|
28
|
-
* <Logo firstWord="My" secondWord="Brand" size="lg" />
|
|
29
|
-
*/
|
|
30
|
-
export function Logo(props: LogoProps): JSX.Element {
|
|
31
|
-
const sizeClass = () => {
|
|
32
|
-
switch (props.size) {
|
|
33
|
-
case "sm":
|
|
34
|
-
return "vui-logo--sm";
|
|
35
|
-
case "lg":
|
|
36
|
-
return "vui-logo--lg";
|
|
37
|
-
case "xl":
|
|
38
|
-
return "vui-logo--xl";
|
|
39
|
-
case "md":
|
|
40
|
-
default:
|
|
41
|
-
return "vui-logo--md";
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const firstWord = () => props.firstWord ?? "Proyecto";
|
|
46
|
-
const secondWord = () => props.secondWord ?? "Viviana";
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<span class={`vui-logo ${sizeClass()} ${props.inverted ? "vui-logo--inverted" : ""} ${props.class ?? ""}`}>
|
|
50
|
-
<span class="vui-logo__first" data-text={props.inverted ? firstWord() : undefined}>
|
|
51
|
-
{firstWord()}
|
|
52
|
-
</span>
|
|
53
|
-
<span class="vui-logo__second" data-text={props.inverted ? undefined : secondWord()}>
|
|
54
|
-
{secondWord()}
|
|
55
|
-
</span>
|
|
56
|
-
</span>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { JSX } from 'solid-js'
|
|
2
|
-
import { Show } from 'solid-js'
|
|
3
|
-
|
|
4
|
-
export interface NavHeaderProps {
|
|
5
|
-
logo?: string
|
|
6
|
-
logoAlt?: string
|
|
7
|
-
logoText?: string
|
|
8
|
-
children?: JSX.Element
|
|
9
|
-
menuIcon?: JSX.Element
|
|
10
|
-
onMenuClick?: () => void
|
|
11
|
-
class?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function NavHeader(props: NavHeaderProps) {
|
|
15
|
-
return (
|
|
16
|
-
<nav class={`flex items-center bg-bg-400 h-[70px] border-b-4 border-accent-500 ${props.class ?? ''}`}>
|
|
17
|
-
<div class="pl-1 md:pl-8 flex items-center">
|
|
18
|
-
<Show when={props.logo} fallback={
|
|
19
|
-
<Show when={props.logoText}>
|
|
20
|
-
<span class="text-[34px] font-light text-primary-700 flex items-center">
|
|
21
|
-
{props.logoText}
|
|
22
|
-
</span>
|
|
23
|
-
</Show>
|
|
24
|
-
}>
|
|
25
|
-
<img src={props.logo} alt={props.logoAlt ?? 'Logo'} class="h-[42px] w-auto" />
|
|
26
|
-
</Show>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<div class="flex-1 flex justify-end items-center pr-1 md:pr-8">
|
|
30
|
-
{props.children}
|
|
31
|
-
<Show when={props.menuIcon}>
|
|
32
|
-
<button
|
|
33
|
-
class="md:hidden flex items-center justify-center"
|
|
34
|
-
onClick={props.onMenuClick}
|
|
35
|
-
>
|
|
36
|
-
{props.menuIcon}
|
|
37
|
-
</button>
|
|
38
|
-
</Show>
|
|
39
|
-
</div>
|
|
40
|
-
</nav>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { JSX, splitProps } from 'solid-js'
|
|
2
|
-
|
|
3
|
-
export interface PageLayoutProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
4
|
-
/** Content of the page */
|
|
5
|
-
children: JSX.Element
|
|
6
|
-
/** Add padding-top to account for fixed header (use for non-landing pages) */
|
|
7
|
-
withHeader?: boolean
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* PageLayout provides consistent page structure with proper background and font styling.
|
|
12
|
-
* Use this as the root wrapper for all pages.
|
|
13
|
-
*/
|
|
14
|
-
export function PageLayout(props: PageLayoutProps) {
|
|
15
|
-
const [local, rest] = splitProps(props, ['class', 'withHeader'])
|
|
16
|
-
|
|
17
|
-
const classes = () => {
|
|
18
|
-
const base = 'vui-page'
|
|
19
|
-
const header = local.withHeader ? 'vui-page--with-header' : ''
|
|
20
|
-
const custom = local.class ?? ''
|
|
21
|
-
return [base, header, custom].filter(Boolean).join(' ')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<div class={classes()} {...rest}>
|
|
26
|
-
{props.children}
|
|
27
|
-
</div>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { JSX } from 'solid-js'
|
|
2
|
-
import { Show } from 'solid-js'
|
|
3
|
-
import { Avatar } from '../../avatar'
|
|
4
|
-
|
|
5
|
-
export interface ProfileCardProps {
|
|
6
|
-
username: string
|
|
7
|
-
avatar?: string
|
|
8
|
-
bio?: string
|
|
9
|
-
followers?: number
|
|
10
|
-
following?: number
|
|
11
|
-
/**
|
|
12
|
-
* Actions to display below the profile.
|
|
13
|
-
* Use a function returning JSX for SSR compatibility: `actions={() => <Button>...</Button>}`
|
|
14
|
-
*/
|
|
15
|
-
actions?: JSX.Element | (() => JSX.Element)
|
|
16
|
-
class?: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function ProfileCard(props: ProfileCardProps) {
|
|
20
|
-
const formatNumber = (num: number) => {
|
|
21
|
-
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`
|
|
22
|
-
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`
|
|
23
|
-
return num.toString()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div class={`bg-bg-200 rounded-xl p-4 ${props.class ?? ''}`}>
|
|
28
|
-
<div class="flex items-start gap-4">
|
|
29
|
-
<Avatar src={props.avatar} alt={props.username} size="lg" />
|
|
30
|
-
<div class="flex-1 min-w-0">
|
|
31
|
-
<h3 class="font-semibold text-primary-100 text-lg truncate">
|
|
32
|
-
{props.username}
|
|
33
|
-
</h3>
|
|
34
|
-
<Show when={props.bio}>
|
|
35
|
-
<p class="text-primary-300 text-sm mt-1 line-clamp-2">{props.bio}</p>
|
|
36
|
-
</Show>
|
|
37
|
-
<div class="flex gap-4 mt-2 text-sm">
|
|
38
|
-
<Show when={props.followers !== undefined}>
|
|
39
|
-
<span class="text-primary-300">
|
|
40
|
-
<span class="font-semibold text-primary-100">
|
|
41
|
-
{formatNumber(props.followers!)}
|
|
42
|
-
</span>{' '}
|
|
43
|
-
seguidores
|
|
44
|
-
</span>
|
|
45
|
-
</Show>
|
|
46
|
-
<Show when={props.following !== undefined}>
|
|
47
|
-
<span class="text-primary-300">
|
|
48
|
-
<span class="font-semibold text-primary-100">
|
|
49
|
-
{formatNumber(props.following!)}
|
|
50
|
-
</span>{' '}
|
|
51
|
-
siguiendo
|
|
52
|
-
</span>
|
|
53
|
-
</Show>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
<Show when={props.actions}>
|
|
58
|
-
<div class="mt-4 flex gap-2">
|
|
59
|
-
{typeof props.actions === 'function' ? props.actions() : props.actions}
|
|
60
|
-
</div>
|
|
61
|
-
</Show>
|
|
62
|
-
</div>
|
|
63
|
-
)
|
|
64
|
-
}
|