From b50c47b7448c4070f7ce97990936431118cfa7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Fri, 16 Jan 2026 20:37:11 -0600 Subject: [PATCH] feat: skeleton --- .../src/components/AvatarCard/index.css | 12 +++++ .../src/components/AvatarCard/index.tsx | 49 ++++++++++--------- .../src/components/AvatarsList/index.css | 2 +- .../src/components/AvatarsList/index.tsx | 4 +- .../src/components/BaseStyles.astro | 1 + .../components/IncrementalLoader/index.tsx | 4 +- .../src/components/InlineSkeleton/index.css | 5 ++ .../src/components/InlineSkeleton/index.tsx | 11 +++++ .../src/components/Skeleton/index.css | 17 +++++++ .../src/components/Skeleton/index.tsx | 9 ++++ ...{SkeletonProps.ts => propsWithSkeleton.ts} | 2 +- 11 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 packages/website-ue/src/components/InlineSkeleton/index.css create mode 100644 packages/website-ue/src/components/InlineSkeleton/index.tsx create mode 100644 packages/website-ue/src/components/Skeleton/index.css create mode 100644 packages/website-ue/src/components/Skeleton/index.tsx rename packages/website-ue/src/types/{SkeletonProps.ts => propsWithSkeleton.ts} (60%) diff --git a/packages/website-ue/src/components/AvatarCard/index.css b/packages/website-ue/src/components/AvatarCard/index.css index 8d531a608..e2fc6e3f7 100644 --- a/packages/website-ue/src/components/AvatarCard/index.css +++ b/packages/website-ue/src/components/AvatarCard/index.css @@ -17,6 +17,13 @@ background-size: cover; background-position: center; + + &.skeleton { + top: 0; + left: 0; + width: 100%; + height: 100%; + } } .count { @@ -31,5 +38,10 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + width: 100%; + + .skeleton { + width: 100%; + } } } diff --git a/packages/website-ue/src/components/AvatarCard/index.tsx b/packages/website-ue/src/components/AvatarCard/index.tsx index 2ca6bbde0..b6bf3c824 100644 --- a/packages/website-ue/src/components/AvatarCard/index.tsx +++ b/packages/website-ue/src/components/AvatarCard/index.tsx @@ -1,8 +1,10 @@ import { literals } from "@blitzkit/i18n"; import { useStrings } from "../../hooks/useStrings"; import type { Avatar } from "../../protos/avatar"; -import type { SkeletonProps } from "../../types/SkeletonProps"; +import type { PropsWithSkeleton } from "../../types/propsWithSkeleton"; import { Badge } from "../Badge"; +import { InlineSkeleton } from "../InlineSkeleton"; +import { Skeleton } from "../Skeleton"; import { Text } from "../Text"; import "./index.css"; @@ -14,13 +16,13 @@ interface Props { const MAX_CARDS = 3; const CARD_TRIM = "var(--space-3)"; -export function AvatarCard(props: SkeletonProps) { +export function AvatarCard(props: PropsWithSkeleton) { return (
- {!props.skeleton && } + - {props.skeleton ? "LOADING" : props.name} + {props.skeleton ? : props.name}
); @@ -30,32 +32,35 @@ interface SeriesProps { avatars: Avatar[]; } -function Series({ avatars }: SeriesProps) { - const count = Math.min(avatars.length, MAX_CARDS); +function Series(props: PropsWithSkeleton) { + const count = Math.min(props.skeleton ? 0 : props.avatars.length, MAX_CARDS); const strings = useStrings(); return (
- {avatars.slice(-MAX_CARDS).map((avatar, index) => ( -
- ))} + {!props.skeleton && + props.avatars.slice(-MAX_CARDS).map((avatar, index) => ( +
+ ))} - {avatars.length > 1 && ( + {!props.skeleton && props.avatars.length > 1 && ( - {literals(strings.units.x, { value: avatars.length })} + {literals(strings.units.x, { value: props.avatars.length })} )} + + {props.skeleton && }
); } diff --git a/packages/website-ue/src/components/AvatarsList/index.css b/packages/website-ue/src/components/AvatarsList/index.css index ec63672bc..29b34f401 100644 --- a/packages/website-ue/src/components/AvatarsList/index.css +++ b/packages/website-ue/src/components/AvatarsList/index.css @@ -1,5 +1,5 @@ .avatars-list { display: grid; - grid-template-columns: repeat(auto-fill, minmax(var(--space-7), 1fr)); + grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr)); gap: var(--space-5) var(--space-4); } diff --git a/packages/website-ue/src/components/AvatarsList/index.tsx b/packages/website-ue/src/components/AvatarsList/index.tsx index 75074338a..77a332faf 100644 --- a/packages/website-ue/src/components/AvatarsList/index.tsx +++ b/packages/website-ue/src/components/AvatarsList/index.tsx @@ -75,8 +75,8 @@ export function AvatarsList() { return (
diff --git a/packages/website-ue/src/components/BaseStyles.astro b/packages/website-ue/src/components/BaseStyles.astro index 04f3e0a22..839d6a103 100644 --- a/packages/website-ue/src/components/BaseStyles.astro +++ b/packages/website-ue/src/components/BaseStyles.astro @@ -80,5 +80,6 @@ import "@fontsource-variable/roboto-flex"; --blur-tint: blur(8rem); --duration-brief: 200ms; + --duration-subtle: 2s; } diff --git a/packages/website-ue/src/components/IncrementalLoader/index.tsx b/packages/website-ue/src/components/IncrementalLoader/index.tsx index 6696bd0fd..ecda105be 100644 --- a/packages/website-ue/src/components/IncrementalLoader/index.tsx +++ b/packages/website-ue/src/components/IncrementalLoader/index.tsx @@ -1,6 +1,6 @@ import { times } from "lodash-es"; import { useEffect, useRef, useState, type ReactNode } from "react"; -import type { SkeletonProps } from "../../types/SkeletonProps"; +import type { PropsWithSkeleton } from "../../types/propsWithSkeleton"; import "./index.css"; export interface IncrementalLoaderProps { @@ -10,7 +10,7 @@ export interface IncrementalLoaderProps { skeleton?: boolean; data: Props[]; - Component: (props: SkeletonProps>) => ReactNode; + Component: (props: PropsWithSkeleton>) => ReactNode; } export function IncrementalLoader({ diff --git a/packages/website-ue/src/components/InlineSkeleton/index.css b/packages/website-ue/src/components/InlineSkeleton/index.css new file mode 100644 index 000000000..5ff0238b9 --- /dev/null +++ b/packages/website-ue/src/components/InlineSkeleton/index.css @@ -0,0 +1,5 @@ +.inline-skeleton { + display: inline-block; + + height: 1em; +} diff --git a/packages/website-ue/src/components/InlineSkeleton/index.tsx b/packages/website-ue/src/components/InlineSkeleton/index.tsx new file mode 100644 index 000000000..d911b170d --- /dev/null +++ b/packages/website-ue/src/components/InlineSkeleton/index.tsx @@ -0,0 +1,11 @@ +import { classNames } from "../../core/ui/classNames"; +import { Skeleton, type SkeletonProps } from "../Skeleton"; +import "./index.css"; + +export interface InlineSkeletonProps extends SkeletonProps {} + +export function InlineSkeleton({ className, ...props }: InlineSkeletonProps) { + return ( + + ); +} diff --git a/packages/website-ue/src/components/Skeleton/index.css b/packages/website-ue/src/components/Skeleton/index.css new file mode 100644 index 000000000..4596ff8fd --- /dev/null +++ b/packages/website-ue/src/components/Skeleton/index.css @@ -0,0 +1,17 @@ +@keyframes skeleton-animation { + 0% { + background-color: var(--black-a8); + } + 50% { + background-color: var(--black-a5); + } + 100% { + background-color: var(--black-a8); + } +} + +.skeleton { + animation: skeleton-animation var(--duration-subtle) ease infinite; + + border-radius: var(--space-2); +} diff --git a/packages/website-ue/src/components/Skeleton/index.tsx b/packages/website-ue/src/components/Skeleton/index.tsx new file mode 100644 index 000000000..b8ec3e726 --- /dev/null +++ b/packages/website-ue/src/components/Skeleton/index.tsx @@ -0,0 +1,9 @@ +import type { ComponentProps } from "react"; +import { classNames } from "../../core/ui/classNames"; +import "./index.css"; + +export interface SkeletonProps extends ComponentProps<"div"> {} + +export function Skeleton({ className, ...props }: SkeletonProps) { + return
; +} diff --git a/packages/website-ue/src/types/SkeletonProps.ts b/packages/website-ue/src/types/propsWithSkeleton.ts similarity index 60% rename from packages/website-ue/src/types/SkeletonProps.ts rename to packages/website-ue/src/types/propsWithSkeleton.ts index 466e9ce50..c367c9c52 100644 --- a/packages/website-ue/src/types/SkeletonProps.ts +++ b/packages/website-ue/src/types/propsWithSkeleton.ts @@ -1,3 +1,3 @@ -export type SkeletonProps = +export type PropsWithSkeleton = | { skeleton: true } | (Props & { skeleton?: false });