diff options
Diffstat (limited to 'src/context')
| -rw-r--r-- | src/context/accessibility-layout.ts | 93 | ||||
| -rw-r--r-- | src/context/today-card-layout.ts | 98 |
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; +} |
