@atmo-dev/events-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DatePicker.svelte +231 -0
- package/dist/DatePicker.svelte.d.ts +11 -0
- package/dist/DateTimePicker.svelte +101 -0
- package/dist/DateTimePicker.svelte.d.ts +9 -0
- package/dist/EventAttendees.svelte +203 -0
- package/dist/EventAttendees.svelte.d.ts +13 -0
- package/dist/EventCard.svelte +131 -0
- package/dist/EventCard.svelte.d.ts +8 -0
- package/dist/EventComments.svelte +99 -0
- package/dist/EventComments.svelte.d.ts +6 -0
- package/dist/EventEditor.svelte +589 -0
- package/dist/EventEditor.svelte.d.ts +20 -0
- package/dist/EventRsvp.svelte +237 -0
- package/dist/EventRsvp.svelte.d.ts +17 -0
- package/dist/EventView.svelte +433 -0
- package/dist/EventView.svelte.d.ts +16 -0
- package/dist/ImageDropper.svelte +66 -0
- package/dist/ImageDropper.svelte.d.ts +7 -0
- package/dist/Map.svelte +27 -0
- package/dist/Map.svelte.d.ts +8 -0
- package/dist/PostToBlueskyModal.svelte +244 -0
- package/dist/PostToBlueskyModal.svelte.d.ts +22 -0
- package/dist/ShareModal.svelte +160 -0
- package/dist/ShareModal.svelte.d.ts +23 -0
- package/dist/ThemeApply.svelte +50 -0
- package/dist/ThemeApply.svelte.d.ts +7 -0
- package/dist/ThemeBackground.svelte +33 -0
- package/dist/ThemeBackground.svelte.d.ts +7 -0
- package/dist/ThemePicker.svelte +102 -0
- package/dist/ThemePicker.svelte.d.ts +7 -0
- package/dist/ThumbnailPresets.svelte +68 -0
- package/dist/ThumbnailPresets.svelte.d.ts +11 -0
- package/dist/TimePicker.svelte +188 -0
- package/dist/TimePicker.svelte.d.ts +9 -0
- package/dist/TimezonePicker.svelte +132 -0
- package/dist/TimezonePicker.svelte.d.ts +6 -0
- package/dist/VodPlayer.svelte +137 -0
- package/dist/VodPlayer.svelte.d.ts +14 -0
- package/dist/VodTranscript.svelte +72 -0
- package/dist/VodTranscript.svelte.d.ts +8 -0
- package/dist/atproto-helpers.d.ts +21 -0
- package/dist/atproto-helpers.js +61 -0
- package/dist/cal/helper.d.ts +1 -0
- package/dist/cal/helper.js +20 -0
- package/dist/cal/ical.d.ts +22 -0
- package/dist/cal/ical.js +188 -0
- package/dist/cal/sanitize.d.ts +3 -0
- package/dist/cal/sanitize.js +25 -0
- package/dist/contrail.d.ts +54 -0
- package/dist/contrail.js +22 -0
- package/dist/date-format.d.ts +22 -0
- package/dist/date-format.js +43 -0
- package/dist/editor/LinksSection.svelte +144 -0
- package/dist/editor/LinksSection.svelte.d.ts +10 -0
- package/dist/editor/LocationSection.svelte +215 -0
- package/dist/editor/LocationSection.svelte.d.ts +8 -0
- package/dist/editor/RecurringModal.svelte +270 -0
- package/dist/editor/RecurringModal.svelte.d.ts +30 -0
- package/dist/editor/ThemeSection.svelte +39 -0
- package/dist/editor/ThemeSection.svelte.d.ts +7 -0
- package/dist/editor/ThumbnailSection.svelte +219 -0
- package/dist/editor/ThumbnailSection.svelte.d.ts +13 -0
- package/dist/editor/adapter.d.ts +98 -0
- package/dist/editor/adapter.js +9 -0
- package/dist/editor/save.d.ts +42 -0
- package/dist/editor/save.js +154 -0
- package/dist/editor/types.d.ts +39 -0
- package/dist/editor/types.js +9 -0
- package/dist/event-types.d.ts +70 -0
- package/dist/event-types.js +11 -0
- package/dist/event-view/AddToCalendarButton.svelte +42 -0
- package/dist/event-view/AddToCalendarButton.svelte.d.ts +9 -0
- package/dist/event-view/EventBadges.svelte +20 -0
- package/dist/event-view/EventBadges.svelte.d.ts +7 -0
- package/dist/event-view/EventDateBlock.svelte +43 -0
- package/dist/event-view/EventDateBlock.svelte.d.ts +7 -0
- package/dist/event-view/EventHostedBy.svelte +63 -0
- package/dist/event-view/EventHostedBy.svelte.d.ts +16 -0
- package/dist/event-view/EventLinksList.svelte +37 -0
- package/dist/event-view/EventLinksList.svelte.d.ts +9 -0
- package/dist/event-view/EventLocationBlock.svelte +48 -0
- package/dist/event-view/EventLocationBlock.svelte.d.ts +7 -0
- package/dist/event-view/EventLocationMap.svelte +72 -0
- package/dist/event-view/EventLocationMap.svelte.d.ts +8 -0
- package/dist/event-view/ExternalRsvpNotice.svelte +44 -0
- package/dist/event-view/ExternalRsvpNotice.svelte.d.ts +6 -0
- package/dist/event-view/InviteShareFlow.svelte +177 -0
- package/dist/event-view/InviteShareFlow.svelte.d.ts +15 -0
- package/dist/event-view/StreamPlacePlayer.svelte +222 -0
- package/dist/event-view/StreamPlacePlayer.svelte.d.ts +8 -0
- package/dist/event-view/format.d.ts +26 -0
- package/dist/event-view/format.js +145 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +18 -0
- package/dist/profile-url.d.ts +1 -0
- package/dist/profile-url.js +7 -0
- package/dist/theme.d.ts +9 -0
- package/dist/theme.js +22 -0
- package/dist/themes/Blobs.svelte +35 -0
- package/dist/themes/Blobs.svelte.d.ts +26 -0
- package/dist/themes/Butterflies.svelte +185 -0
- package/dist/themes/Butterflies.svelte.d.ts +3 -0
- package/dist/themes/Fireflies.svelte +134 -0
- package/dist/themes/Fireflies.svelte.d.ts +3 -0
- package/dist/themes/Kaleidoscope.svelte +177 -0
- package/dist/themes/Kaleidoscope.svelte.d.ts +3 -0
- package/dist/themes/Matrix.svelte +150 -0
- package/dist/themes/Matrix.svelte.d.ts +3 -0
- package/dist/themes/Stars.svelte +98 -0
- package/dist/themes/Stars.svelte.d.ts +3 -0
- package/dist/thumbnails/designs.d.ts +18 -0
- package/dist/thumbnails/designs.js +316 -0
- package/package.json +95 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural shape of a `community.lexicon.calendar.event` record, plus the
|
|
3
|
+
* extensions atmo adds (timezone, media, facets, theme, bskyPostRef).
|
|
4
|
+
*
|
|
5
|
+
* Defined permissively so consumers using project-local generated lexicon
|
|
6
|
+
* types can still pass their records into our components without type
|
|
7
|
+
* gymnastics. Components inspect specific fields and `$type` strings as
|
|
8
|
+
* needed; rich variant unions live in the consumer's lexicon types if they
|
|
9
|
+
* want them.
|
|
10
|
+
*/
|
|
11
|
+
export type EventLocationVariant = {
|
|
12
|
+
$type: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
};
|
|
15
|
+
export type EventStatus = string;
|
|
16
|
+
export type EventMode = string;
|
|
17
|
+
export interface EventLexiconMain {
|
|
18
|
+
$type?: 'community.lexicon.calendar.event';
|
|
19
|
+
createdAt: string;
|
|
20
|
+
name: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
startsAt?: string;
|
|
23
|
+
endsAt?: string;
|
|
24
|
+
locations?: EventLocationVariant[];
|
|
25
|
+
mode?: EventMode;
|
|
26
|
+
status?: EventStatus;
|
|
27
|
+
}
|
|
28
|
+
export type EventData = EventLexiconMain & {
|
|
29
|
+
startsAt: string;
|
|
30
|
+
timezone?: string;
|
|
31
|
+
/** Atmo stores URIs as objects with optional names rather than bare strings. */
|
|
32
|
+
uris?: Array<{
|
|
33
|
+
uri: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
}>;
|
|
36
|
+
media?: Array<{
|
|
37
|
+
role: string;
|
|
38
|
+
alt?: string;
|
|
39
|
+
content: {
|
|
40
|
+
$type: 'blob';
|
|
41
|
+
ref: {
|
|
42
|
+
$link: string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
}>;
|
|
47
|
+
facets?: Array<{
|
|
48
|
+
index: {
|
|
49
|
+
byteStart: number;
|
|
50
|
+
byteEnd: number;
|
|
51
|
+
};
|
|
52
|
+
features: Array<{
|
|
53
|
+
$type: string;
|
|
54
|
+
did?: string;
|
|
55
|
+
uri?: string;
|
|
56
|
+
tag?: string;
|
|
57
|
+
}>;
|
|
58
|
+
}>;
|
|
59
|
+
additionalData?: Record<string, unknown>;
|
|
60
|
+
theme?: {
|
|
61
|
+
name: string;
|
|
62
|
+
accentColor: string;
|
|
63
|
+
baseColor: string;
|
|
64
|
+
};
|
|
65
|
+
bskyPostRef?: {
|
|
66
|
+
uri: string;
|
|
67
|
+
cid: string;
|
|
68
|
+
showComments: boolean;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural shape of a `community.lexicon.calendar.event` record, plus the
|
|
3
|
+
* extensions atmo adds (timezone, media, facets, theme, bskyPostRef).
|
|
4
|
+
*
|
|
5
|
+
* Defined permissively so consumers using project-local generated lexicon
|
|
6
|
+
* types can still pass their records into our components without type
|
|
7
|
+
* gymnastics. Components inspect specific fields and `$type` strings as
|
|
8
|
+
* needed; rich variant unions live in the consumer's lexicon types if they
|
|
9
|
+
* want them.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { generateICalEvent } from '../cal/ical.js';
|
|
3
|
+
import type { FlatEventRecord } from '../contrail.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
eventData,
|
|
7
|
+
eventUri,
|
|
8
|
+
pageHref
|
|
9
|
+
}: { eventData: FlatEventRecord; eventUri: string; pageHref: string } = $props();
|
|
10
|
+
|
|
11
|
+
function downloadIcs() {
|
|
12
|
+
const ical = generateICalEvent(eventData, eventUri, pageHref);
|
|
13
|
+
const blob = new Blob([ical], { type: 'text/calendar;charset=utf-8' });
|
|
14
|
+
const url = URL.createObjectURL(blob);
|
|
15
|
+
const a = document.createElement('a');
|
|
16
|
+
a.href = url;
|
|
17
|
+
a.download = `${eventData.name.replace(/[^a-zA-Z0-9]/g, '-')}.ics`;
|
|
18
|
+
a.click();
|
|
19
|
+
URL.revokeObjectURL(url);
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<button
|
|
24
|
+
onclick={downloadIcs}
|
|
25
|
+
class="text-base-700 dark:text-base-300 hover:text-base-900 dark:hover:text-base-100 flex cursor-pointer items-center gap-2 text-sm font-medium transition-colors"
|
|
26
|
+
>
|
|
27
|
+
<svg
|
|
28
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
29
|
+
fill="none"
|
|
30
|
+
viewBox="0 0 24 24"
|
|
31
|
+
stroke-width="1.5"
|
|
32
|
+
stroke="currentColor"
|
|
33
|
+
class="size-4"
|
|
34
|
+
>
|
|
35
|
+
<path
|
|
36
|
+
stroke-linecap="round"
|
|
37
|
+
stroke-linejoin="round"
|
|
38
|
+
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5"
|
|
39
|
+
/>
|
|
40
|
+
</svg>
|
|
41
|
+
Add to Calendar
|
|
42
|
+
</button>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FlatEventRecord } from '../contrail.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
eventData: FlatEventRecord;
|
|
4
|
+
eventUri: string;
|
|
5
|
+
pageHref: string;
|
|
6
|
+
};
|
|
7
|
+
declare const AddToCalendarButton: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type AddToCalendarButton = ReturnType<typeof AddToCalendarButton>;
|
|
9
|
+
export default AddToCalendarButton;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge } from '@foxui/core';
|
|
3
|
+
import { getModeColor, getModeLabel } from './format';
|
|
4
|
+
|
|
5
|
+
let { mode, isOngoing }: { mode?: string; isOngoing: boolean } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
{#if mode || isOngoing}
|
|
9
|
+
<div class="mb-8 flex items-center gap-2">
|
|
10
|
+
{#if isOngoing}
|
|
11
|
+
<Badge size="md" variant="primary">
|
|
12
|
+
<span class="bg-accent-500 mr-1 inline-block size-1.5 animate-pulse rounded-full"></span>
|
|
13
|
+
Live
|
|
14
|
+
</Badge>
|
|
15
|
+
{/if}
|
|
16
|
+
{#if mode}
|
|
17
|
+
<Badge size="md" variant="primary" class={getModeColor(mode)}>{getModeLabel(mode)}</Badge>
|
|
18
|
+
{/if}
|
|
19
|
+
</div>
|
|
20
|
+
{/if}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge } from '@foxui/core';
|
|
3
|
+
import { formatDay, formatFullDate, formatMonth, formatTime, formatWeekday } from './format';
|
|
4
|
+
|
|
5
|
+
let { startDate, endDate }: { startDate: Date; endDate: Date | null } = $props();
|
|
6
|
+
|
|
7
|
+
let isSameDay = $derived(
|
|
8
|
+
endDate &&
|
|
9
|
+
startDate.getFullYear() === endDate.getFullYear() &&
|
|
10
|
+
startDate.getMonth() === endDate.getMonth() &&
|
|
11
|
+
startDate.getDate() === endDate.getDate()
|
|
12
|
+
);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<div class="mb-4 flex items-center gap-4">
|
|
16
|
+
<div
|
|
17
|
+
class="border-base-200 dark:border-base-700 bg-base-100 dark:bg-base-950/30 flex size-12 shrink-0 flex-col items-center justify-center overflow-hidden rounded-xl border"
|
|
18
|
+
>
|
|
19
|
+
<span class="text-base-500 dark:text-base-400 text-[9px] leading-none font-semibold">
|
|
20
|
+
{formatMonth(startDate)}
|
|
21
|
+
</span>
|
|
22
|
+
<span class="text-base-900 dark:text-base-50 text-lg leading-tight font-bold">
|
|
23
|
+
{formatDay(startDate)}
|
|
24
|
+
</span>
|
|
25
|
+
</div>
|
|
26
|
+
<div>
|
|
27
|
+
<p class="text-base-900 dark:text-base-50 font-semibold">
|
|
28
|
+
{formatWeekday(startDate)}, {formatFullDate(startDate)}
|
|
29
|
+
{#if endDate && !isSameDay}
|
|
30
|
+
- {formatWeekday(endDate)}, {formatFullDate(endDate)}
|
|
31
|
+
{/if}
|
|
32
|
+
</p>
|
|
33
|
+
<p class="text-base-500 dark:text-base-400 flex flex-wrap items-center gap-2 text-sm">
|
|
34
|
+
<span>
|
|
35
|
+
{formatTime(startDate)}
|
|
36
|
+
{#if endDate && isSameDay}
|
|
37
|
+
- {formatTime(endDate)}
|
|
38
|
+
{/if}
|
|
39
|
+
</span>
|
|
40
|
+
<Badge size="sm" variant="secondary">local time</Badge>
|
|
41
|
+
</p>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Avatar as FoxAvatar } from '@foxui/core';
|
|
3
|
+
import type { HostProfile } from '../contrail.js';
|
|
4
|
+
|
|
5
|
+
type Speaker = { id?: string; handle?: string; name: string; avatar?: string };
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
hostProfile,
|
|
9
|
+
hostUrl,
|
|
10
|
+
did,
|
|
11
|
+
speakers = []
|
|
12
|
+
}: {
|
|
13
|
+
hostProfile: HostProfile | null | undefined;
|
|
14
|
+
hostUrl: string;
|
|
15
|
+
did: string;
|
|
16
|
+
speakers?: Speaker[];
|
|
17
|
+
} = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<div>
|
|
21
|
+
<p class="text-base-500 dark:text-base-400 mb-3 text-xs font-semibold tracking-wider uppercase">
|
|
22
|
+
Hosted By
|
|
23
|
+
</p>
|
|
24
|
+
<a
|
|
25
|
+
href={hostUrl}
|
|
26
|
+
class="text-base-900 dark:text-base-100 flex items-center gap-2.5 font-medium transition-opacity hover:opacity-80"
|
|
27
|
+
>
|
|
28
|
+
<FoxAvatar
|
|
29
|
+
src={hostProfile?.avatar}
|
|
30
|
+
alt={hostProfile?.displayName || hostProfile?.handle || did}
|
|
31
|
+
class="size-8 shrink-0"
|
|
32
|
+
/>
|
|
33
|
+
<span class="truncate text-sm">
|
|
34
|
+
{hostProfile?.displayName || hostProfile?.handle || did}
|
|
35
|
+
</span>
|
|
36
|
+
</a>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
{#if speakers.length > 0}
|
|
40
|
+
<div>
|
|
41
|
+
<p class="text-base-500 dark:text-base-400 mb-3 text-xs font-semibold tracking-wider uppercase">
|
|
42
|
+
Speakers
|
|
43
|
+
</p>
|
|
44
|
+
<div class="space-y-2">
|
|
45
|
+
{#each speakers as speaker, i (speaker.id || i)}
|
|
46
|
+
{#if speaker.handle}
|
|
47
|
+
<a
|
|
48
|
+
href="/p/{speaker.handle}"
|
|
49
|
+
class="text-base-900 dark:text-base-100 flex items-center gap-2.5 font-medium transition-opacity hover:opacity-80"
|
|
50
|
+
>
|
|
51
|
+
<FoxAvatar src={speaker.avatar} alt={speaker.name} class="size-8 shrink-0" />
|
|
52
|
+
<span class="truncate text-sm">{speaker.name}</span>
|
|
53
|
+
</a>
|
|
54
|
+
{:else}
|
|
55
|
+
<div class="text-base-900 dark:text-base-100 flex items-center gap-2.5 font-medium">
|
|
56
|
+
<FoxAvatar alt={speaker.name} class="size-8 shrink-0" />
|
|
57
|
+
<span class="truncate text-sm">{speaker.name}</span>
|
|
58
|
+
</div>
|
|
59
|
+
{/if}
|
|
60
|
+
{/each}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HostProfile } from '../contrail.js';
|
|
2
|
+
type Speaker = {
|
|
3
|
+
id?: string;
|
|
4
|
+
handle?: string;
|
|
5
|
+
name: string;
|
|
6
|
+
avatar?: string;
|
|
7
|
+
};
|
|
8
|
+
type $$ComponentProps = {
|
|
9
|
+
hostProfile: HostProfile | null | undefined;
|
|
10
|
+
hostUrl: string;
|
|
11
|
+
did: string;
|
|
12
|
+
speakers?: Speaker[];
|
|
13
|
+
};
|
|
14
|
+
declare const EventHostedBy: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
15
|
+
type EventHostedBy = ReturnType<typeof EventHostedBy>;
|
|
16
|
+
export default EventHostedBy;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { uris = [] }: { uris?: Array<{ uri: string; name?: string }> } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
{#if uris.length > 0}
|
|
6
|
+
<div>
|
|
7
|
+
<p class="text-base-500 dark:text-base-400 mb-4 text-xs font-semibold tracking-wider uppercase">
|
|
8
|
+
Links
|
|
9
|
+
</p>
|
|
10
|
+
<div class="space-y-3">
|
|
11
|
+
{#each uris as link (link.name + link.uri)}
|
|
12
|
+
<a
|
|
13
|
+
href={link.uri}
|
|
14
|
+
target="_blank"
|
|
15
|
+
rel="noopener noreferrer"
|
|
16
|
+
class="text-base-700 dark:text-base-300 hover:text-base-900 dark:hover:text-base-100 flex items-center gap-1.5 text-sm transition-colors"
|
|
17
|
+
>
|
|
18
|
+
<svg
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
fill="none"
|
|
21
|
+
viewBox="0 0 24 24"
|
|
22
|
+
stroke-width="1.5"
|
|
23
|
+
stroke="currentColor"
|
|
24
|
+
class="size-3.5 shrink-0"
|
|
25
|
+
>
|
|
26
|
+
<path
|
|
27
|
+
stroke-linecap="round"
|
|
28
|
+
stroke-linejoin="round"
|
|
29
|
+
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
|
|
30
|
+
/>
|
|
31
|
+
</svg>
|
|
32
|
+
<span class="truncate">{link.name || link.uri.replace(/^https?:\/\//, '')}</span>
|
|
33
|
+
</a>
|
|
34
|
+
{/each}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
{/if}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { LocationData } from './format';
|
|
3
|
+
|
|
4
|
+
let { locationData }: { locationData: LocationData | null } = $props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#if locationData}
|
|
8
|
+
<a
|
|
9
|
+
href={locationData.googleMapsUrl}
|
|
10
|
+
target="_blank"
|
|
11
|
+
rel="noopener noreferrer"
|
|
12
|
+
class="mb-6 flex items-center gap-4 transition-opacity hover:opacity-80"
|
|
13
|
+
>
|
|
14
|
+
<div
|
|
15
|
+
class="border-base-200 dark:border-base-700 bg-base-100 dark:bg-base-950/30 flex size-12 shrink-0 items-center justify-center rounded-xl border"
|
|
16
|
+
>
|
|
17
|
+
<svg
|
|
18
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
19
|
+
fill="none"
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
stroke-width="1.5"
|
|
22
|
+
stroke="currentColor"
|
|
23
|
+
class="text-base-900 dark:text-base-200 size-5"
|
|
24
|
+
>
|
|
25
|
+
<path
|
|
26
|
+
stroke-linecap="round"
|
|
27
|
+
stroke-linejoin="round"
|
|
28
|
+
d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
|
|
29
|
+
/>
|
|
30
|
+
<path
|
|
31
|
+
stroke-linecap="round"
|
|
32
|
+
stroke-linejoin="round"
|
|
33
|
+
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z"
|
|
34
|
+
/>
|
|
35
|
+
</svg>
|
|
36
|
+
</div>
|
|
37
|
+
<div>
|
|
38
|
+
{#if locationData.name}
|
|
39
|
+
<p class="text-base-900 dark:text-base-50 font-semibold">{locationData.name}</p>
|
|
40
|
+
<p class="text-base-500 dark:text-base-400 text-sm">{locationData.shortAddress}</p>
|
|
41
|
+
{:else}
|
|
42
|
+
<p class="text-base-900 dark:text-base-50 font-semibold">
|
|
43
|
+
{locationData.shortAddress}
|
|
44
|
+
</p>
|
|
45
|
+
{/if}
|
|
46
|
+
</div>
|
|
47
|
+
</a>
|
|
48
|
+
{/if}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LocationData } from './format';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
locationData: LocationData | null;
|
|
4
|
+
};
|
|
5
|
+
declare const EventLocationBlock: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type EventLocationBlock = ReturnType<typeof EventLocationBlock>;
|
|
7
|
+
export default EventLocationBlock;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge } from '@foxui/core';
|
|
3
|
+
import Map from '../Map.svelte';
|
|
4
|
+
import type { LocationData, GeoLocation } from './format';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
locationData,
|
|
8
|
+
geoLocation
|
|
9
|
+
}: {
|
|
10
|
+
locationData: LocationData | null;
|
|
11
|
+
geoLocation: GeoLocation | null;
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
let copied = $state(false);
|
|
15
|
+
|
|
16
|
+
async function copyCoords() {
|
|
17
|
+
if (!geoLocation) return;
|
|
18
|
+
const text = `${geoLocation.lat.toFixed(5)}, ${geoLocation.lng.toFixed(5)}`;
|
|
19
|
+
try {
|
|
20
|
+
await navigator.clipboard.writeText(text);
|
|
21
|
+
copied = true;
|
|
22
|
+
setTimeout(() => (copied = false), 2000);
|
|
23
|
+
} catch {}
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
{#if geoLocation && locationData}
|
|
28
|
+
<div class="mt-8 mb-8">
|
|
29
|
+
<div class="mb-3 flex items-baseline gap-2">
|
|
30
|
+
<p class="text-base-500 dark:text-base-400 text-xs font-semibold tracking-wider uppercase">
|
|
31
|
+
Location:
|
|
32
|
+
</p>
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
onclick={copyCoords}
|
|
36
|
+
class="ml-auto cursor-pointer transition-opacity active:opacity-60"
|
|
37
|
+
title="Copy coordinates"
|
|
38
|
+
aria-label="Copy coordinates"
|
|
39
|
+
>
|
|
40
|
+
<Badge size="sm" variant="secondary" class="font-mono">
|
|
41
|
+
{copied
|
|
42
|
+
? 'Copied!'
|
|
43
|
+
: `${geoLocation.lat.toFixed(5)}, ${geoLocation.lng.toFixed(5)}`}
|
|
44
|
+
</Badge>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="h-64 w-full overflow-hidden rounded-xl">
|
|
48
|
+
<Map lat={geoLocation.lat} lng={geoLocation.lng} />
|
|
49
|
+
</div>
|
|
50
|
+
<p class="text-base-700 dark:text-base-200 mt-3 text-sm">{locationData.fullString}</p>
|
|
51
|
+
<p class="text-base-500 dark:text-base-400 mt-1 text-xs">
|
|
52
|
+
Open in
|
|
53
|
+
<a
|
|
54
|
+
href={geoLocation.googleMapsUrl}
|
|
55
|
+
target="_blank"
|
|
56
|
+
rel="noopener noreferrer"
|
|
57
|
+
class="text-base-700 dark:text-base-300 hover:underline"
|
|
58
|
+
>
|
|
59
|
+
Google Maps
|
|
60
|
+
</a>
|
|
61
|
+
|
|
|
62
|
+
<a
|
|
63
|
+
href={geoLocation.osmUrl}
|
|
64
|
+
target="_blank"
|
|
65
|
+
rel="noopener noreferrer"
|
|
66
|
+
class="text-base-700 dark:text-base-300 hover:underline"
|
|
67
|
+
>
|
|
68
|
+
OpenStreetMap
|
|
69
|
+
</a>
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
{/if}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LocationData, GeoLocation } from './format';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
locationData: LocationData | null;
|
|
4
|
+
geoLocation: GeoLocation | null;
|
|
5
|
+
};
|
|
6
|
+
declare const EventLocationMap: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type EventLocationMap = ReturnType<typeof EventLocationMap>;
|
|
8
|
+
export default EventLocationMap;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button } from '@foxui/core';
|
|
3
|
+
|
|
4
|
+
// Shown in place of the RSVP controls for imported events whose host opted out
|
|
5
|
+
// of atmo RSVPs (additionalData.externalSource.rsvpMode === 'external_only').
|
|
6
|
+
// Directs attendees to the original event page instead.
|
|
7
|
+
let { url }: { url: string } = $props();
|
|
8
|
+
|
|
9
|
+
let host = $derived.by(() => {
|
|
10
|
+
try {
|
|
11
|
+
return new URL(url).hostname.replace(/^www\./, '');
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<div
|
|
19
|
+
class="border-base-200 dark:border-base-800 bg-base-100 dark:bg-base-950/50 mt-8 mb-2 flex flex-col gap-3 rounded-2xl border p-4 sm:flex-row sm:items-center sm:justify-between"
|
|
20
|
+
>
|
|
21
|
+
<div class="min-w-0">
|
|
22
|
+
<p class="text-base-900 dark:text-base-50 text-sm font-semibold">RSVP on the original page</p>
|
|
23
|
+
<p class="text-base-600 dark:text-base-400 mt-0.5 text-xs">
|
|
24
|
+
{host
|
|
25
|
+
? `This event is hosted on ${host}. Head there to RSVP.`
|
|
26
|
+
: 'This event is hosted elsewhere. Open the original page to RSVP.'}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
<Button href={url} target="_blank" rel="noopener noreferrer" class="shrink-0">
|
|
30
|
+
{host ? `RSVP on ${host}` : 'Open original event'}
|
|
31
|
+
<svg
|
|
32
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
33
|
+
viewBox="0 0 20 20"
|
|
34
|
+
fill="currentColor"
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
>
|
|
37
|
+
<path
|
|
38
|
+
fill-rule="evenodd"
|
|
39
|
+
d="M5.22 14.78a.75.75 0 0 0 1.06 0l7.22-7.22v5.69a.75.75 0 0 0 1.5 0v-7.5a.75.75 0 0 0-.75-.75h-7.5a.75.75 0 0 0 0 1.5h5.69l-7.22 7.22a.75.75 0 0 0 0 1.06Z"
|
|
40
|
+
clip-rule="evenodd"
|
|
41
|
+
/>
|
|
42
|
+
</svg>
|
|
43
|
+
</Button>
|
|
44
|
+
</div>
|