feat: dialog

This commit is contained in:
TrèsAbhi committed 2025-05-30 20:30:16 -05:00
1 parent 3aafe4d9f1
commit 8373fb1b7a
5 files changed
+53 -22

No files matched your search

@@ -43,12 +43,16 @@ export class CatalogItemAccessor {
return this; return this;
} }
get<Type>(Message: MessageFns<Type>, component: string) { getOptional<Type>(Message: MessageFns<Type>, component: string) {
if (component in this.components) { if (component in this.components) {
return Message.decode(this.components[component].value); return Message.decode(this.components[component].value);
} }
}
throw new Error(`Unknown component: ${component}`); get<Type>(Message: MessageFns<Type>, component: string) {
const value = this.getOptional(Message, component);
if (value === undefined) throw new Error(`Unknown component: ${component}`);
return value;
} }
pack() { pack() {
+1 -1
View File
@@ -1260,7 +1260,7 @@
"seo_description": "All World of Tanks Blitz player profile avatars", "seo_description": "All World of Tanks Blitz player profile avatars",
"description": "Player profile avatars", "description": "Player profile avatars",
"unnamed_avatar": "Unnamed avatar", "unnamed_avatars": "Unnamed avatars",
"search": { "search": {
"hint": "Search avatars...", "hint": "Search avatars...",
"results": "%s1 avatars" "results": "%s1 avatars"
@@ -1,29 +1,48 @@
import type { ProfileAvatarComponent } from '@protos/blitz_static_profile_avatar_component'; import type { ProfileAvatarComponent } from '@protos/blitz_static_profile_avatar_component';
import type { SellableComponent } from '@protos/blitz_static_sellable_component'; import type { SellableComponent } from '@protos/blitz_static_sellable_component';
import type { StuffUIComponent } from '@protos/blitz_static_stuff_ui_component'; import type { StuffUIComponent } from '@protos/blitz_static_stuff_ui_component';
import { Badge, Box, Flex, Text } from '@radix-ui/themes'; import {
Badge,
Box,
Dialog,
Flex,
Text,
type FlexProps,
} from '@radix-ui/themes';
import { literals } from 'packages/i18n/src'; import { literals } from 'packages/i18n/src';
import { Var } from '../../core/radix/var'; import { Var } from '../../core/radix/var';
import { useLocale } from '../../hooks/useLocale'; import { useLocale } from '../../hooks/useLocale';
interface AvatarGroupProps { interface AvatarGroupProps {
name: string; name?: string;
avatars: { avatars: {
stuff: StuffUIComponent; stuff: StuffUIComponent;
avatar: ProfileAvatarComponent; avatar: ProfileAvatarComponent;
sellable: SellableComponent; sellable?: SellableComponent;
}[]; }[];
} }
const dx = '4px'; const dx = '4px';
const MAX_AVATARS_PREVIEW = 5; const MAX_AVATARS_PREVIEW = 5;
export function AvatarGroup({ avatars, name }: AvatarGroupProps) { export function AvatarGroup({ name, avatars }: AvatarGroupProps) {
return (
<Dialog.Root>
<Dialog.Trigger>
<Content avatars={avatars} name={name} />
</Dialog.Trigger>
<Dialog.Content>asd</Dialog.Content>
</Dialog.Root>
);
}
function Content({ avatars, name, ...props }: FlexProps & AvatarGroupProps) {
const { strings } = useLocale(); const { strings } = useLocale();
const avatarsReduced = [...avatars].reverse().slice(0, MAX_AVATARS_PREVIEW); const avatarsReduced = [...avatars].reverse().slice(0, MAX_AVATARS_PREVIEW);
return ( return (
<Flex direction="column" gap="3"> <Flex direction="column" gap="3" style={{ cursor: 'pointer' }} {...props}>
<Box width="100%" style={{ aspectRatio: '7 / 8' }} position="relative"> <Box width="100%" style={{ aspectRatio: '7 / 8' }} position="relative">
{avatarsReduced.map((avatar, index) => ( {avatarsReduced.map((avatar, index) => (
<Box <Box
@@ -35,7 +54,7 @@ export function AvatarGroup({ avatars, name }: AvatarGroupProps) {
style={{ style={{
filter: `brightness(${(index + 1) / avatarsReduced.length})`, filter: `brightness(${(index + 1) / avatarsReduced.length})`,
borderRadius: Var('radius-2'), borderRadius: Var('radius-2'),
boxShadow: Var('shadow-3'), boxShadow: Var('shadow-2'),
backgroundImage: `url(${avatar.avatar.avatar})`, backgroundImage: `url(${avatar.avatar.avatar})`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
@@ -69,9 +88,10 @@ export function AvatarGroup({ avatars, name }: AvatarGroupProps) {
style={{ style={{
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
fontStyle: name === undefined ? 'italic' : undefined,
}} }}
> >
{name} {name ?? strings.website.tools.avatars.unnamed_avatars}
</Text> </Text>
</Flex> </Flex>
); );
@@ -41,30 +41,37 @@ function Content() {
ProfileAvatarComponent, ProfileAvatarComponent,
'profileAvatarComponent', 'profileAvatarComponent',
); );
const sellable = item.get(SellableComponent, 'sellableComponent'); const sellable = item.getOptional(
const name = SellableComponent,
gameStrings[stuff.display_name] ?? 'sellableComponent',
strings.website.tools.avatars.unnamed_avatar; // maybe move to dumb component? );
const name = gameStrings[stuff.display_name] as string | undefined;
return { avatar, name, stuff, sellable }; return { avatar, name, stuff, sellable };
}) })
.sort((a, b) => a.stuff.grade - b.stuff.grade) .sort((a, b) => a.stuff.grade - b.stuff.grade)
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) =>
const groups: Record<string, typeof avatars> = {}; a.name === undefined
? 1
: b.name === undefined
? -1
: a.name.localeCompare(b.name),
);
const groups = new Map<string | undefined, typeof avatars>();
for (const avatar of avatars) { for (const avatar of avatars) {
if (avatar.name in groups) { if (groups.has(avatar.name)) {
groups[avatar.name].push(avatar); groups.get(avatar.name)!.push(avatar);
} else { } else {
groups[avatar.name] = [avatar]; groups.set(avatar.name, [avatar]);
} }
} }
return Object.entries(groups); return [...groups];
}, []); }, []);
return ( return (
<PageWrapper> <PageWrapper color="gold">
<Grid <Grid
columns={{ columns={{
initial: 'repeat(auto-fill, minmax(6rem, 1fr))', initial: 'repeat(auto-fill, minmax(6rem, 1fr))',
@@ -13,6 +13,6 @@ const gameStrings = await metadata.stringsPartial(locale, [
]); ]);
--- ---
<ToolLayout tool={avatarsTool} {locale}> <ToolLayout tool={avatarsTool} {locale} >
<Page localeData={{ locale, gameStrings }} client:load /> <Page localeData={{ locale, gameStrings }} client:load />
</ToolLayout> </ToolLayout>