mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
feat(profile): add full-box card background tab and rendering
Adds a "Cards" tab to the Profile Background picker (BackgroundsView)
that selects a pattern applied to the entire user info card and the
extended profile container, in addition to the existing avatar-pad
background/stand/overlay layers.
- AvatarInfoUser/Utilities: propagate cardBackgroundId from RoomUserData.
- InfoStandWidgetUserView: stateful cardBackgroundId, applied as
.profile-card-background.card-background-{id} on the outer Column
with bg-color suppressed when active.
- UserContainerView: same class on the wrapper of the extended profile.
- BackgroundsView: 4th tab "cards" backed by cards.data config
(falls back to backgrounds.data); sends 4-id message via the
extended sendBackgroundMessage signature.
- ui-config.example: cards.data dataset (15 entries).
- BackgroundsView.css: 188 .card-background-{N} rules cloned from
background-{N} (repeat-tiled) plus 15 CSS-pattern overrides for the
provisional dataset (gradients, stripes, dots, grid, checker).
This commit is contained in:
@@ -20,9 +20,11 @@ interface BackgroundsViewProps {
|
||||
setSelectedStand: Dispatch<SetStateAction<number>>;
|
||||
selectedOverlay: number;
|
||||
setSelectedOverlay: Dispatch<SetStateAction<number>>;
|
||||
selectedCardBackground: number;
|
||||
setSelectedCardBackground: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
const TABS = ['backgrounds', 'stands', 'overlays'] as const;
|
||||
const TABS = ['backgrounds', 'stands', 'overlays', 'cards'] as const;
|
||||
type TabType = typeof TABS[number];
|
||||
|
||||
export const BackgroundsView: FC<BackgroundsViewProps> = ({
|
||||
@@ -32,7 +34,9 @@ export const BackgroundsView: FC<BackgroundsViewProps> = ({
|
||||
selectedStand,
|
||||
setSelectedStand,
|
||||
selectedOverlay,
|
||||
setSelectedOverlay
|
||||
setSelectedOverlay,
|
||||
selectedCardBackground,
|
||||
setSelectedCardBackground
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState<TabType>('backgrounds');
|
||||
const { roomSession } = useRoom();
|
||||
@@ -58,20 +62,21 @@ export const BackgroundsView: FC<BackgroundsViewProps> = ({
|
||||
const allData = useMemo(() => ({
|
||||
backgrounds: processData(GetConfigurationValue('backgrounds.data'), 'background'),
|
||||
stands: processData(GetConfigurationValue('stands.data'), 'stand'),
|
||||
overlays: processData(GetConfigurationValue('overlays.data'), 'overlay')
|
||||
overlays: processData(GetConfigurationValue('overlays.data'), 'overlay'),
|
||||
cards: processData(GetConfigurationValue('cards.data') || GetConfigurationValue('backgrounds.data'), 'background')
|
||||
}), [processData]);
|
||||
|
||||
const handleSelection = useCallback((id: number) => {
|
||||
if (!roomSession) return;
|
||||
|
||||
const setters = { backgrounds: setSelectedBackground, stands: setSelectedStand, overlays: setSelectedOverlay };
|
||||
|
||||
const currentValues = { backgrounds: selectedBackground, stands: selectedStand, overlays: selectedOverlay };
|
||||
const setters = { backgrounds: setSelectedBackground, stands: setSelectedStand, overlays: setSelectedOverlay, cards: setSelectedCardBackground };
|
||||
|
||||
const currentValues = { backgrounds: selectedBackground, stands: selectedStand, overlays: selectedOverlay, cards: selectedCardBackground };
|
||||
|
||||
setters[activeTab](id);
|
||||
const newValues = { ...currentValues, [activeTab]: id };
|
||||
roomSession.sendBackgroundMessage( newValues.backgrounds, newValues.stands, newValues.overlays );
|
||||
}, [activeTab, roomSession, selectedBackground, selectedStand, selectedOverlay, setSelectedBackground, setSelectedStand, setSelectedOverlay]);
|
||||
roomSession.sendBackgroundMessage( newValues.backgrounds, newValues.stands, newValues.overlays, newValues.cards );
|
||||
}, [activeTab, roomSession, selectedBackground, selectedStand, selectedOverlay, selectedCardBackground, setSelectedBackground, setSelectedStand, setSelectedOverlay, setSelectedCardBackground]);
|
||||
|
||||
const renderItem = useCallback((item: ItemData, type: string) => (
|
||||
<Flex
|
||||
@@ -81,7 +86,10 @@ export const BackgroundsView: FC<BackgroundsViewProps> = ({
|
||||
onClick={() => item.selectable && handleSelection(item.id)}
|
||||
className={item.selectable ? '' : 'non-selectable'}
|
||||
>
|
||||
<Base className={`profile-${type} ${type}-${item.id}`} />
|
||||
<Base
|
||||
className={`profile-${type} ${type}-${item.id}`}
|
||||
style={type === 'card-background' ? { width: 60, height: 80, borderRadius: 4 } : undefined}
|
||||
/>
|
||||
{item.isHcOnly && <LayoutCurrencyIcon position="absolute" className="top-1 inset-e-1" type="hc" />}
|
||||
</Flex>
|
||||
), [handleSelection]);
|
||||
@@ -103,7 +111,7 @@ export const BackgroundsView: FC<BackgroundsViewProps> = ({
|
||||
<NitroCardContentView gap={1}>
|
||||
<Text bold center>Select an Option</Text>
|
||||
<Grid gap={1} columnCount={7} overflow="auto">
|
||||
{allData[activeTab].map(item => renderItem(item, activeTab.slice(0, -1)))}
|
||||
{allData[activeTab].map(item => renderItem(item, activeTab === 'cards' ? 'card-background' : activeTab.slice(0, -1)))}
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
|
||||
@@ -24,12 +24,14 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
||||
const [backgroundId, setBackgroundId] = useState<number>(null);
|
||||
const [standId, setStandId] = useState<number>(null);
|
||||
const [overlayId, setOverlayId] = useState<number>(null);
|
||||
const [cardBackgroundId, setCardBackgroundId] = useState<number>(null);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const { roomSession = null } = useRoom();
|
||||
|
||||
const infostandBackgroundClass = `background-${backgroundId ?? 'default'}`;
|
||||
const infostandStandClass = `stand-${standId ?? 'default'}`;
|
||||
const infostandOverlayClass = `overlay-${overlayId ?? 'default'}`;
|
||||
const infostandCardBackgroundClass = cardBackgroundId ? `card-background-${cardBackgroundId}` : '';
|
||||
|
||||
const handleProfileClick = useCallback(() => { GetUserProfile(avatarInfo.webID); }, [avatarInfo.webID]);
|
||||
|
||||
@@ -91,6 +93,7 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
||||
newValue.backgroundId = event.backgroundId;
|
||||
newValue.standId = event.standId;
|
||||
newValue.overlayId = event.overlayId;
|
||||
newValue.cardBackgroundId = event.cardBackgroundId ?? 0;
|
||||
return newValue;
|
||||
});
|
||||
});
|
||||
@@ -125,6 +128,7 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
||||
setBackgroundId(avatarInfo.backgroundId);
|
||||
setStandId(avatarInfo.standId);
|
||||
setOverlayId(avatarInfo.overlayId);
|
||||
setCardBackgroundId(avatarInfo.cardBackgroundId ?? 0);
|
||||
|
||||
SendMessageComposer(new UserRelationshipsComposer(avatarInfo.webID));
|
||||
|
||||
@@ -135,6 +139,7 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
||||
setBackgroundId(null);
|
||||
setStandId(null);
|
||||
setOverlayId(null);
|
||||
setCardBackgroundId(null);
|
||||
};
|
||||
}, [avatarInfo]);
|
||||
|
||||
@@ -142,7 +147,7 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
||||
|
||||
return (
|
||||
<>
|
||||
<Column className="relative min-w-[190px] max-w-[190px] z-30 pointer-events-auto bg-[rgba(28,28,32,0.95)] [box-shadow:inset_0_5px_#22222799,inset_0_-4px_#12121599] rounded">
|
||||
<Column className={`relative min-w-[190px] max-w-[190px] z-30 pointer-events-auto ${cardBackgroundId ? '' : 'bg-[rgba(28,28,32,0.95)]'} [box-shadow:inset_0_5px_#22222799,inset_0_-4px_#12121599] rounded overflow-hidden profile-card-background ${infostandCardBackgroundClass}`}>
|
||||
<Column className="h-full p-[8px] overflow-auto" gap={1} overflow="visible">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -277,6 +282,8 @@ export const InfoStandWidgetUserView: FC<InfoStandWidgetUserViewProps> = props =
|
||||
setSelectedStand={setStandId}
|
||||
selectedOverlay={overlayId}
|
||||
setSelectedOverlay={setOverlayId}
|
||||
selectedCardBackground={cardBackgroundId}
|
||||
setSelectedCardBackground={setCardBackgroundId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -18,6 +18,7 @@ export const UserContainerView: FC<{
|
||||
const infostandBackgroundClass = `background-${userProfile.backgroundId ?? 'default'}`;
|
||||
const infostandStandClass = `stand-${userProfile.standId ?? 'default'}`;
|
||||
const infostandOverlayClass = `overlay-${userProfile.overlayId ?? 'default'}`;
|
||||
const profileCardBgClass = userProfile.cardBackgroundId ? `card-background-${userProfile.cardBackgroundId}` : '';
|
||||
|
||||
const addFriend = () =>
|
||||
{
|
||||
@@ -32,7 +33,7 @@ export const UserContainerView: FC<{
|
||||
}, [ userProfile ]);
|
||||
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<div className={`flex gap-2 p-2 rounded profile-card-background ${profileCardBgClass}`}>
|
||||
<div className={`flex flex-col justify-center items-center w-[75px] h-[120px] rounded-sm relative overflow-hidden profile-background ${infostandBackgroundClass}`}>
|
||||
<div className={`absolute inset-0 profile-stand ${infostandStandClass}`} />
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ userProfile.figure } />
|
||||
|
||||
Reference in New Issue
Block a user