From bce557cc2dc767628bed6aac87301a1be7c5431b Mon Sep 17 00:00:00 2001
From: rxliuli <rxliuli@gmail.com>
Date: Tue, 4 Nov 2025 05:03:50 +0800
Subject: init commit

---
 .../MediumStoryCard/EditorialStoryCardItem.svelte  | 304 +++++++++++++++++++++
 1 file changed, 304 insertions(+)
 create mode 100644 src/components/jet/item/MediumStoryCard/EditorialStoryCardItem.svelte

(limited to 'src/components/jet/item/MediumStoryCard/EditorialStoryCardItem.svelte')

diff --git a/src/components/jet/item/MediumStoryCard/EditorialStoryCardItem.svelte b/src/components/jet/item/MediumStoryCard/EditorialStoryCardItem.svelte
new file mode 100644
index 0000000..7b7807c
--- /dev/null
+++ b/src/components/jet/item/MediumStoryCard/EditorialStoryCardItem.svelte
@@ -0,0 +1,304 @@
+<script lang="ts">
+    import {
+        isFlowAction,
+        type EditorialStoryCard,
+        type FlowAction,
+    } from '@jet-app/app-store/api/models';
+    import type { Opt } from '@jet/environment';
+
+    import LineClamp from '@amp/web-app-components/src/components/LineClamp/LineClamp.svelte';
+    import { sanitizeHtml } from '@amp/web-app-components/src/utils/sanitize-html';
+    import AppIcon from '~/components/AppIcon.svelte';
+    import Artwork from '~/components/Artwork.svelte';
+    import LinkWrapper from '~/components/LinkWrapper.svelte';
+    import { getI18n } from '~/stores/i18n';
+    import HoverWrapper from '~/components/HoverWrapper.svelte';
+
+    export let item: EditorialStoryCard;
+
+    let {
+        clickAction,
+        collectionIcons,
+        title,
+        lockup: { title: lockupTitle, subtitle, heading: lockupHeading } = {},
+    } = item;
+    const i18n = getI18n();
+    const hasMultipleCollectionIcons = (collectionIcons?.length ?? 0) > 1;
+    const destination: Opt<FlowAction> =
+        clickAction && isFlowAction(clickAction) ? clickAction : undefined;
+</script>
+
+<LinkWrapper action={destination}>
+    <article>
+        {#if item.artwork}
+            <div class="artwork-container">
+                <HoverWrapper element="div">
+                    <Artwork
+                        artwork={item.artwork}
+                        profile="editorial-story-card"
+                    />
+                </HoverWrapper>
+            </div>
+        {/if}
+        <div class="details-container">
+            <div
+                class="title-container"
+                class:on-dark={item.isMediaDark}
+                class:on-light={!item.isMediaDark}
+            >
+                {#if item.badge}
+                    <h4>{item.badge.title}</h4>
+                {/if}
+
+                {#if item.title}
+                    <h3>{@html sanitizeHtml(item.title)}</h3>
+                {/if}
+
+                {#if item.description}
+                    <p>{@html sanitizeHtml(item.description)}</p>
+                {/if}
+            </div>
+
+            {#if collectionIcons && !item.editorialDisplayOptions.suppressLockup}
+                <div class="lockup-container">
+                    <ul class:with-multiple-icons={hasMultipleCollectionIcons}>
+                        {#each collectionIcons as collectionIcon}
+                            <li class="app-icon-container">
+                                <AppIcon
+                                    icon={collectionIcon}
+                                    fixedWidth={false}
+                                    profile={hasMultipleCollectionIcons
+                                        ? 'app-icon-medium'
+                                        : 'app-icon'}
+                                />
+                            </li>
+                        {/each}
+                    </ul>
+
+                    {#if !hasMultipleCollectionIcons}
+                        <div class="metadata-container">
+                            {#if lockupHeading}
+                                <span class="lockup-eyebrow">
+                                    {lockupHeading}
+                                </span>
+                            {/if}
+
+                            <!--
+                            Some cards with the lockup UI don't have a `lockup` property,
+                            so we use the title of the item as a fallback.
+                        -->
+                            {#if lockupTitle || title}
+                                <LineClamp clamp={1}>
+                                    <h4 class="lockup-title">
+                                        {lockupTitle || title}
+                                    </h4>
+                                </LineClamp>
+                            {/if}
+
+                            {#if subtitle}
+                                <LineClamp clamp={1}>
+                                    <p class="lockup-subtitle">{subtitle}</p>
+                                </LineClamp>
+                            {/if}
+                        </div>
+
+                        {#if destination}
+                            <div class="button-container">
+                                <span class="get-button transparent">
+                                    {$i18n.t('ASE.Web.AppStore.View')}
+                                </span>
+                            </div>
+                        {/if}
+                    {/if}
+                </div>
+            {/if}
+        </div>
+        <div
+            class="blur-overlay"
+            style:--brightness={item.isMediaDark ? 0.75 : 1.25}
+        />
+    </article>
+</LinkWrapper>
+
+<style lang="scss">
+    @use '@amp/web-shared-styles/sasskit-stylekit/ac-sasskit-config';
+    @use 'ac-sasskit/core/helpers' as *;
+    @use 'ac-sasskit/core/locale' as *;
+
+    article {
+        position: relative;
+        overflow: hidden;
+        border-radius: var(--global-border-radius-large);
+        box-shadow: var(--shadow-medium);
+        aspect-ratio: 3/4;
+        container-type: inline-size;
+        container-name: card;
+    }
+
+    .artwork-container {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+    }
+
+    .details-container {
+        display: flex;
+        flex-direction: column;
+        justify-content: end;
+        height: 100%;
+        border-radius: var(--global-border-radius-large);
+        overflow: hidden;
+        z-index: 1;
+    }
+
+    .title-container {
+        padding: 20px;
+        z-index: 2;
+    }
+
+    .title-container h3 {
+        margin-bottom: 2px;
+        font: var(--title-1-emphasized);
+        text-wrap: pretty;
+    }
+
+    .title-container h4 {
+        font: var(--callout-emphasized);
+    }
+
+    .on-dark {
+        color: var(--systemPrimary-onDark);
+    }
+
+    .on-light {
+        color: var(--systemPrimary-onLight);
+    }
+
+    .title-container.on-dark h4 {
+        color: var(--systemSecondary-onDark);
+        mix-blend-mode: plus-lighter;
+    }
+
+    .title-container.on-light h4 {
+        color: var(--systemSecondary-onLight);
+    }
+
+    .title-container.on-dark p {
+        font: var(--body);
+        color: var(--systemSecondary-onDark);
+    }
+
+    .title-container.on-light p {
+        font: var(--body);
+        color: var(--systemSecondary-onLight);
+    }
+
+    .lockup-container {
+        display: flex;
+        align-items: center;
+        min-height: 80px;
+        padding: 10px 20px;
+        color: var(--systemPrimary-onDark);
+        background-image: linear-gradient(rgba(0, 0, 0, 0.2) 0 0);
+        z-index: 2;
+    }
+
+    .metadata-container {
+        flex-grow: 1;
+        margin-inline-end: 16px;
+    }
+
+    .lockup-title {
+        font: var(--title-3-emphasized);
+    }
+
+    .lockup-eyebrow {
+        color: var(--systemSecondary-onDark);
+        font: var(--subhead-emphasized);
+        text-transform: uppercase;
+        mix-blend-mode: plus-lighter;
+    }
+
+    .lockup-subtitle {
+        color: var(--systemSecondary-onDark);
+        font: var(--callout);
+        mix-blend-mode: plus-lighter;
+    }
+
+    .app-icon-container {
+        flex-shrink: 0;
+        width: 48px;
+        margin-inline-end: 16px;
+    }
+
+    article:hover .blur-overlay {
+        height: 52%;
+        backdrop-filter: blur(70px) saturate(1.5)
+            brightness(calc(var(--brightness) * 0.9));
+    }
+
+    .blur-overlay {
+        position: absolute;
+        z-index: 1;
+        top: unset;
+        bottom: 0;
+        width: 100%;
+        height: 50%;
+        border-radius: var(--global-border-radius-large);
+        mask-image: linear-gradient(
+            180deg,
+            rgba(255, 255, 255, 0) 5%,
+            rgba(0, 0, 0, 1) 50%
+        );
+        backdrop-filter: blur(50px) saturate(1.5)
+            brightness((var(--brightness)));
+        transition-property: height, backdrop-filter;
+        transition-duration: 210ms;
+        transition-timing-function: ease-out;
+    }
+
+    ul.with-multiple-icons {
+        width: 100%;
+        display: grid;
+        gap: 12px;
+
+        .app-icon-container {
+            width: 100%;
+            margin-inline-end: unset;
+        }
+    }
+
+    // In the following container queries, we are specifying column counts and hiding icons past
+    // that number to ensure a reasonable number of icons are shown for different size cards.
+    @container card (max-width: 300px) {
+        ul.with-multiple-icons {
+            // Think of "4" as the number of columns to show
+            grid-template-columns: repeat(4, 1fr);
+        }
+
+        // And "5" as the number of columns to hide past
+        .app-icon-container:nth-child(n + 5) {
+            display: none;
+        }
+    }
+
+    @container card (min-width: 300px) and (max-width: 400px) {
+        ul.with-multiple-icons {
+            grid-template-columns: repeat(5, 1fr);
+        }
+
+        .app-icon-container:nth-child(n + 6) {
+            display: none;
+        }
+    }
+
+    @container card (min-width: 400px) {
+        ul.with-multiple-icons {
+            grid-template-columns: repeat(6, 1fr);
+        }
+
+        .app-icon-container:nth-child(n + 7) {
+            display: none;
+        }
+    }
+</style>
-- 
cgit v1.2.3

