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:
simoleo89
2026-05-03 22:00:33 +02:00
parent 92e9bb19cd
commit 72bc4da3c0
7 changed files with 697 additions and 12 deletions
@@ -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>
)}