init commit

This commit is contained in:
rxliuli
2025-11-04 05:03:50 +08:00
commit bce557cc2d
1396 changed files with 172991 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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;
}