mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: dialog
This commit is contained in:
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() {
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in new issue
Block a user