summaryrefslogtreecommitdiff
path: root/src/context
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/context
init commit
Diffstat (limited to 'src/context')
-rw-r--r--src/context/accessibility-layout.ts93
-rw-r--r--src/context/today-card-layout.ts98
2 files changed, 191 insertions, 0 deletions
diff --git a/src/context/accessibility-layout.ts b/src/context/accessibility-layout.ts
new file mode 100644
index 0000000..110100f
--- /dev/null
+++ b/src/context/accessibility-layout.ts
@@ -0,0 +1,93 @@
+import { getContext, setContext } from 'svelte';
+
+import type { Shelf } from '@jet-app/app-store/api/models';
+import { isAccessibilityHeaderShelf } from '~/components/jet/shelf/AccessibilityHeaderShelf.svelte';
+import { isAccessibilityFeaturesShelf } from '~/components/jet/shelf/AccessibilityFeaturesShelf.svelte';
+import { isAccessibilityDeveloperLinkShelf } from '~/components/jet/shelf/AccessibilityDeveloperLinkShelf.svelte';
+
+/**
+ * Describes the layout configuration for accessibility shelves
+ */
+interface AccessibilityLayoutConfiguration {
+ withBottomPadding: boolean;
+}
+
+const ACCESSIBILITY_LAYOUT_FALLBACK: AccessibilityLayoutConfiguration =
+ Object.freeze({
+ withBottomPadding: false,
+ });
+
+type AccessibilityLayoutStore = WeakMap<
+ Shelf,
+ AccessibilityLayoutConfiguration
+>;
+type AccessibilityLayoutStoreContext = AccessibilityLayoutStore | undefined;
+
+const ACCESSIBILITY_LAYOUT_CONTEXT_ID = 'accessibility-layout-context';
+
+/**
+ * Check if a shelf is accessibility-related
+ */
+function isAccessibilityRelated(shelf: Shelf): boolean {
+ return (
+ shelf.contentType === 'accessibilityParagraph' ||
+ shelf.contentType === 'accessibilityFeatures'
+ );
+}
+
+/**
+ * Check if a shelf is one of the target accessibility shelves
+ */
+function isTargetAccessibilityShelf(shelf: Shelf): boolean {
+ return (
+ isAccessibilityHeaderShelf(shelf) ||
+ isAccessibilityFeaturesShelf(shelf) ||
+ isAccessibilityDeveloperLinkShelf(shelf)
+ );
+}
+
+/**
+ * Store the {@linkcode AccessibilityLayoutConfiguration} for each accessibility shelf
+ * in "context", so it can be retrieved at the shelf-component level
+ *
+ * This determines bottom padding based on whether the next shelf is accessibility-related
+ */
+export function setAccessibilityLayoutContext(page: { shelves: Shelf[] }) {
+ const store: AccessibilityLayoutStore = new WeakMap();
+
+ for (let i = 0; i < page.shelves.length; i++) {
+ const shelf = page.shelves[i];
+
+ // Only process target accessibility shelves
+ if (!isTargetAccessibilityShelf(shelf)) {
+ continue;
+ }
+
+ // Check if the next shelf is accessibility-related
+ const nextShelf = page.shelves[i + 1];
+ const hasAccessibilityNext =
+ nextShelf && isAccessibilityRelated(nextShelf);
+
+ store.set(shelf, {
+ withBottomPadding: !hasAccessibilityNext,
+ });
+ }
+
+ setContext<AccessibilityLayoutStoreContext>(
+ ACCESSIBILITY_LAYOUT_CONTEXT_ID,
+ store,
+ );
+}
+
+/**
+ * Retrieve the {@linkcode AccessibilityLayoutConfiguration} for a given accessibility shelf
+ */
+export function getAccessibilityLayoutConfiguration(
+ shelf: Shelf,
+): AccessibilityLayoutConfiguration {
+ const accessibilityLayout = getContext<AccessibilityLayoutStoreContext>(
+ ACCESSIBILITY_LAYOUT_CONTEXT_ID,
+ );
+
+ return accessibilityLayout?.get(shelf) ?? ACCESSIBILITY_LAYOUT_FALLBACK;
+}
diff --git a/src/context/today-card-layout.ts b/src/context/today-card-layout.ts
new file mode 100644
index 0000000..54f66ec
--- /dev/null
+++ b/src/context/today-card-layout.ts
@@ -0,0 +1,98 @@
+import { getContext, setContext } from 'svelte';
+
+import type { TodayPage } from '@jet-app/app-store/api/models';
+import {
+ type TodayCardShelf,
+ isTodayCardShelf,
+} from '~/components/jet/shelf/TodayCardShelf.svelte';
+
+/**
+ * Describes the configuration of the card layout within a {@linkcode TodayCardShelf}
+ */
+interface LayoutConfiguration {
+ wrap: {
+ shouldStretchFirstCard: boolean;
+ };
+ nowrap: {
+ shouldStretchFirstCard: boolean;
+ };
+}
+
+const LAYOUT_CONFIGURATION_FALLBACK: LayoutConfiguration = Object.freeze({
+ wrap: {
+ shouldStretchFirstCard: true,
+ },
+ nowrap: {
+ shouldStretchFirstCard: true,
+ },
+});
+
+type TodayCardLayoutStore = WeakMap<TodayCardShelf, LayoutConfiguration>;
+type TodayCardLayoutStoreContext = TodayCardLayoutStore | undefined;
+
+const TODAY_CARD_LAYOUT_CONTEXT_ID = 'today-card-layout-context';
+
+/**
+ * Store the {@linkcode LayoutConfiguration} for each {@linkcode TodayCardShelf} in a
+ * {@linkcode TodayPage} in "context", so it can be retrieved at the shelf-component level
+ *
+ * This is necessary because the layout of the cards within each shelf of a {@linkcode TodayPage}
+ * is only knowable given information about the shelves that were rendered before it
+ *
+ * The information about the shelf layout is persisted through the "context" API so that the
+ * rendering of a {@linkcode TodayPage} can defer to the "default" page component, which requires
+ * that we pass no additional arguments into each shelf component
+ *
+ * {@linkcode getTodayCardLayoutConfiguration} can be used to look up the {@linkcode LayoutConfiguration}
+ * stored for a given {@linkcode TodayCardShelf}
+ */
+export function setTodayCardLayoutContext(page: Pick<TodayPage, 'shelves'>) {
+ const store: TodayCardLayoutStore = new WeakMap();
+
+ let shouldStretchFirstCardMultiline = false;
+ let shouldStretchFirstCardInline = false;
+
+ for (const shelf of page.shelves) {
+ // Skip any non-`TodayCard` shelves
+ if (!isTodayCardShelf(shelf)) {
+ continue;
+ }
+
+ store.set(shelf, {
+ wrap: {
+ shouldStretchFirstCard: shouldStretchFirstCardMultiline,
+ },
+ nowrap: {
+ shouldStretchFirstCard: shouldStretchFirstCardInline,
+ },
+ });
+
+ // In the multi-line card configuration, shelves with two or three cards in them will
+ // require that the next shelf swaps to stretching the cards at the opposite end
+ if (shelf.items.length === 2 || shelf.items.length === 3) {
+ shouldStretchFirstCardMultiline = !shouldStretchFirstCardMultiline;
+ }
+
+ // In the "inline" card configuration, each shelf should always alternate which end the
+ // card is stretched on
+ shouldStretchFirstCardInline = !shouldStretchFirstCardInline;
+ }
+
+ setContext<TodayCardLayoutStoreContext>(
+ TODAY_CARD_LAYOUT_CONTEXT_ID,
+ store,
+ );
+}
+
+/**
+ * Retrieve the {@linkcode LayoutConfiguration} for a given {@linkcode TodayCardShelf}
+ */
+export function getTodayCardLayoutConfiguration(
+ shelf: TodayCardShelf,
+): LayoutConfiguration {
+ const todayCardLayout = getContext<TodayCardLayoutStoreContext>(
+ TODAY_CARD_LAYOUT_CONTEXT_ID,
+ );
+
+ return todayCardLayout?.get(shelf) ?? LAYOUT_CONFIGURATION_FALLBACK;
+}