@proyecto-viviana/ui 0.4.1 → 0.5.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.
- package/dist/styles.css +0 -3
- package/dist/viviana-tokens.css +240 -171
- package/package.json +3 -57
- package/src/viviana-tokens.css +240 -171
- package/dist/CalendarCard.d.ts +0 -2
- package/dist/CalendarCard.d.ts.map +0 -1
- package/dist/CalendarCard.js +0 -96
- package/dist/CalendarCard.js.map +0 -1
- package/dist/CalendarCard.jsx +0 -50
- package/dist/CalendarCard.jsx.map +0 -1
- package/dist/Chip.d.ts +0 -2
- package/dist/Chip.d.ts.map +0 -1
- package/dist/Chip.js +0 -2
- package/dist/Chip.jsx +0 -2
- package/dist/Conversation.d.ts +0 -2
- package/dist/Conversation.d.ts.map +0 -1
- package/dist/Conversation.js +0 -176
- package/dist/Conversation.js.map +0 -1
- package/dist/Conversation.jsx +0 -106
- package/dist/Conversation.jsx.map +0 -1
- package/dist/EventCard.d.ts +0 -2
- package/dist/EventCard.d.ts.map +0 -1
- package/dist/EventCard.js +0 -171
- package/dist/EventCard.js.map +0 -1
- package/dist/EventCard.jsx +0 -94
- package/dist/EventCard.jsx.map +0 -1
- package/dist/Logo.d.ts +0 -2
- package/dist/Logo.d.ts.map +0 -1
- package/dist/Logo.js +0 -116
- package/dist/Logo.js.map +0 -1
- package/dist/Logo.jsx +0 -101
- package/dist/Logo.jsx.map +0 -1
- package/dist/PageLayout.d.ts +0 -2
- package/dist/PageLayout.d.ts.map +0 -1
- package/dist/PageLayout.js +0 -31
- package/dist/PageLayout.js.map +0 -1
- package/dist/PageLayout.jsx +0 -23
- package/dist/PageLayout.jsx.map +0 -1
- package/dist/ProfileCard.d.ts +0 -2
- package/dist/ProfileCard.d.ts.map +0 -1
- package/dist/ProfileCard.js +0 -77
- package/dist/ProfileCard.js.map +0 -1
- package/dist/ProfileCard.jsx +0 -52
- package/dist/ProfileCard.jsx.map +0 -1
- package/dist/ProjectCard.d.ts +0 -2
- package/dist/ProjectCard.d.ts.map +0 -1
- package/dist/ProjectCard.js +0 -162
- package/dist/ProjectCard.js.map +0 -1
- package/dist/ProjectCard.jsx +0 -131
- package/dist/ProjectCard.jsx.map +0 -1
- package/dist/TimelineItem.d.ts +0 -2
- package/dist/TimelineItem.d.ts.map +0 -1
- package/dist/TimelineItem.js +0 -137
- package/dist/TimelineItem.js.map +0 -1
- package/dist/TimelineItem.jsx +0 -70
- package/dist/TimelineItem.jsx.map +0 -1
- package/dist/chip2.js +0 -82
- package/dist/chip2.js.map +0 -1
- package/dist/chip2.jsx +0 -68
- package/dist/chip2.jsx.map +0 -1
- package/dist/custom/calendar-card/index.d.ts +0 -12
- package/dist/custom/calendar-card/index.d.ts.map +0 -1
- package/dist/custom/chip/index.d.ts +0 -16
- package/dist/custom/chip/index.d.ts.map +0 -1
- package/dist/custom/conversation/index.d.ts +0 -32
- package/dist/custom/conversation/index.d.ts.map +0 -1
- package/dist/custom/event-card/index.d.ts +0 -30
- package/dist/custom/event-card/index.d.ts.map +0 -1
- package/dist/custom/header/index.d.ts +0 -12
- package/dist/custom/header/index.d.ts.map +0 -1
- package/dist/custom/lateral-nav/index.d.ts +0 -32
- package/dist/custom/lateral-nav/index.d.ts.map +0 -1
- package/dist/custom/logo/index.d.ts +0 -11
- package/dist/custom/logo/index.d.ts.map +0 -1
- package/dist/custom/nav-header/index.d.ts +0 -13
- package/dist/custom/nav-header/index.d.ts.map +0 -1
- package/dist/custom/page-layout/index.d.ts +0 -7
- package/dist/custom/page-layout/index.d.ts.map +0 -1
- package/dist/custom/profile-card/index.d.ts +0 -16
- package/dist/custom/profile-card/index.d.ts.map +0 -1
- package/dist/custom/project-card/index.d.ts +0 -13
- package/dist/custom/project-card/index.d.ts.map +0 -1
- package/dist/custom/timeline-item/index.d.ts +0 -28
- package/dist/custom/timeline-item/index.d.ts.map +0 -1
- package/src/CalendarCard.ts +0 -1
- package/src/Chip.ts +0 -1
- package/src/Conversation.ts +0 -1
- package/src/EventCard.ts +0 -1
- package/src/Logo.ts +0 -1
- package/src/PageLayout.ts +0 -1
- package/src/ProfileCard.ts +0 -1
- package/src/ProjectCard.ts +0 -1
- package/src/TimelineItem.ts +0 -1
- package/src/custom/calendar-card/index.tsx +0 -124
- package/src/custom/chip/index.tsx +0 -82
- package/src/custom/conversation/index.tsx +0 -216
- package/src/custom/event-card/index.tsx +0 -253
- package/src/custom/header/index.tsx +0 -64
- package/src/custom/lateral-nav/index.tsx +0 -143
- package/src/custom/logo/index.tsx +0 -60
- package/src/custom/nav-header/index.tsx +0 -106
- package/src/custom/page-layout/index.tsx +0 -33
- package/src/custom/profile-card/index.tsx +0 -123
- package/src/custom/project-card/index.tsx +0 -76
- package/src/custom/timeline-item/index.tsx +0 -148
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import { Show, For } from "solid-js";
|
|
2
|
-
import { Avatar } from "@proyecto-viviana/solid-spectrum";
|
|
3
|
-
import { Button as HeadlessButton } from "@proyecto-viviana/solidaria-components";
|
|
4
|
-
// Styled via the S2 style() macro through viviana's own seam (../../style →
|
|
5
|
-
// src/style.ts). A conversation preview row, message bubbles, and a thread
|
|
6
|
-
// list, on the S2 spacing/radius ramps in Silapse colors.
|
|
7
|
-
import { style } from "../../style" with { type: "macro" };
|
|
8
|
-
|
|
9
|
-
export interface Message {
|
|
10
|
-
id: string;
|
|
11
|
-
content: string;
|
|
12
|
-
sender: "user" | "other";
|
|
13
|
-
timestamp?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ConversationPreviewProps {
|
|
17
|
-
user: {
|
|
18
|
-
name: string;
|
|
19
|
-
avatar?: string;
|
|
20
|
-
online?: boolean;
|
|
21
|
-
};
|
|
22
|
-
lastMessage?: string;
|
|
23
|
-
unreadCount?: number;
|
|
24
|
-
timestamp?: string;
|
|
25
|
-
onClick?: () => void;
|
|
26
|
-
class?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const preview = style({
|
|
30
|
-
display: "flex",
|
|
31
|
-
alignItems: "center",
|
|
32
|
-
gap: 12,
|
|
33
|
-
width: "full",
|
|
34
|
-
padding: 12,
|
|
35
|
-
borderRadius: "lg",
|
|
36
|
-
textAlign: "start",
|
|
37
|
-
cursor: "pointer",
|
|
38
|
-
backgroundColor: "transparent",
|
|
39
|
-
borderStyle: "none",
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const previewBody = style({
|
|
43
|
-
display: "flex",
|
|
44
|
-
flexDirection: "column",
|
|
45
|
-
gap: 2,
|
|
46
|
-
flexGrow: 1,
|
|
47
|
-
minWidth: 0,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const previewTop = style({
|
|
51
|
-
display: "flex",
|
|
52
|
-
alignItems: "center",
|
|
53
|
-
justifyContent: "space-between",
|
|
54
|
-
gap: 8,
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const previewName = style({
|
|
58
|
-
font: "ui",
|
|
59
|
-
fontWeight: "bold",
|
|
60
|
-
color: "[var(--color-primary-100)]",
|
|
61
|
-
overflow: "hidden",
|
|
62
|
-
textOverflow: "ellipsis",
|
|
63
|
-
whiteSpace: "nowrap",
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const previewTime = style({
|
|
67
|
-
font: "detail-sm",
|
|
68
|
-
// `--color-text-muted` renders ~1.7:1 light / ~2.4:1 dark on the `bg-200`
|
|
69
|
-
// panel (fails AA both modes); the flipping `--color-text` clears it. Muting
|
|
70
|
-
// is carried by the smaller `detail-sm` size, not a sub-AA color.
|
|
71
|
-
color: "[var(--color-text)]",
|
|
72
|
-
flexShrink: 0,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const previewMessage = style({
|
|
76
|
-
font: "ui-sm",
|
|
77
|
-
// `--color-text-secondary` renders 3.84:1 on the light `bg-200` panel (fails
|
|
78
|
-
// the 4.5 small-text floor); the flipping `--color-text` clears both themes.
|
|
79
|
-
color: "[var(--color-text)]",
|
|
80
|
-
overflow: "hidden",
|
|
81
|
-
textOverflow: "ellipsis",
|
|
82
|
-
whiteSpace: "nowrap",
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const unread = style({
|
|
86
|
-
display: "flex",
|
|
87
|
-
alignItems: "center",
|
|
88
|
-
justifyContent: "center",
|
|
89
|
-
flexShrink: 0,
|
|
90
|
-
width: 20,
|
|
91
|
-
height: 20,
|
|
92
|
-
borderRadius: "full",
|
|
93
|
-
font: "detail-sm",
|
|
94
|
-
fontWeight: "bold",
|
|
95
|
-
backgroundColor: "[var(--color-accent)]",
|
|
96
|
-
// Text on the non-flipping pink `--color-accent` fill: the near-white
|
|
97
|
-
// `--color-bg-400` gives only 2.74:1 in light mode (fail). `--color-grey-900`
|
|
98
|
-
// (the darkest token) reaches ~5.2:1 light / ~6.1:1 dark — the Chip accent-fill
|
|
99
|
-
// resolution.
|
|
100
|
-
color: "[var(--color-grey-900)]",
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
export function ConversationPreview(props: ConversationPreviewProps) {
|
|
104
|
-
return (
|
|
105
|
-
<HeadlessButton
|
|
106
|
-
type="button"
|
|
107
|
-
class={`${preview} ${props.class ?? ""}`}
|
|
108
|
-
onPress={() => props.onClick?.()}
|
|
109
|
-
>
|
|
110
|
-
<Avatar src={props.user.avatar} alt={props.user.name} online={props.user.online} size="md" />
|
|
111
|
-
<div class={previewBody}>
|
|
112
|
-
<div class={previewTop}>
|
|
113
|
-
<span class={previewName}>{props.user.name}</span>
|
|
114
|
-
<Show when={props.timestamp}>
|
|
115
|
-
<span class={previewTime}>{props.timestamp}</span>
|
|
116
|
-
</Show>
|
|
117
|
-
</div>
|
|
118
|
-
<Show when={props.lastMessage}>
|
|
119
|
-
<p class={previewMessage}>{props.lastMessage}</p>
|
|
120
|
-
</Show>
|
|
121
|
-
</div>
|
|
122
|
-
<Show when={props.unreadCount && props.unreadCount > 0}>
|
|
123
|
-
<span class={unread}>{props.unreadCount}</span>
|
|
124
|
-
</Show>
|
|
125
|
-
</HeadlessButton>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export interface ConversationBubbleProps {
|
|
130
|
-
content: string;
|
|
131
|
-
sender: "user" | "other";
|
|
132
|
-
timestamp?: string;
|
|
133
|
-
class?: string;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const bubbleRow = style<{ isUser: boolean }>({
|
|
137
|
-
display: "flex",
|
|
138
|
-
justifyContent: { default: "start", isUser: "end" },
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
const bubble = style<{ isUser: boolean }>({
|
|
142
|
-
display: "flex",
|
|
143
|
-
flexDirection: "column",
|
|
144
|
-
gap: 2,
|
|
145
|
-
maxWidth: "[70%]",
|
|
146
|
-
paddingX: 16,
|
|
147
|
-
paddingY: 8,
|
|
148
|
-
borderRadius: "lg",
|
|
149
|
-
backgroundColor: {
|
|
150
|
-
default: "[var(--color-bg-300)]",
|
|
151
|
-
isUser: "[var(--color-accent)]",
|
|
152
|
-
},
|
|
153
|
-
color: {
|
|
154
|
-
// `other` bubble: `--color-primary-100` on the neutral `bg-300` fill (AA both
|
|
155
|
-
// themes). `user` bubble text on the pink `--color-accent` fill: the near-white
|
|
156
|
-
// `--color-bg-400` gives only 2.74:1 light (fail) → `--color-grey-900` (~5.2:1
|
|
157
|
-
// light / ~6.1:1 dark), the Chip accent-fill resolution.
|
|
158
|
-
default: "[var(--color-primary-100)]",
|
|
159
|
-
isUser: "[var(--color-grey-900)]",
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const bubbleTime = style<{ isUser: boolean }>({
|
|
164
|
-
font: "detail-sm",
|
|
165
|
-
color: {
|
|
166
|
-
// `other` timestamp on `bg-300`: `--color-text-muted` fails AA both modes
|
|
167
|
-
// (~2.2:1 light / ~2.8:1 dark) → the flipping `--color-text`. `user` timestamp
|
|
168
|
-
// on the `--color-accent` fill: `--color-bg-300` gives 2.42:1 light (fail) →
|
|
169
|
-
// `--color-grey-900` (matching the bubble body).
|
|
170
|
-
default: "[var(--color-text)]",
|
|
171
|
-
isUser: "[var(--color-grey-900)]",
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
export function ConversationBubble(props: ConversationBubbleProps) {
|
|
176
|
-
const isUser = () => props.sender === "user";
|
|
177
|
-
|
|
178
|
-
return (
|
|
179
|
-
<div class={`${bubbleRow({ isUser: isUser() })} ${props.class ?? ""}`}>
|
|
180
|
-
<div class={bubble({ isUser: isUser() })}>
|
|
181
|
-
<p>{props.content}</p>
|
|
182
|
-
<Show when={props.timestamp}>
|
|
183
|
-
<span class={bubbleTime({ isUser: isUser() })}>{props.timestamp}</span>
|
|
184
|
-
</Show>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export interface ConversationProps {
|
|
191
|
-
messages: Message[];
|
|
192
|
-
class?: string;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const thread = style({
|
|
196
|
-
display: "flex",
|
|
197
|
-
flexDirection: "column",
|
|
198
|
-
gap: 8,
|
|
199
|
-
padding: 16,
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
export function Conversation(props: ConversationProps) {
|
|
203
|
-
return (
|
|
204
|
-
<div class={`${thread} ${props.class ?? ""}`}>
|
|
205
|
-
<For each={props.messages}>
|
|
206
|
-
{(message) => (
|
|
207
|
-
<ConversationBubble
|
|
208
|
-
content={message.content}
|
|
209
|
-
sender={message.sender}
|
|
210
|
-
timestamp={message.timestamp}
|
|
211
|
-
/>
|
|
212
|
-
)}
|
|
213
|
-
</For>
|
|
214
|
-
</div>
|
|
215
|
-
);
|
|
216
|
-
}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import type { JSX } from "solid-js";
|
|
2
|
-
import { Show, For } from "solid-js";
|
|
3
|
-
import { Avatar } from "@proyecto-viviana/solid-spectrum";
|
|
4
|
-
import { Button as HeadlessButton } from "@proyecto-viviana/solidaria-components";
|
|
5
|
-
// Styled via the S2 style() macro through viviana's own seam (../../style →
|
|
6
|
-
// src/style.ts). An event summary card (S2 Card shape: preview + content +
|
|
7
|
-
// footer) plus a compact list-item variant, on the S2 ramps in Silapse colors.
|
|
8
|
-
import { style } from "../../style" with { type: "macro" };
|
|
9
|
-
|
|
10
|
-
export interface EventCardProps {
|
|
11
|
-
title: string;
|
|
12
|
-
image?: string;
|
|
13
|
-
date?: string;
|
|
14
|
-
author?: string;
|
|
15
|
-
authorAvatar?: string;
|
|
16
|
-
attendees?: { avatar?: string; name: string }[];
|
|
17
|
-
attendeeCount?: number;
|
|
18
|
-
decorationImage?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Actions to display below the event.
|
|
21
|
-
* Use a function returning JSX for SSR compatibility: `actions={() => <Button>...</Button>}`
|
|
22
|
-
*/
|
|
23
|
-
actions?: JSX.Element | (() => JSX.Element);
|
|
24
|
-
class?: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const card = style({
|
|
28
|
-
position: "relative",
|
|
29
|
-
display: "flex",
|
|
30
|
-
flexDirection: "column",
|
|
31
|
-
overflow: "hidden",
|
|
32
|
-
borderRadius: "xl",
|
|
33
|
-
backgroundColor: "[var(--color-bg-200)]",
|
|
34
|
-
boxShadow: "elevated",
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
const decoration = style({
|
|
38
|
-
position: "absolute",
|
|
39
|
-
top: 8,
|
|
40
|
-
insetEnd: 8,
|
|
41
|
-
width: 32,
|
|
42
|
-
height: 32,
|
|
43
|
-
objectFit: "contain",
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const preview = style({
|
|
47
|
-
width: "full",
|
|
48
|
-
aspectRatio: "16/9",
|
|
49
|
-
objectFit: "cover",
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const body = style({
|
|
53
|
-
display: "flex",
|
|
54
|
-
flexDirection: "column",
|
|
55
|
-
gap: 12,
|
|
56
|
-
padding: 16,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const title = style({
|
|
60
|
-
font: "heading",
|
|
61
|
-
// WCAG AA fix: the title sits on the `--color-bg-200` card. `--color-accent`
|
|
62
|
-
// (#df5c9a) is a fixed pink in BOTH themes, so it does not flip against a card
|
|
63
|
-
// that is dark-grey in dark mode and light-blue in light mode — it read 4.48:1
|
|
64
|
-
// dark / 1.89:1 light. `font: heading` renders 22px / weight 800, which is WCAG
|
|
65
|
-
// large text via the BOLD path (≥18.66px && ≥700), NOT because it is ≥24px — so
|
|
66
|
-
// the 3:1 large-text floor applies. `--color-accent-500` is the smallest ramp
|
|
67
|
-
// step that flips with the theme (bright pink dark / deep magenta light) AND
|
|
68
|
-
// clears 3:1 both ways (3.87:1 dark / 4.91:1 light), keeping the brand accent.
|
|
69
|
-
color: "[var(--color-accent-500)]",
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const meta = style({
|
|
73
|
-
display: "flex",
|
|
74
|
-
alignItems: "center",
|
|
75
|
-
gap: 16,
|
|
76
|
-
font: "ui-sm",
|
|
77
|
-
// WCAG AA fix: the meta text is small (`ui-sm`), so the 4.5:1 normal-text
|
|
78
|
-
// floor applies. `--color-text-secondary` read only 3.84:1 on the light
|
|
79
|
-
// `--color-bg-200` card; `--color-text` flips and clears 4.5:1 both ways
|
|
80
|
-
// (15.33:1 dark / 7.53:1 light). Secondary de-emphasis rides on the smaller
|
|
81
|
-
// `ui-sm` size rather than a sub-AA color.
|
|
82
|
-
color: "[var(--color-text)]",
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const metaItem = style({
|
|
86
|
-
display: "flex",
|
|
87
|
-
alignItems: "center",
|
|
88
|
-
gap: 4,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const metaIcon = style({
|
|
92
|
-
// WCAG AA fix: the meta glyphs (@ / ⏱) are small (inherit `ui-sm`), so the
|
|
93
|
-
// 4.5:1 floor applies. `--color-accent` fails on the card (4.48:1 dark /
|
|
94
|
-
// 1.89:1 light) and no pink ramp step clears 4.5:1 on both the dark-grey and
|
|
95
|
-
// light-blue card, so the glyphs take the flipping `--color-text` like the
|
|
96
|
-
// meta text they prefix.
|
|
97
|
-
color: "[var(--color-text)]",
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const attendeesRow = style({
|
|
101
|
-
display: "flex",
|
|
102
|
-
alignItems: "center",
|
|
103
|
-
gap: 8,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const avatars = style({
|
|
107
|
-
display: "flex",
|
|
108
|
-
alignItems: "center",
|
|
109
|
-
gap: 4,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const more = style({
|
|
113
|
-
font: "ui-sm",
|
|
114
|
-
// WCAG AA fix: the "+N más" overflow count is small text on the same
|
|
115
|
-
// `--color-bg-200` card as the meta, so it shares the same 3.84:1-light
|
|
116
|
-
// failure and the same `--color-text` fix.
|
|
117
|
-
color: "[var(--color-text)]",
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const actionRow = style({
|
|
121
|
-
display: "flex",
|
|
122
|
-
gap: 8,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
export function EventCard(props: EventCardProps) {
|
|
126
|
-
const displayedAttendees = () => props.attendees?.slice(0, 3) ?? [];
|
|
127
|
-
const remainingCount = () => {
|
|
128
|
-
const total = props.attendeeCount ?? props.attendees?.length ?? 0;
|
|
129
|
-
const displayed = displayedAttendees().length;
|
|
130
|
-
return total - displayed;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<div class={`${card} ${props.class ?? ""}`}>
|
|
135
|
-
<Show when={props.decorationImage}>
|
|
136
|
-
<img class={decoration} src={props.decorationImage} alt="" />
|
|
137
|
-
</Show>
|
|
138
|
-
|
|
139
|
-
<Show when={props.image}>
|
|
140
|
-
<img class={preview} src={props.image} alt={props.title} />
|
|
141
|
-
</Show>
|
|
142
|
-
|
|
143
|
-
<div class={body}>
|
|
144
|
-
<h3 class={title}>{props.title}</h3>
|
|
145
|
-
|
|
146
|
-
<Show when={props.date || props.author}>
|
|
147
|
-
<div class={meta}>
|
|
148
|
-
<Show when={props.author}>
|
|
149
|
-
<div class={metaItem}>
|
|
150
|
-
<span class={metaIcon}>@</span>
|
|
151
|
-
<span>{props.author}</span>
|
|
152
|
-
</div>
|
|
153
|
-
</Show>
|
|
154
|
-
<Show when={props.date}>
|
|
155
|
-
<div class={metaItem}>
|
|
156
|
-
<span class={metaIcon}>⏱</span>
|
|
157
|
-
<span>{props.date}</span>
|
|
158
|
-
</div>
|
|
159
|
-
</Show>
|
|
160
|
-
</div>
|
|
161
|
-
</Show>
|
|
162
|
-
|
|
163
|
-
<Show when={displayedAttendees().length > 0}>
|
|
164
|
-
<div class={attendeesRow}>
|
|
165
|
-
<div class={avatars}>
|
|
166
|
-
<For each={displayedAttendees()}>
|
|
167
|
-
{(attendee) => <Avatar src={attendee.avatar} alt={attendee.name} size="sm" />}
|
|
168
|
-
</For>
|
|
169
|
-
</div>
|
|
170
|
-
<Show when={remainingCount() > 0}>
|
|
171
|
-
<span class={more}>+{remainingCount()} más</span>
|
|
172
|
-
</Show>
|
|
173
|
-
</div>
|
|
174
|
-
</Show>
|
|
175
|
-
|
|
176
|
-
<Show when={props.actions}>
|
|
177
|
-
<div class={actionRow}>
|
|
178
|
-
{typeof props.actions === "function" ? props.actions() : props.actions}
|
|
179
|
-
</div>
|
|
180
|
-
</Show>
|
|
181
|
-
</div>
|
|
182
|
-
</div>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export interface EventListItemProps {
|
|
187
|
-
title: string;
|
|
188
|
-
image?: string;
|
|
189
|
-
subtitle?: string;
|
|
190
|
-
onClick?: () => void;
|
|
191
|
-
class?: string;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const listItem = style({
|
|
195
|
-
display: "flex",
|
|
196
|
-
alignItems: "center",
|
|
197
|
-
gap: 12,
|
|
198
|
-
width: "full",
|
|
199
|
-
padding: 8,
|
|
200
|
-
borderRadius: "lg",
|
|
201
|
-
textAlign: "start",
|
|
202
|
-
cursor: "pointer",
|
|
203
|
-
backgroundColor: "transparent",
|
|
204
|
-
borderStyle: "none",
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const thumb = style({
|
|
208
|
-
width: 48,
|
|
209
|
-
height: 48,
|
|
210
|
-
flexShrink: 0,
|
|
211
|
-
borderRadius: "default",
|
|
212
|
-
objectFit: "cover",
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
const listBody = style({
|
|
216
|
-
display: "flex",
|
|
217
|
-
flexDirection: "column",
|
|
218
|
-
flexGrow: 1,
|
|
219
|
-
minWidth: 0,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
const listTitle = style({
|
|
223
|
-
font: "ui",
|
|
224
|
-
fontWeight: "medium",
|
|
225
|
-
color: "[var(--color-primary-100)]",
|
|
226
|
-
overflow: "hidden",
|
|
227
|
-
textOverflow: "ellipsis",
|
|
228
|
-
whiteSpace: "nowrap",
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
const listSubtitle = style({
|
|
232
|
-
font: "ui-sm",
|
|
233
|
-
color: "[var(--color-text-secondary)]",
|
|
234
|
-
overflow: "hidden",
|
|
235
|
-
textOverflow: "ellipsis",
|
|
236
|
-
whiteSpace: "nowrap",
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
export function EventListItem(props: EventListItemProps) {
|
|
240
|
-
return (
|
|
241
|
-
<HeadlessButton class={`${listItem} ${props.class ?? ""}`} onPress={() => props.onClick?.()}>
|
|
242
|
-
<Show when={props.image}>
|
|
243
|
-
<img class={thumb} src={props.image} alt={props.title} />
|
|
244
|
-
</Show>
|
|
245
|
-
<div class={listBody}>
|
|
246
|
-
<h4 class={listTitle}>{props.title}</h4>
|
|
247
|
-
<Show when={props.subtitle}>
|
|
248
|
-
<p class={listSubtitle}>{props.subtitle}</p>
|
|
249
|
-
</Show>
|
|
250
|
-
</div>
|
|
251
|
-
</HeadlessButton>
|
|
252
|
-
);
|
|
253
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { JSX } from "solid-js";
|
|
2
|
-
import { Logo, type LogoProps } from "../logo";
|
|
3
|
-
// Styled via the S2 style() macro through viviana's own seam (../../style →
|
|
4
|
-
// src/style.ts). A top app-bar: centered max-width row with a logo group and
|
|
5
|
-
// a nav slot, on the S2 ramps in Silapse colors.
|
|
6
|
-
import { style } from "../../style" with { type: "macro" };
|
|
7
|
-
|
|
8
|
-
export interface HeaderProps {
|
|
9
|
-
logoImage?: JSX.Element;
|
|
10
|
-
/** Props to pass to the Logo component (firstWord, secondWord, size, inverted). Pass null to hide the text logo. */
|
|
11
|
-
logoProps?: LogoProps | null;
|
|
12
|
-
logo?: JSX.Element;
|
|
13
|
-
children?: JSX.Element;
|
|
14
|
-
class?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const bar = style({
|
|
18
|
-
display: "flex",
|
|
19
|
-
alignItems: "center",
|
|
20
|
-
justifyContent: "center",
|
|
21
|
-
width: "full",
|
|
22
|
-
backgroundColor: "[var(--color-header-bg)]",
|
|
23
|
-
borderBottomWidth: 1,
|
|
24
|
-
borderStyle: "solid",
|
|
25
|
-
borderColor: "[var(--color-border)]",
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const inner = style({
|
|
29
|
-
display: "flex",
|
|
30
|
-
alignItems: "center",
|
|
31
|
-
justifyContent: "space-between",
|
|
32
|
-
width: "full",
|
|
33
|
-
maxWidth: 1152,
|
|
34
|
-
paddingX: 24,
|
|
35
|
-
paddingY: 12,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const logoGroup = style({
|
|
39
|
-
display: "flex",
|
|
40
|
-
alignItems: "center",
|
|
41
|
-
gap: 12,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const nav = style({
|
|
45
|
-
display: "flex",
|
|
46
|
-
alignItems: "center",
|
|
47
|
-
gap: 16,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
export function Header(props: HeaderProps) {
|
|
51
|
-
const showTextLogo = () => props.logo !== undefined || props.logoProps !== null;
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<header class={`${bar} ${props.class ?? ""}`}>
|
|
55
|
-
<div class={inner}>
|
|
56
|
-
<div class={logoGroup}>
|
|
57
|
-
{props.logoImage}
|
|
58
|
-
{showTextLogo() && (props.logo ?? <Logo size="lg" {...(props.logoProps ?? {})} />)}
|
|
59
|
-
</div>
|
|
60
|
-
<nav class={nav}>{props.children}</nav>
|
|
61
|
-
</div>
|
|
62
|
-
</header>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import type { JSX } from "solid-js";
|
|
2
|
-
import { Show, For } from "solid-js";
|
|
3
|
-
// Styled via the S2 style() macro through viviana's own seam (../../style →
|
|
4
|
-
// src/style.ts). A sidebar nav (sections with an accent rail + links) on the
|
|
5
|
-
// S2 spacing/type ramps in Silapse colors.
|
|
6
|
-
import { style } from "../../style" with { type: "macro" };
|
|
7
|
-
|
|
8
|
-
export interface NavItemProps {
|
|
9
|
-
title: string;
|
|
10
|
-
children?: JSX.Element;
|
|
11
|
-
class?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const item = style({
|
|
15
|
-
display: "flex",
|
|
16
|
-
alignItems: "center",
|
|
17
|
-
font: "heading-sm",
|
|
18
|
-
color: "[var(--color-primary-200)]",
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export function NavItem(props: NavItemProps) {
|
|
22
|
-
return (
|
|
23
|
-
<li class={`${item} ${props.class ?? ""}`}>
|
|
24
|
-
<span>{props.title}</span>
|
|
25
|
-
{props.children}
|
|
26
|
-
</li>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface NavLinkProps {
|
|
31
|
-
href: string;
|
|
32
|
-
children: JSX.Element;
|
|
33
|
-
active?: boolean;
|
|
34
|
-
class?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const link = style<{ isActive: boolean }>({
|
|
38
|
-
// A ≥24px hit target (WCAG 2.5.8): the link is a full-width flex row, not a
|
|
39
|
-
// bare inline anchor (which rendered ~15px tall on the `ui` ramp).
|
|
40
|
-
display: "flex",
|
|
41
|
-
alignItems: "center",
|
|
42
|
-
minHeight: 32,
|
|
43
|
-
font: "ui",
|
|
44
|
-
textDecoration: { default: "none", isActive: "underline" },
|
|
45
|
-
fontWeight: { default: "normal", isActive: "medium" },
|
|
46
|
-
// `color` MUST stay after `font`: the S2 style() `font` shorthand emits a
|
|
47
|
-
// default text color that wins on source order otherwise. The resting link
|
|
48
|
-
// uses the flipping `--color-text` (AA on the light `bg-200` panel); the active
|
|
49
|
-
// link is set apart by its underline + medium weight + `--color-primary-300`.
|
|
50
|
-
color: {
|
|
51
|
-
default: "[var(--color-text)]",
|
|
52
|
-
isActive: "[var(--color-primary-300)]",
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
export function NavLink(props: NavLinkProps) {
|
|
57
|
-
return (
|
|
58
|
-
<a
|
|
59
|
-
href={props.href}
|
|
60
|
-
class={`${link({ isActive: props.active ?? false })} ${props.class ?? ""}`}
|
|
61
|
-
>
|
|
62
|
-
{props.children}
|
|
63
|
-
</a>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface NavSectionProps {
|
|
68
|
-
title: string;
|
|
69
|
-
links?: { href: string; label: string; active?: boolean }[];
|
|
70
|
-
children?: JSX.Element;
|
|
71
|
-
class?: string;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const sectionRow = style({
|
|
75
|
-
display: "flex",
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const accentRail = style({
|
|
79
|
-
flexShrink: 0,
|
|
80
|
-
width: 4,
|
|
81
|
-
height: 20,
|
|
82
|
-
backgroundColor: "[var(--color-accent-300)]",
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const list = style({
|
|
86
|
-
display: "flex",
|
|
87
|
-
flexDirection: "column",
|
|
88
|
-
flexGrow: 1,
|
|
89
|
-
gap: 4,
|
|
90
|
-
paddingStart: 16,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
export function NavSection(props: NavSectionProps) {
|
|
94
|
-
return (
|
|
95
|
-
<div class={props.class ?? ""}>
|
|
96
|
-
<NavItem title={props.title} />
|
|
97
|
-
<div class={sectionRow}>
|
|
98
|
-
<div class={accentRail} />
|
|
99
|
-
<ul class={list}>
|
|
100
|
-
<Show when={props.links}>
|
|
101
|
-
<For each={props.links}>
|
|
102
|
-
{(navLink) => (
|
|
103
|
-
<li>
|
|
104
|
-
<NavLink href={navLink.href} active={navLink.active}>
|
|
105
|
-
{navLink.label}
|
|
106
|
-
</NavLink>
|
|
107
|
-
</li>
|
|
108
|
-
)}
|
|
109
|
-
</For>
|
|
110
|
-
</Show>
|
|
111
|
-
{props.children}
|
|
112
|
-
</ul>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface LateralNavProps {
|
|
119
|
-
transparent?: boolean;
|
|
120
|
-
children?: JSX.Element;
|
|
121
|
-
class?: string;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const nav = style<{ isTransparent: boolean }>({
|
|
125
|
-
width: 300,
|
|
126
|
-
margin: 0,
|
|
127
|
-
padding: 12,
|
|
128
|
-
borderEndWidth: 1,
|
|
129
|
-
borderStyle: "solid",
|
|
130
|
-
borderColor: "[var(--color-primary-600)]",
|
|
131
|
-
backgroundColor: {
|
|
132
|
-
default: "[var(--color-bg-200)]",
|
|
133
|
-
isTransparent: "transparent",
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
export function LateralNav(props: LateralNavProps) {
|
|
138
|
-
return (
|
|
139
|
-
<div class={`${nav({ isTransparent: props.transparent ?? false })} ${props.class ?? ""}`}>
|
|
140
|
-
{props.children}
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
}
|