summaryrefslogtreecommitdiff
path: root/src/components/jet/web-navigation
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/web-navigation
init commit
Diffstat (limited to 'src/components/jet/web-navigation')
-rw-r--r--src/components/jet/web-navigation/CategoryTabItem.svelte67
-rw-r--r--src/components/jet/web-navigation/PlatformSelectorDropdown.svelte88
-rw-r--r--src/components/jet/web-navigation/PlatformSelectorItem.svelte97
3 files changed, 252 insertions, 0 deletions
diff --git a/src/components/jet/web-navigation/CategoryTabItem.svelte b/src/components/jet/web-navigation/CategoryTabItem.svelte
new file mode 100644
index 0000000..61f2570
--- /dev/null
+++ b/src/components/jet/web-navigation/CategoryTabItem.svelte
@@ -0,0 +1,67 @@
+<script lang="ts">
+ import { createEventDispatcher } from 'svelte';
+ import { buildSrc } from '@amp/web-app-components/src/components/Artwork/utils/srcset';
+ import Item from '@amp/web-app-components/src/components/Navigation/Item.svelte';
+ import ItemContent from '@amp/web-app-components/src/components/Navigation/ItemContent.svelte';
+
+ const dispatch = createEventDispatcher();
+
+ export let item: any;
+ export let selected: boolean = false;
+ export let translateFn: (key: string) => string;
+ $$props; // lets the other props automatically passed to navigation item components enter without being delcared explicitly
+
+ const itemClicked = (): void => {
+ dispatch('selectItem', item);
+ };
+
+ $: backgroundImage = item.artwork
+ ? buildSrc(
+ item.artwork.template,
+ {
+ crop: 'bb',
+ width: 40,
+ height: 40,
+ fileType: 'webp',
+ },
+ {},
+ )
+ : undefined;
+</script>
+
+<Item {item} {selected} {translateFn}>
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
+ <a
+ href={item.url}
+ class="navigation-item__link"
+ role="button"
+ aria-pressed={selected}
+ on:click|preventDefault={itemClicked}
+ >
+ <ItemContent label={item.label}>
+ <div
+ slot="icon"
+ aria-hidden={true}
+ class="icon"
+ style:--background-image={`url(${backgroundImage})`}
+ />
+ </ItemContent>
+ </a>
+</Item>
+
+<style>
+ .icon {
+ display: flex;
+ align-self: center;
+ width: 20px;
+ height: 20px;
+ background: var(--keyColor);
+ mask: var(--background-image) center / contain no-repeat;
+
+ @media (--sidebar-visible) {
+ width: 18px;
+ height: 18px;
+ }
+ }
+</style>
diff --git a/src/components/jet/web-navigation/PlatformSelectorDropdown.svelte b/src/components/jet/web-navigation/PlatformSelectorDropdown.svelte
new file mode 100644
index 0000000..f0fe666
--- /dev/null
+++ b/src/components/jet/web-navigation/PlatformSelectorDropdown.svelte
@@ -0,0 +1,88 @@
+<script lang="ts">
+ import type { WebNavigationLink } from '@jet-app/app-store/api/models/web-navigation';
+
+ import SFSymbol from '~/components/SFSymbol.svelte';
+ import PlatformSelectorItem from '~/components/jet/web-navigation/PlatformSelectorItem.svelte';
+ import { getI18n } from '~/stores/i18n';
+ import Menu from '~/components/Menu.svelte';
+ import { getJet } from '~/jet';
+
+ export let platformSelectors: WebNavigationLink[];
+
+ const i18n = getI18n();
+ const jet = getJet();
+
+ $: activeSelector = platformSelectors.find((selector) => selector.isActive);
+
+ const handleShowMenu = () => {
+ jet.recordCustomMetricsEvent({
+ eventType: 'click',
+ actionType: 'open',
+ targetType: 'button',
+ targetId: 'PlatformSelector',
+ });
+ };
+</script>
+
+{#if activeSelector}
+ <nav>
+ <Menu options={platformSelectors} forcedXPosition={25} {handleShowMenu}>
+ <svelte:fragment slot="trigger">
+ <span
+ class="platform-selector-text"
+ id="platform-selector-text"
+ aria-labelledby="app-store-icon-contianer platform-selector-text"
+ aria-haspopup="menu"
+ >
+ {$i18n.t(
+ 'ASE.Web.AppStore.Navigation.PlatformSelectorText',
+ {
+ platform: activeSelector.action.title,
+ },
+ )}
+
+ <SFSymbol name="chevron.down" />
+ </span>
+ </svelte:fragment>
+
+ <svelte:fragment slot="option" let:option>
+ <PlatformSelectorItem platformSelector={option} />
+ </svelte:fragment>
+ </Menu>
+ </nav>
+{/if}
+
+<style>
+ nav {
+ --menu-item-padding: 0;
+ --menu-item-margin: 0 0 8px 0;
+ --menu-popover-padding: 8px;
+ --menu-common-padding: 8px;
+ --menu-trigger-padding: 0;
+ --menu-popover-background-color: var(--pageBg);
+ --menu-popover-box-shadow: 10px 10px 10px 0
+ var(--systemQuaternary-onLight);
+ --menu-popover-border-radius: 14px;
+ --menu-popover-border: 1px solid var(--systemQuaternary);
+ --menu-popover-z-index: calc(var(--z-web-chrome) + 1);
+ }
+
+ .platform-selector-text {
+ display: flex;
+ align-items: center;
+ gap: var(--platform-selector-trigger-gap, 4px);
+ font: var(--title-2);
+ white-space: nowrap;
+ }
+
+ .platform-selector-text :global(svg) {
+ height: 0.7em;
+ position: relative;
+ top: 2px;
+ fill: var(--systemPrimary);
+ }
+
+ nav :global(.menu-popover) {
+ width: 211px;
+ }
+</style>
diff --git a/src/components/jet/web-navigation/PlatformSelectorItem.svelte b/src/components/jet/web-navigation/PlatformSelectorItem.svelte
new file mode 100644
index 0000000..9b72fda
--- /dev/null
+++ b/src/components/jet/web-navigation/PlatformSelectorItem.svelte
@@ -0,0 +1,97 @@
+<script lang="ts">
+ import { isSome } from '@jet/environment/types/optional';
+ import type { WebNavigationLink } from '@jet-app/app-store/api/models/web-navigation';
+ import { isSearchResultsPageIntent } from '@jet-app/app-store/api/intents/search-results-page-intent';
+
+ import FlowAction from '~/components/jet/action/FlowAction.svelte';
+ import SystemImage, {
+ isSystemImageArtwork,
+ } from '~/components/SystemImage.svelte';
+ import SFSymbol from '~/components/SFSymbol.svelte';
+ import { getI18n } from '~/stores/i18n';
+
+ export let platformSelector: WebNavigationLink;
+
+ const i18n = getI18n();
+
+ $: ({ action, isActive } = platformSelector);
+ $: ({ artwork } = action);
+</script>
+
+<FlowAction destination={action}>
+ <span class="platform-selector" class:is-active={isActive}>
+ {#if isSome(artwork) && isSystemImageArtwork(artwork)}
+ <div class="icon-container">
+ <SystemImage {artwork} />
+ </div>
+ {/if}
+
+ <span
+ class="platform-title"
+ aria-label={$i18n.t(
+ 'ASE.Web.AppStore.Navigation.AX.PlatformSelectorItem',
+ {
+ platform: action.title,
+ },
+ )}
+ >
+ {action.title}
+ </span>
+
+ {#if action.destination && isSearchResultsPageIntent(action.destination)}
+ <span aria-hidden={true} class="search-icon-container">
+ <SFSymbol name="magnifyingglass" />
+ </span>
+ {/if}
+ </span>
+</FlowAction>
+
+<style lang="scss">
+ @use '@amp/web-shared-styles/app/core/globalvars' as *;
+
+ .platform-selector {
+ display: flex;
+ border-radius: var(--global-border-radius-medium);
+ padding: 8px;
+ margin-bottom: 4px;
+ gap: 10px;
+ transition: background-color 175ms ease-in;
+ }
+
+ .platform-selector:not(.is-active):hover {
+ background-color: rgba(45, 45, 45, 0.035);
+
+ @media (prefers-color-scheme: dark) {
+ background-color: rgba(45, 45, 45, 0.35);
+ }
+ }
+
+ .platform-selector.is-active {
+ background-color: var(--systemQuinary);
+ }
+
+ .icon-container {
+ display: flex;
+ justify-content: center;
+ padding-inline-end: 2px;
+ }
+
+ .icon-container :global(svg) {
+ max-height: 16px;
+ width: 23px;
+ }
+
+ .search-icon-container {
+ display: flex;
+ }
+
+ .search-icon-container :global(svg) {
+ fill: var(--systemSecondary);
+ width: 16px;
+ }
+
+ .platform-title {
+ font: var(--body);
+ flex-grow: 1;
+ }
+</style>