accessibility-layout.ts
1 import { getContext, setContext } from 'svelte'; 2 3 import type { Shelf } from '@jet-app/app-store/api/models'; 4 import { isAccessibilityHeaderShelf } from '~/components/jet/shelf/AccessibilityHeaderShelf.svelte'; 5 import { isAccessibilityFeaturesShelf } from '~/components/jet/shelf/AccessibilityFeaturesShelf.svelte'; 6 import { isAccessibilityDeveloperLinkShelf } from '~/components/jet/shelf/AccessibilityDeveloperLinkShelf.svelte'; 7 8 /** 9 * Describes the layout configuration for accessibility shelves 10 */ 11 interface AccessibilityLayoutConfiguration { 12 withBottomPadding: boolean; 13 } 14 15 const ACCESSIBILITY_LAYOUT_FALLBACK: AccessibilityLayoutConfiguration = 16 Object.freeze({ 17 withBottomPadding: false, 18 }); 19 20 type AccessibilityLayoutStore = WeakMap< 21 Shelf, 22 AccessibilityLayoutConfiguration 23 >; 24 type AccessibilityLayoutStoreContext = AccessibilityLayoutStore | undefined; 25 26 const ACCESSIBILITY_LAYOUT_CONTEXT_ID = 'accessibility-layout-context'; 27 28 /** 29 * Check if a shelf is accessibility-related 30 */ 31 function isAccessibilityRelated(shelf: Shelf): boolean { 32 return ( 33 shelf.contentType === 'accessibilityParagraph' || 34 shelf.contentType === 'accessibilityFeatures' 35 ); 36 } 37 38 /** 39 * Check if a shelf is one of the target accessibility shelves 40 */ 41 function isTargetAccessibilityShelf(shelf: Shelf): boolean { 42 return ( 43 isAccessibilityHeaderShelf(shelf) || 44 isAccessibilityFeaturesShelf(shelf) || 45 isAccessibilityDeveloperLinkShelf(shelf) 46 ); 47 } 48 49 /** 50 * Store the {@linkcode AccessibilityLayoutConfiguration} for each accessibility shelf 51 * in "context", so it can be retrieved at the shelf-component level 52 * 53 * This determines bottom padding based on whether the next shelf is accessibility-related 54 */ 55 export function setAccessibilityLayoutContext(page: { shelves: Shelf[] }) { 56 const store: AccessibilityLayoutStore = new WeakMap(); 57 58 for (let i = 0; i < page.shelves.length; i++) { 59 const shelf = page.shelves[i]; 60 61 // Only process target accessibility shelves 62 if (!isTargetAccessibilityShelf(shelf)) { 63 continue; 64 } 65 66 // Check if the next shelf is accessibility-related 67 const nextShelf = page.shelves[i + 1]; 68 const hasAccessibilityNext = 69 nextShelf && isAccessibilityRelated(nextShelf); 70 71 store.set(shelf, { 72 withBottomPadding: !hasAccessibilityNext, 73 }); 74 } 75 76 setContext<AccessibilityLayoutStoreContext>( 77 ACCESSIBILITY_LAYOUT_CONTEXT_ID, 78 store, 79 ); 80 } 81 82 /** 83 * Retrieve the {@linkcode AccessibilityLayoutConfiguration} for a given accessibility shelf 84 */ 85 export function getAccessibilityLayoutConfiguration( 86 shelf: Shelf, 87 ): AccessibilityLayoutConfiguration { 88 const accessibilityLayout = getContext<AccessibilityLayoutStoreContext>( 89 ACCESSIBILITY_LAYOUT_CONTEXT_ID, 90 ); 91 92 return accessibilityLayout?.get(shelf) ?? ACCESSIBILITY_LAYOUT_FALLBACK; 93 }