@foxui/social 0.4.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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/components/blog-entry/BlogEntry.svelte +80 -0
- package/dist/components/blog-entry/BlogEntry.svelte.d.ts +14 -0
- package/dist/components/blog-entry/index.d.ts +1 -0
- package/dist/components/blog-entry/index.js +1 -0
- package/dist/components/bluesky-login/BlueskyLogin.svelte +23 -0
- package/dist/components/bluesky-login/BlueskyLogin.svelte.d.ts +4 -0
- package/dist/components/bluesky-login/BlueskyLoginModal.svelte +141 -0
- package/dist/components/bluesky-login/BlueskyLoginModal.svelte.d.ts +9 -0
- package/dist/components/bluesky-login/index.d.ts +8 -0
- package/dist/components/bluesky-login/index.js +3 -0
- package/dist/components/bluesky-post/BlueskyPost.svelte +36 -0
- package/dist/components/bluesky-post/BlueskyPost.svelte.d.ts +10 -0
- package/dist/components/bluesky-post/index.d.ts +5 -0
- package/dist/components/bluesky-post/index.js +110 -0
- package/dist/components/chat/Chat.svelte +0 -0
- package/dist/components/chat/Chat.svelte.d.ts +26 -0
- package/dist/components/chat/ChatMessage.svelte +0 -0
- package/dist/components/chat/ChatMessage.svelte.d.ts +26 -0
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.js +1 -0
- package/dist/components/emoji-picker/EmojiPicker.svelte +91 -0
- package/dist/components/emoji-picker/EmojiPicker.svelte.d.ts +11 -0
- package/dist/components/emoji-picker/PopoverEmojiPicker.svelte +24 -0
- package/dist/components/emoji-picker/PopoverEmojiPicker.svelte.d.ts +11 -0
- package/dist/components/emoji-picker/emoji.d.ts +7 -0
- package/dist/components/emoji-picker/emoji.js +56 -0
- package/dist/components/emoji-picker/index.d.ts +2 -0
- package/dist/components/emoji-picker/index.js +2 -0
- package/dist/components/github-corner/GithubCorner.svelte +62 -0
- package/dist/components/github-corner/GithubCorner.svelte.d.ts +4 -0
- package/dist/components/github-corner/index.d.ts +1 -0
- package/dist/components/github-corner/index.js +1 -0
- package/dist/components/index.d.ts +12 -0
- package/dist/components/index.js +20 -0
- package/dist/components/nested-comments/Comment.svelte +151 -0
- package/dist/components/nested-comments/Comment.svelte.d.ts +9 -0
- package/dist/components/nested-comments/NestedComments.svelte +14 -0
- package/dist/components/nested-comments/NestedComments.svelte.d.ts +7 -0
- package/dist/components/nested-comments/index.d.ts +1 -0
- package/dist/components/nested-comments/index.js +1 -0
- package/dist/components/post/Post.svelte +294 -0
- package/dist/components/post/Post.svelte.d.ts +23 -0
- package/dist/components/post/PostAction.svelte +27 -0
- package/dist/components/post/PostAction.svelte.d.ts +9 -0
- package/dist/components/post/embeds/Embed.svelte +24 -0
- package/dist/components/post/embeds/Embed.svelte.d.ts +7 -0
- package/dist/components/post/embeds/External.svelte +39 -0
- package/dist/components/post/embeds/External.svelte.d.ts +7 -0
- package/dist/components/post/embeds/Images.svelte +38 -0
- package/dist/components/post/embeds/Images.svelte.d.ts +7 -0
- package/dist/components/post/embeds/Video.svelte +45 -0
- package/dist/components/post/embeds/Video.svelte.d.ts +7 -0
- package/dist/components/post/index.d.ts +63 -0
- package/dist/components/post/index.js +1 -0
- package/dist/components/social-icons/All.svelte +47 -0
- package/dist/components/social-icons/All.svelte.d.ts +12 -0
- package/dist/components/social-icons/Bluesky.svelte +37 -0
- package/dist/components/social-icons/Bluesky.svelte.d.ts +8 -0
- package/dist/components/social-icons/Discord.svelte +37 -0
- package/dist/components/social-icons/Discord.svelte.d.ts +8 -0
- package/dist/components/social-icons/Facebook.svelte +37 -0
- package/dist/components/social-icons/Facebook.svelte.d.ts +8 -0
- package/dist/components/social-icons/Github.svelte +38 -0
- package/dist/components/social-icons/Github.svelte.d.ts +8 -0
- package/dist/components/social-icons/Twitter.svelte +37 -0
- package/dist/components/social-icons/Twitter.svelte.d.ts +8 -0
- package/dist/components/social-icons/Youtube.svelte +36 -0
- package/dist/components/social-icons/Youtube.svelte.d.ts +8 -0
- package/dist/components/social-icons/index.d.ts +7 -0
- package/dist/components/social-icons/index.js +8 -0
- package/dist/components/star-rating/StarRating.svelte +104 -0
- package/dist/components/star-rating/StarRating.svelte.d.ts +13 -0
- package/dist/components/star-rating/index.d.ts +1 -0
- package/dist/components/star-rating/index.js +1 -0
- package/dist/components/swiper-cards/CardSwiper.svelte +235 -0
- package/dist/components/swiper-cards/CardSwiper.svelte.d.ts +18 -0
- package/dist/components/swiper-cards/index.d.ts +14 -0
- package/dist/components/swiper-cards/index.js +1 -0
- package/dist/components/user-profile/UserProfile.svelte +60 -0
- package/dist/components/user-profile/UserProfile.svelte.d.ts +13 -0
- package/dist/components/user-profile/index.d.ts +1 -0
- package/dist/components/user-profile/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Embed from './embeds/Embed.svelte';
|
|
3
|
+
import { cn, Avatar, Prose } from '@foxui/core';
|
|
4
|
+
import type { WithChildren, WithElementRef } from 'bits-ui';
|
|
5
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
6
|
+
import type { PostData } from '.';
|
|
7
|
+
import PostAction from './PostAction.svelte';
|
|
8
|
+
import type { Snippet } from 'svelte';
|
|
9
|
+
import { numberToHumanReadable } from '..';
|
|
10
|
+
import { RelativeTime } from '@foxui/time';
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
ref = $bindable(),
|
|
14
|
+
data,
|
|
15
|
+
class: className,
|
|
16
|
+
bookmarked = $bindable(false),
|
|
17
|
+
liked = $bindable(false),
|
|
18
|
+
|
|
19
|
+
showReply = $bindable(true),
|
|
20
|
+
showRepost = $bindable(true),
|
|
21
|
+
showLike = $bindable(true),
|
|
22
|
+
showBookmark = $bindable(true),
|
|
23
|
+
|
|
24
|
+
onReplyClick,
|
|
25
|
+
onRepostClick,
|
|
26
|
+
onLikeClick,
|
|
27
|
+
onBookmarkClick,
|
|
28
|
+
|
|
29
|
+
customActions,
|
|
30
|
+
|
|
31
|
+
children,
|
|
32
|
+
|
|
33
|
+
logo
|
|
34
|
+
}: WithElementRef<WithChildren<HTMLAttributes<HTMLDivElement>>> & {
|
|
35
|
+
data: PostData;
|
|
36
|
+
class?: string;
|
|
37
|
+
|
|
38
|
+
bookmarked?: boolean;
|
|
39
|
+
liked?: boolean;
|
|
40
|
+
|
|
41
|
+
showReply?: boolean;
|
|
42
|
+
showRepost?: boolean;
|
|
43
|
+
showLike?: boolean;
|
|
44
|
+
showBookmark?: boolean;
|
|
45
|
+
|
|
46
|
+
onReplyClick?: () => void;
|
|
47
|
+
onRepostClick?: () => void;
|
|
48
|
+
onLikeClick?: () => void;
|
|
49
|
+
onBookmarkClick?: () => void;
|
|
50
|
+
|
|
51
|
+
customActions?: Snippet;
|
|
52
|
+
|
|
53
|
+
logo?: Snippet;
|
|
54
|
+
} = $props();
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div
|
|
58
|
+
bind:this={ref}
|
|
59
|
+
class={cn('text-base-950 dark:text-base-50 transition-colors duration-200', className)}
|
|
60
|
+
>
|
|
61
|
+
{#if data.reposted}
|
|
62
|
+
<div class="mb-3 inline-flex items-center gap-2 text-xs">
|
|
63
|
+
<svg
|
|
64
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
65
|
+
viewBox="0 0 24 24"
|
|
66
|
+
fill="currentColor"
|
|
67
|
+
class="size-3"
|
|
68
|
+
>
|
|
69
|
+
<path
|
|
70
|
+
fill-rule="evenodd"
|
|
71
|
+
d="M4.755 10.059a7.5 7.5 0 0 1 12.548-3.364l1.903 1.903h-3.183a.75.75 0 1 0 0 1.5h4.992a.75.75 0 0 0 .75-.75V4.356a.75.75 0 0 0-1.5 0v3.18l-1.9-1.9A9 9 0 0 0 3.306 9.67a.75.75 0 1 0 1.45.388Zm15.408 3.352a.75.75 0 0 0-.919.53 7.5 7.5 0 0 1-12.548 3.364l-1.902-1.903h3.183a.75.75 0 0 0 0-1.5H2.984a.75.75 0 0 0-.75.75v4.992a.75.75 0 0 0 1.5 0v-3.18l1.9 1.9a9 9 0 0 0 15.059-4.035.75.75 0 0 0-.53-.918Z"
|
|
72
|
+
clip-rule="evenodd"
|
|
73
|
+
/>
|
|
74
|
+
</svg>
|
|
75
|
+
|
|
76
|
+
<div class="inline-flex gap-1">
|
|
77
|
+
reposted by
|
|
78
|
+
<a
|
|
79
|
+
href={data.reposted.href}
|
|
80
|
+
class="hover:text-accent-600 dark:hover:text-accent-400 font-bold"
|
|
81
|
+
>
|
|
82
|
+
@{data.reposted.handle}
|
|
83
|
+
</a>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
{/if}
|
|
87
|
+
{#if data.replyTo}
|
|
88
|
+
<div class="mb-3 inline-flex items-center gap-2 text-xs">
|
|
89
|
+
|
|
90
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-3">
|
|
91
|
+
<path fill-rule="evenodd" d="M14.47 2.47a.75.75 0 0 1 1.06 0l6 6a.75.75 0 0 1 0 1.06l-6 6a.75.75 0 1 1-1.06-1.06l4.72-4.72H9a5.25 5.25 0 1 0 0 10.5h3a.75.75 0 0 1 0 1.5H9a6.75 6.75 0 0 1 0-13.5h10.19l-4.72-4.72a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
|
|
92
|
+
</svg>
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
<div class="inline-flex gap-1">
|
|
96
|
+
replying to
|
|
97
|
+
<a
|
|
98
|
+
href={data.replyTo.href}
|
|
99
|
+
class="hover:text-accent-600 dark:hover:text-accent-400 font-bold"
|
|
100
|
+
>
|
|
101
|
+
@{data.replyTo.handle}
|
|
102
|
+
</a>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
{/if}
|
|
106
|
+
<div class="flex gap-4">
|
|
107
|
+
<Avatar src={data.author.avatar} />
|
|
108
|
+
|
|
109
|
+
<div class="w-full">
|
|
110
|
+
<div class="mb-1 flex items-start justify-between gap-2">
|
|
111
|
+
<div class="flex items-start gap-4">
|
|
112
|
+
{#if data.author.href}
|
|
113
|
+
<a
|
|
114
|
+
class="hover:bg-accent-900/5 group/post-author -mx-2 -my-0.5 flex flex-col items-baseline gap-x-2 gap-y-0.5 rounded-xl px-2 py-0.5 sm:flex-row"
|
|
115
|
+
href={data.author.href}
|
|
116
|
+
>
|
|
117
|
+
{#if data.author.displayName}
|
|
118
|
+
<div
|
|
119
|
+
class="text-base-900 group-hover/post-author:text-accent-600 dark:text-base-50 dark:group-hover/post-author:text-accent-300 text-sm font-semibold leading-tight"
|
|
120
|
+
>
|
|
121
|
+
{data.author.displayName}
|
|
122
|
+
</div>
|
|
123
|
+
{/if}
|
|
124
|
+
<div
|
|
125
|
+
class={cn(
|
|
126
|
+
'group-hover/post-author:text-accent-600 dark:group-hover/post-author:text-accent-400 text-sm',
|
|
127
|
+
!data.author.displayName
|
|
128
|
+
? 'text-base-900 dark:text-base-50 font-semibold'
|
|
129
|
+
: 'text-base-600 dark:text-base-400'
|
|
130
|
+
)}
|
|
131
|
+
>
|
|
132
|
+
@{data.author.handle}
|
|
133
|
+
</div>
|
|
134
|
+
</a>
|
|
135
|
+
{:else}
|
|
136
|
+
<div
|
|
137
|
+
class="-mx-2 -my-0.5 flex flex-col items-baseline gap-x-2 gap-y-0.5 rounded-xl px-2 py-0.5 sm:flex-row"
|
|
138
|
+
>
|
|
139
|
+
<div class="text-base-900 dark:text-base-50 text-sm font-semibold leading-tight">
|
|
140
|
+
{data.author.displayName}
|
|
141
|
+
</div>
|
|
142
|
+
<div class="text-base-600 dark:text-base-400 text-sm">
|
|
143
|
+
@{data.author.handle}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
{/if}
|
|
147
|
+
|
|
148
|
+
<div class="text-base-600 dark:text-base-400 block text-sm no-underline">
|
|
149
|
+
<RelativeTime date={new Date(data.createdAt)} locale="en" />
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
{#if logo}
|
|
154
|
+
{@render logo?.()}
|
|
155
|
+
{/if}
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<Prose size="md">
|
|
159
|
+
{#if data.htmlContent}
|
|
160
|
+
{@html data.htmlContent}
|
|
161
|
+
{:else}
|
|
162
|
+
{@render children?.()}
|
|
163
|
+
{/if}
|
|
164
|
+
</Prose>
|
|
165
|
+
|
|
166
|
+
{#if data.embed}
|
|
167
|
+
<Embed embed={data.embed} />
|
|
168
|
+
{/if}
|
|
169
|
+
|
|
170
|
+
{#if showReply || showRepost || showLike || showBookmark || customActions}
|
|
171
|
+
<div class="text-base-500 dark:text-base-400 mt-4 flex justify-between gap-2">
|
|
172
|
+
{#if showReply}
|
|
173
|
+
<PostAction onclick={onReplyClick}>
|
|
174
|
+
<svg
|
|
175
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
176
|
+
fill="none"
|
|
177
|
+
viewBox="0 0 24 24"
|
|
178
|
+
stroke-width="1.5"
|
|
179
|
+
stroke="currentColor"
|
|
180
|
+
class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
|
|
181
|
+
>
|
|
182
|
+
<path
|
|
183
|
+
stroke-linecap="round"
|
|
184
|
+
stroke-linejoin="round"
|
|
185
|
+
d="M12 20.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 7.444 3 12c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 0 1-.923 1.785A5.969 5.969 0 0 0 6 21c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337Z"
|
|
186
|
+
/>
|
|
187
|
+
</svg>
|
|
188
|
+
{#if data.replyCount}
|
|
189
|
+
{numberToHumanReadable(data.replyCount)}
|
|
190
|
+
{/if}
|
|
191
|
+
</PostAction>
|
|
192
|
+
{/if}
|
|
193
|
+
|
|
194
|
+
{#if showRepost}
|
|
195
|
+
<PostAction onclick={onRepostClick}>
|
|
196
|
+
<svg
|
|
197
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
198
|
+
fill="none"
|
|
199
|
+
viewBox="0 0 24 24"
|
|
200
|
+
stroke-width="1.5"
|
|
201
|
+
stroke="currentColor"
|
|
202
|
+
class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
|
|
203
|
+
>
|
|
204
|
+
<path
|
|
205
|
+
stroke-linecap="round"
|
|
206
|
+
stroke-linejoin="round"
|
|
207
|
+
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"
|
|
208
|
+
/>
|
|
209
|
+
</svg>
|
|
210
|
+
{#if data.repostCount}
|
|
211
|
+
{numberToHumanReadable(data.repostCount)}
|
|
212
|
+
{/if}
|
|
213
|
+
</PostAction>
|
|
214
|
+
{/if}
|
|
215
|
+
{#if showLike}
|
|
216
|
+
<PostAction
|
|
217
|
+
class={liked ? 'text-accent-700 dark:text-accent-500 font-semibold' : ''}
|
|
218
|
+
onclick={onLikeClick}
|
|
219
|
+
>
|
|
220
|
+
{#if liked}
|
|
221
|
+
<svg
|
|
222
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
223
|
+
viewBox="0 0 24 24"
|
|
224
|
+
fill="currentColor"
|
|
225
|
+
class="group-hover/post-action:bg-accent-500/10 text-accent-700 dark:text-accent-500 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
|
|
226
|
+
>
|
|
227
|
+
<path
|
|
228
|
+
d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z"
|
|
229
|
+
/>
|
|
230
|
+
</svg>
|
|
231
|
+
{:else}
|
|
232
|
+
<svg
|
|
233
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
234
|
+
fill="none"
|
|
235
|
+
viewBox="0 0 24 24"
|
|
236
|
+
stroke-width="1.5"
|
|
237
|
+
stroke="currentColor"
|
|
238
|
+
class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
|
|
239
|
+
>
|
|
240
|
+
<path
|
|
241
|
+
stroke-linecap="round"
|
|
242
|
+
stroke-linejoin="round"
|
|
243
|
+
d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"
|
|
244
|
+
/>
|
|
245
|
+
</svg>
|
|
246
|
+
{/if}
|
|
247
|
+
{#if data.likeCount}
|
|
248
|
+
{numberToHumanReadable(data.likeCount)}
|
|
249
|
+
{/if}
|
|
250
|
+
</PostAction>
|
|
251
|
+
{/if}
|
|
252
|
+
|
|
253
|
+
{#if showBookmark}
|
|
254
|
+
<PostAction onclick={onBookmarkClick}>
|
|
255
|
+
<span class="sr-only">Bookmark</span>
|
|
256
|
+
|
|
257
|
+
{#if bookmarked}
|
|
258
|
+
<svg
|
|
259
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
260
|
+
viewBox="0 0 24 24"
|
|
261
|
+
fill="currentColor"
|
|
262
|
+
class="group-hover/post-action:bg-accent-500/10 text-accent-700 dark:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
|
|
263
|
+
>
|
|
264
|
+
<path
|
|
265
|
+
fill-rule="evenodd"
|
|
266
|
+
d="M6.32 2.577a49.255 49.255 0 0 1 11.36 0c1.497.174 2.57 1.46 2.57 2.93V21a.75.75 0 0 1-1.085.67L12 18.089l-7.165 3.583A.75.75 0 0 1 3.75 21V5.507c0-1.47 1.073-2.756 2.57-2.93Z"
|
|
267
|
+
clip-rule="evenodd"
|
|
268
|
+
/>
|
|
269
|
+
</svg>
|
|
270
|
+
{:else}
|
|
271
|
+
<svg
|
|
272
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
273
|
+
fill="none"
|
|
274
|
+
viewBox="0 0 24 24"
|
|
275
|
+
stroke-width="1.5"
|
|
276
|
+
stroke="currentColor"
|
|
277
|
+
class="group-hover/post-action:bg-accent-500/10 group-hover/post-action:text-accent-700 dark:group-hover/post-action:text-accent-400 -m-1.5 size-7 rounded-full p-1.5 transition-all duration-100"
|
|
278
|
+
>
|
|
279
|
+
<path
|
|
280
|
+
stroke-linecap="round"
|
|
281
|
+
stroke-linejoin="round"
|
|
282
|
+
d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z"
|
|
283
|
+
/>
|
|
284
|
+
</svg>
|
|
285
|
+
{/if}
|
|
286
|
+
</PostAction>
|
|
287
|
+
{/if}
|
|
288
|
+
|
|
289
|
+
{@render customActions?.()}
|
|
290
|
+
</div>
|
|
291
|
+
{/if}
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { WithChildren, WithElementRef } from 'bits-ui';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import type { PostData } from '.';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
type $$ComponentProps = WithElementRef<WithChildren<HTMLAttributes<HTMLDivElement>>> & {
|
|
6
|
+
data: PostData;
|
|
7
|
+
class?: string;
|
|
8
|
+
bookmarked?: boolean;
|
|
9
|
+
liked?: boolean;
|
|
10
|
+
showReply?: boolean;
|
|
11
|
+
showRepost?: boolean;
|
|
12
|
+
showLike?: boolean;
|
|
13
|
+
showBookmark?: boolean;
|
|
14
|
+
onReplyClick?: () => void;
|
|
15
|
+
onRepostClick?: () => void;
|
|
16
|
+
onLikeClick?: () => void;
|
|
17
|
+
onBookmarkClick?: () => void;
|
|
18
|
+
customActions?: Snippet;
|
|
19
|
+
logo?: Snippet;
|
|
20
|
+
};
|
|
21
|
+
declare const Post: import("svelte").Component<$$ComponentProps, {}, "ref" | "bookmarked" | "liked" | "showReply" | "showRepost" | "showLike" | "showBookmark">;
|
|
22
|
+
type Post = ReturnType<typeof Post>;
|
|
23
|
+
export default Post;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '@foxui/core';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
onclick,
|
|
7
|
+
children,
|
|
8
|
+
class: className
|
|
9
|
+
}: {
|
|
10
|
+
onclick?: () => void;
|
|
11
|
+
children: Snippet;
|
|
12
|
+
class?: string;
|
|
13
|
+
} = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{#if onclick}
|
|
17
|
+
<button
|
|
18
|
+
class={cn('group/post-action inline-flex cursor-pointer items-center gap-2 text-sm', className)}
|
|
19
|
+
{onclick}
|
|
20
|
+
>
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</button>
|
|
23
|
+
{:else}
|
|
24
|
+
<div class={cn('inline-flex items-center gap-2 text-sm', className)}>
|
|
25
|
+
{@render children?.()}
|
|
26
|
+
</div>
|
|
27
|
+
{/if}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
onclick?: () => void;
|
|
4
|
+
children: Snippet;
|
|
5
|
+
class?: string;
|
|
6
|
+
};
|
|
7
|
+
declare const PostAction: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type PostAction = ReturnType<typeof PostAction>;
|
|
9
|
+
export default PostAction;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PostEmbed } from '../';
|
|
3
|
+
import External from './External.svelte';
|
|
4
|
+
import Images from './Images.svelte';
|
|
5
|
+
import Video from './Video.svelte';
|
|
6
|
+
|
|
7
|
+
const { embed }: { embed: PostEmbed } = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<div class="flex flex-col gap-2 pt-3 text-sm">
|
|
11
|
+
{#if embed.type === 'images'}
|
|
12
|
+
<Images data={embed} />
|
|
13
|
+
{:else if embed.type === 'external' && embed.external}
|
|
14
|
+
<External data={embed} />
|
|
15
|
+
{:else if embed.type === 'video' && embed.video}
|
|
16
|
+
<Video data={embed} />
|
|
17
|
+
{:else if embed.type === 'unknown'}
|
|
18
|
+
<div
|
|
19
|
+
class="text-base-700 dark:text-base-300 bg-base-200/50 dark:bg-base-900/50 border-base-300 dark:border-base-600/30 rounded-2xl border p-4 text-sm"
|
|
20
|
+
>
|
|
21
|
+
Unknown embed type
|
|
22
|
+
</div>
|
|
23
|
+
{/if}
|
|
24
|
+
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PostEmbedExternal } from '..';
|
|
3
|
+
|
|
4
|
+
const { data }: { data: PostEmbedExternal } = $props();
|
|
5
|
+
|
|
6
|
+
const domain = new URL(data.external.href).hostname.replace('www.', '');
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<article
|
|
10
|
+
class={[
|
|
11
|
+
'group dark:bg-base-900 bg-base-200 border-base-300 dark:border-base-600/30 max-w-md relative isolate flex flex-col justify-end overflow-hidden rounded-2xl border p-4',
|
|
12
|
+
data.external.thumb ? 'aspect-[16/9]' : ''
|
|
13
|
+
]}
|
|
14
|
+
>
|
|
15
|
+
{#if data.external.thumb}
|
|
16
|
+
<img
|
|
17
|
+
src={data.external.thumb}
|
|
18
|
+
alt={data.external.description}
|
|
19
|
+
class="absolute inset-0 -z-10 size-full object-cover transition-transform duration-500 ease-in-out group-hover:scale-102"
|
|
20
|
+
/>
|
|
21
|
+
{/if}
|
|
22
|
+
<div
|
|
23
|
+
class="dark:from-base-950/90 dark:via-base-950/40 from-base-50/90 via-base-50/40 absolute inset-0 -z-10 bg-gradient-to-t"
|
|
24
|
+
></div>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
class="text-base-700 dark:text-base-300 flex flex-wrap items-center gap-y-1 overflow-hidden text-sm"
|
|
28
|
+
>
|
|
29
|
+
<div>{domain}</div>
|
|
30
|
+
</div>
|
|
31
|
+
<h3
|
|
32
|
+
class="dark:text-base-50 text-base-900 group-hover:text-accent-600 dark:group-hover:text-accent-400 mt-1 text-lg/6 font-semibold transition-colors duration-200"
|
|
33
|
+
>
|
|
34
|
+
<a href={data.external.href} target="_blank" rel="noopener noreferrer nofollow">
|
|
35
|
+
<span class="absolute inset-0"></span>
|
|
36
|
+
{data.external.title}
|
|
37
|
+
</a>
|
|
38
|
+
</h3>
|
|
39
|
+
</article>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PostEmbedImage } from '..';
|
|
3
|
+
|
|
4
|
+
const { data }: { data: PostEmbedImage } = $props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
{#snippet imageSnippet(
|
|
8
|
+
image: {
|
|
9
|
+
alt: string;
|
|
10
|
+
thumb: string;
|
|
11
|
+
fullsize: string;
|
|
12
|
+
aspectRatio?: { width: number; height: number };
|
|
13
|
+
},
|
|
14
|
+
className?: string
|
|
15
|
+
)}
|
|
16
|
+
<img
|
|
17
|
+
loading="lazy"
|
|
18
|
+
src={image.thumb}
|
|
19
|
+
alt={image.alt}
|
|
20
|
+
style={image.aspectRatio
|
|
21
|
+
? `aspect-ratio: ${image.aspectRatio.width} / ${image.aspectRatio.height}`
|
|
22
|
+
: 'aspect-ratio: 1 / 1'}
|
|
23
|
+
class={[
|
|
24
|
+
'border-base-500/20 dark:border-base-400/20 w-fit max-w-full rounded-2xl border max-h-[40rem] object-contain',
|
|
25
|
+
className
|
|
26
|
+
]}
|
|
27
|
+
/>
|
|
28
|
+
{/snippet}
|
|
29
|
+
|
|
30
|
+
{#if data.images.length === 1}
|
|
31
|
+
{@render imageSnippet(data.images[0])}
|
|
32
|
+
{:else}
|
|
33
|
+
<div class="columns-2 gap-4">
|
|
34
|
+
{#each data.images as image}
|
|
35
|
+
{@render imageSnippet(image, 'mb-4')}
|
|
36
|
+
{/each}
|
|
37
|
+
</div>
|
|
38
|
+
{/if}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import Hls from 'hls.js';
|
|
4
|
+
import type { PostEmbedVideo } from '..';
|
|
5
|
+
|
|
6
|
+
const { data }: { data: PostEmbedVideo } = $props();
|
|
7
|
+
|
|
8
|
+
onMount(async () => {
|
|
9
|
+
const Plyr = (await import('plyr')).default;
|
|
10
|
+
if (Hls.isSupported()) {
|
|
11
|
+
var hls = new Hls();
|
|
12
|
+
hls.loadSource(data.video.playlist);
|
|
13
|
+
hls.attachMedia(element);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
new Plyr(element, {
|
|
17
|
+
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'fullscreen'],
|
|
18
|
+
ratio: data.video.aspectRatio
|
|
19
|
+
? `${data.video.aspectRatio.width}:${data.video.aspectRatio.height}`
|
|
20
|
+
: '16:9'
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
let element: HTMLMediaElement;
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<svelte:head>
|
|
28
|
+
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
|
|
29
|
+
</svelte:head>
|
|
30
|
+
|
|
31
|
+
<div
|
|
32
|
+
style={data.video.aspectRatio
|
|
33
|
+
? `aspect-ratio: ${data.video.aspectRatio.width} / ${data.video.aspectRatio.height}`
|
|
34
|
+
: 'aspect-ratio: 16 / 9'}
|
|
35
|
+
class="border-base-300 dark:border-base-400/40 w-full max-w-full overflow-hidden rounded-2xl border"
|
|
36
|
+
>
|
|
37
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
38
|
+
<video bind:this={element} class="h-full w-full" aria-label={data.video.alt}></video>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
* {
|
|
43
|
+
--plyr-color-main: var(--color-accent-500);
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type PostEmbedImage = {
|
|
2
|
+
type: 'images';
|
|
3
|
+
images: {
|
|
4
|
+
alt: string;
|
|
5
|
+
thumb: string;
|
|
6
|
+
fullsize: string;
|
|
7
|
+
aspectRatio?: {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
}[];
|
|
12
|
+
};
|
|
13
|
+
export type PostEmbedExternal = {
|
|
14
|
+
type: 'external';
|
|
15
|
+
external: {
|
|
16
|
+
href: string;
|
|
17
|
+
thumb: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export type PostEmbedVideo = {
|
|
23
|
+
type: 'video';
|
|
24
|
+
video: {
|
|
25
|
+
playlist: string;
|
|
26
|
+
thumb: string;
|
|
27
|
+
alt: string;
|
|
28
|
+
aspectRatio?: {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export type UnknownEmbed = {
|
|
35
|
+
type: 'unknown';
|
|
36
|
+
} & Record<string, unknown>;
|
|
37
|
+
export type PostEmbed = PostEmbedImage | PostEmbedExternal | PostEmbedVideo | UnknownEmbed;
|
|
38
|
+
export type PostData = {
|
|
39
|
+
href?: string;
|
|
40
|
+
id?: string;
|
|
41
|
+
reposted?: {
|
|
42
|
+
handle: string;
|
|
43
|
+
href: string;
|
|
44
|
+
};
|
|
45
|
+
replyTo?: {
|
|
46
|
+
handle: string;
|
|
47
|
+
href: string;
|
|
48
|
+
};
|
|
49
|
+
author: {
|
|
50
|
+
displayName: string;
|
|
51
|
+
handle: string;
|
|
52
|
+
avatar?: string;
|
|
53
|
+
href?: string;
|
|
54
|
+
};
|
|
55
|
+
replyCount: number;
|
|
56
|
+
repostCount: number;
|
|
57
|
+
likeCount: number;
|
|
58
|
+
createdAt: string;
|
|
59
|
+
embed?: PostEmbed;
|
|
60
|
+
htmlContent?: string;
|
|
61
|
+
replies?: PostData[];
|
|
62
|
+
};
|
|
63
|
+
export { default as Post } from './Post.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Post } from './Post.svelte';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Discord from './Discord.svelte';
|
|
3
|
+
import Github from './Github.svelte';
|
|
4
|
+
import Twitter from './Twitter.svelte';
|
|
5
|
+
import Youtube from './Youtube.svelte';
|
|
6
|
+
import Bluesky from './Bluesky.svelte';
|
|
7
|
+
import Facebook from './Facebook.svelte';
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
discord,
|
|
11
|
+
github,
|
|
12
|
+
twitter,
|
|
13
|
+
youtube,
|
|
14
|
+
bluesky,
|
|
15
|
+
facebook,
|
|
16
|
+
svgClasses
|
|
17
|
+
}: {
|
|
18
|
+
discord?: string;
|
|
19
|
+
github?: string;
|
|
20
|
+
twitter?: string;
|
|
21
|
+
youtube?: string;
|
|
22
|
+
bluesky?: string;
|
|
23
|
+
facebook?: string;
|
|
24
|
+
svgClasses?: string;
|
|
25
|
+
} = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<div class="flex flex-wrap items-center gap-4">
|
|
29
|
+
{#if discord}
|
|
30
|
+
<Discord href={discord} {svgClasses} />
|
|
31
|
+
{/if}
|
|
32
|
+
{#if github}
|
|
33
|
+
<Github href={github} {svgClasses} />
|
|
34
|
+
{/if}
|
|
35
|
+
{#if twitter}
|
|
36
|
+
<Twitter href={twitter} {svgClasses} />
|
|
37
|
+
{/if}
|
|
38
|
+
{#if youtube}
|
|
39
|
+
<Youtube href={youtube} {svgClasses} />
|
|
40
|
+
{/if}
|
|
41
|
+
{#if bluesky}
|
|
42
|
+
<Bluesky href={bluesky} {svgClasses} />
|
|
43
|
+
{/if}
|
|
44
|
+
{#if facebook}
|
|
45
|
+
<Facebook href={facebook} {svgClasses} />
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
discord?: string;
|
|
3
|
+
github?: string;
|
|
4
|
+
twitter?: string;
|
|
5
|
+
youtube?: string;
|
|
6
|
+
bluesky?: string;
|
|
7
|
+
facebook?: string;
|
|
8
|
+
svgClasses?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const All: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type All = ReturnType<typeof All>;
|
|
12
|
+
export default All;
|