summaryrefslogtreecommitdiff
path: root/src/components/jet/item/SmallStoryCardWithMediaRiver.svelte
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /src/components/jet/item/SmallStoryCardWithMediaRiver.svelte
init commit
Diffstat (limited to 'src/components/jet/item/SmallStoryCardWithMediaRiver.svelte')
-rw-r--r--src/components/jet/item/SmallStoryCardWithMediaRiver.svelte118
1 files changed, 118 insertions, 0 deletions
diff --git a/src/components/jet/item/SmallStoryCardWithMediaRiver.svelte b/src/components/jet/item/SmallStoryCardWithMediaRiver.svelte
new file mode 100644
index 0000000..038f504
--- /dev/null
+++ b/src/components/jet/item/SmallStoryCardWithMediaRiver.svelte
@@ -0,0 +1,118 @@
+<script lang="ts" context="module">
+ import type {
+ TodayCard,
+ TodayCardMediaRiver,
+ } from '@jet-app/app-store/api/models';
+
+ export interface TodayCardWithMediaRiver extends TodayCard {
+ media: TodayCardMediaRiver;
+ }
+
+ export function isSmallStoryCardWithMediaRiver(
+ item: TodayCard,
+ ): item is TodayCardWithMediaRiver {
+ return !!item.media && item.media.kind === 'river';
+ }
+</script>
+
+<script lang="ts">
+ import type { Opt } from '@jet/environment/types/optional';
+ import HoverWrapper from '~/components/HoverWrapper.svelte';
+ import LinkWrapper from '~/components/LinkWrapper.svelte';
+ import AppIconRiver from '~/components/AppIconRiver.svelte';
+ import {
+ getBackgroundGradientCSSVarsFromArtworks,
+ getLuminanceForRGB,
+ } from '~/utils/color';
+
+ export let item: TodayCardWithMediaRiver;
+
+ $: icons = item.media.lockups.map((lockup) => lockup.icon);
+ $: backgroundGradientCssVars = getBackgroundGradientCSSVarsFromArtworks(
+ icons,
+ {
+ // sorts from darkest to lightest
+ sortFn: (a, b) => getLuminanceForRGB(a) - getLuminanceForRGB(b),
+ },
+ );
+
+ let title: Opt<string>;
+ let eyebrow: Opt<string>;
+ $: {
+ eyebrow = item.heading;
+ title = item.title;
+
+ if (item.inlineDescription) {
+ eyebrow = item.title;
+ title = item.inlineDescription;
+ }
+ }
+</script>
+
+<LinkWrapper action={item.clickAction}>
+ <HoverWrapper>
+ <div class="river-container" style={backgroundGradientCssVars}>
+ <AppIconRiver {icons} profile="app-icon" />
+ </div>
+ </HoverWrapper>
+
+ <div class="text-container">
+ {#if eyebrow}
+ <h4>{eyebrow}</h4>
+ {/if}
+
+ {#if title}
+ <h3>{title}</h3>
+ {/if}
+ </div>
+</LinkWrapper>
+
+<style>
+ .river-container {
+ --app-icon-river-icon-width: 48px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ aspect-ratio: 16 / 9;
+ width: 100%;
+ border-radius: 8px;
+ background: radial-gradient(
+ circle at 3% -50%,
+ var(--top-left, #000) 20%,
+ transparent 70%
+ ),
+ radial-gradient(
+ circle at -50% 120%,
+ var(--bottom-left, #000) 40%,
+ transparent 80%
+ ),
+ radial-gradient(
+ circle at 140% -50%,
+ var(--top-right, #000) 60%,
+ transparent 80%
+ ),
+ radial-gradient(
+ circle at 62% 100%,
+ var(--bottom-right, #000) 50%,
+ transparent 100%
+ );
+ }
+
+ .river-container :global(.app-icons:last-of-type) {
+ margin-bottom: 0;
+ }
+
+ .text-container {
+ margin-top: 8px;
+ }
+
+ h3 {
+ font: var(--title-3);
+ }
+
+ h4 {
+ margin-bottom: 2px;
+ font: var(--callout-emphasized);
+ color: var(--systemSecondary);
+ }
+</style>