mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-07-14 18:15:56 -04:00
- Export `isLocaleRTL` from `@sofa/i18n` to detect right-to-left locales - Native: call `I18nManager.allowRTL` / `forceRTL` on locale init and on locale change; reload the app immediately if the current layout direction doesn't match the persisted locale, and prompt the user to restart when switching between LTR and RTL locales - Native: flip the `SettingsRow` chevron to `IconChevronLeft` when `I18nManager.isRTL` is true - Native: add Arabic Intl polyfill locale data (PluralRules, NumberFormat, DateTimeFormat, RelativeTimeFormat) - Web: enable `"rtl": true` in `components.json` and regenerate all shadcn UI components to use CSS logical properties (`ms-*`, `me-*`, `ps-*`, `pe-*`, `text-start`, `text-end`) instead of physical `ml-*`/`mr-*`/`text-left` equivalents - Web: apply the same logical-property conversions to non-generated components (nav-bar, settings sections, title cards, hero banner, continue-watching card) - Web: set `document.dir` based on the active locale's RTL flag in the i18n initialiser and root route - Add `../../packages/i18n/src/po.d.ts` to native `tsconfig.json` includes so TypeScript accepts `.po` imports
97 lines
3.8 KiB
TypeScript
97 lines
3.8 KiB
TypeScript
import { Trans } from "@lingui/react/macro";
|
|
import { IconDeviceTv, IconMovie, IconPlus, IconStar } from "@tabler/icons-react";
|
|
import { Link } from "@tanstack/react-router";
|
|
|
|
interface HeroBannerProps {
|
|
id: string;
|
|
type: "movie" | "tv";
|
|
title: string;
|
|
overview: string;
|
|
backdropPath: string | null;
|
|
voteAverage: number;
|
|
}
|
|
|
|
export function HeroBanner({
|
|
id,
|
|
type,
|
|
title,
|
|
overview,
|
|
backdropPath,
|
|
voteAverage,
|
|
}: HeroBannerProps) {
|
|
return (
|
|
<div className="animate-stagger-item relative -mt-6 mr-[calc(-50vw+50%)] mb-4 ml-[calc(-50vw+50%)] overflow-hidden">
|
|
<div className="relative aspect-[21/9] max-h-[420px] min-h-[280px] w-full">
|
|
{backdropPath ? (
|
|
<img
|
|
src={backdropPath}
|
|
alt={title}
|
|
loading="eager"
|
|
decoding="async"
|
|
className="absolute inset-0 h-full w-full object-cover"
|
|
/>
|
|
) : (
|
|
<div className="from-card via-secondary to-muted h-full w-full bg-gradient-to-br" />
|
|
)}
|
|
|
|
{/* Gradient overlays */}
|
|
<div className="from-background via-background/60 absolute inset-0 bg-gradient-to-t to-transparent" />
|
|
<div className="from-background/80 absolute inset-0 bg-gradient-to-r via-transparent to-transparent" />
|
|
|
|
{/* Content */}
|
|
<div className="absolute inset-0 flex items-end">
|
|
<div className="w-full pb-8">
|
|
<div className="mx-auto max-w-6xl pr-[max(1rem,env(safe-area-inset-right))] pl-[max(1rem,env(safe-area-inset-left))] sm:pr-[max(1.5rem,env(safe-area-inset-right))] sm:pl-[max(1.5rem,env(safe-area-inset-left))]">
|
|
<div
|
|
className="animate-stagger-item"
|
|
style={{ "--stagger-index": 3 } as React.CSSProperties}
|
|
>
|
|
<div className="mb-3 flex items-center gap-2">
|
|
<span className="bg-primary/10 text-primary inline-flex cursor-default items-center justify-center gap-1 rounded px-1.5 py-1 text-xs font-medium">
|
|
{type === "movie" ? (
|
|
<>
|
|
<IconMovie aria-hidden className="size-3.5" />
|
|
<Trans>Movie</Trans>
|
|
</>
|
|
) : (
|
|
<>
|
|
<IconDeviceTv aria-hidden className="size-3.5" />
|
|
<Trans>TV</Trans>
|
|
</>
|
|
)}
|
|
</span>
|
|
{voteAverage > 0 && (
|
|
<span className="text-primary flex items-center gap-1 text-sm">
|
|
<IconStar aria-hidden={true} className="fill-primary size-3.5" />
|
|
{voteAverage.toFixed(1)}
|
|
</span>
|
|
)}
|
|
<span className="text-muted-foreground text-xs">
|
|
<Trans>Trending today</Trans>
|
|
</span>
|
|
</div>
|
|
<Link to="/titles/$id" params={{ id }} className="group/title text-start">
|
|
<h2 className="font-display group-hover/title:text-primary text-3xl tracking-tight text-balance transition-colors sm:text-4xl">
|
|
{title}
|
|
</h2>
|
|
</Link>
|
|
<p className="text-muted-foreground mt-2 line-clamp-2 max-w-2xl text-sm">
|
|
{overview}
|
|
</p>
|
|
<Link
|
|
to="/titles/$id"
|
|
params={{ id }}
|
|
className="bg-primary text-primary-foreground hover:shadow-primary/20 mt-4 inline-flex h-9 items-center gap-2 rounded-lg px-4 text-sm font-medium transition-shadow hover:shadow-md"
|
|
>
|
|
<IconPlus aria-hidden={true} className="size-4" />
|
|
<Trans>Add to Library</Trans>
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|