-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathevent-card.tsx
More file actions
138 lines (122 loc) · 4.1 KB
/
event-card.tsx
File metadata and controls
138 lines (122 loc) · 4.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import type { ReactNode } from "react"
import { clsx } from "clsx"
import { CalendarIcon } from "@/app/conf/_design-system/pixelarticons/calendar-icon"
import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon"
import { Tag } from "@/app/conf/_design-system/tag"
const dateFormatter = new Intl.DateTimeFormat("en", {
day: "numeric",
month: "short",
year: "numeric",
})
const isoLikeDatePattern =
/^(\d{4}-\d{2}-\d{2}|\d{4}\/?\d{2}\/?\d{2}|\d{2}\/\d{2}\/\d{4})/
function normaliseDate(value: EventCardProps["date"]) {
if (value instanceof Date && !Number.isNaN(value.getTime())) {
return value
}
if (typeof value === "string") {
const parsed = new Date(value)
if (!Number.isNaN(parsed.getTime())) {
return parsed
}
}
return undefined
}
function formatDateLabel(value: EventCardProps["date"]) {
const parsed = normaliseDate(value)
if (parsed) {
if (typeof value === "string" && !isoLikeDatePattern.test(value.trim())) {
return value.trim() || undefined
}
return dateFormatter.format(parsed)
}
if (typeof value === "string") {
const trimmed = value.trim()
return trimmed.length > 0 ? trimmed : undefined
}
return undefined
}
export interface EventCardProps {
href: string
date?: Date | string | null
city: ReactNode
name: ReactNode
meta?: ReactNode
official?: boolean
}
export function EventCard({
href,
date,
city,
name,
meta,
official,
}: EventCardProps) {
const dateLabel = formatDateLabel(date)
const parsedDate = normaliseDate(date)
return (
<a
href={href}
className={clsx(
"gql-focus-visible group flex min-w-[260px] flex-col overflow-hidden border border-neu-200 bg-neu-0 text-left text-current no-underline ring-neu-400 hover:bg-sec-base/[.035] hover:ring-1 hover:ring-offset-1 hover:ring-offset-neu-0 dark:border-neu-100 dark:ring-neu-100 xs:min-w-[352px]",
)}
target="_blank"
rel="noreferrer"
>
<div className="flex flex-1 flex-col">
<div
className={clsx(
"flex items-center justify-between gap-2 px-2 text-neu-700 dark:text-neu-600 xs:px-4",
meta
? "border-b border-neu-200 py-2.5 dark:border-neu-100"
: "-mb-2 pt-2 xs:-mb-4 xs:pt-3",
)}
>
{meta ? (
<span className="typography-body-md font-medium">{meta}</span>
) : (
<span className="sr-only">Official GraphQL Local</span>
)}
{official ? (
<Tag color="hsl(var(--color-pri-base))" className="*:gap-1">
<span className="font-sans" aria-hidden>
★
</span>
Official
</Tag>
) : meta ? null : (
<div className="h-[22px]" />
)}
</div>
<div className="typography-h3 flex min-h-[100px] flex-1 flex-col justify-center px-2 py-3 text-neu-900 xs:min-h-[124px] xs:px-4 xs:py-6">
{name}
</div>
<div
className={clsx(
"flex flex-wrap border-t border-neu-200 text-neu-700 dark:border-neu-100",
dateLabel && city
? "grid grid-cols-2 divide-x divide-neu-200 dark:divide-neu-100"
: "",
)}
>
{dateLabel && (
<div className="typography-body-sm flex items-center gap-1 px-2 py-1.5 text-neu-700 dark:text-neu-600 xs:gap-1.5 xs:px-4 xs:py-2.5">
<CalendarIcon className="size-4 shrink-0 translate-y-[-.5px] text-neu-600 dark:text-neu-500 xs:size-5" />
{parsedDate ? (
<time dateTime={parsedDate.toISOString()}>{dateLabel}</time>
) : (
<span>{dateLabel}</span>
)}
</div>
)}
{city && (
<div className="typography-body-sm flex items-center gap-1.5 whitespace-pre px-2 py-1.5 text-neu-700 dark:text-neu-600 xs:px-4 xs:py-2.5">
<PinIcon className="size-4 shrink-0 translate-y-[-.5px] text-neu-600 dark:text-neu-500 xs:size-5" />
{city}
</div>
)}
</div>
</div>
</a>
)
}