mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
toolbar: always-mount nav rows + drive show/hide via framer variants
Replace the outer AnimatePresence wrapper around the four toolbar rows
(desktop backplate, left-nav, right-nav, mobile-nav) with always-mounted
motion.div elements driven by an isVisible-derived variant string
('visible' or 'hidden'). This eliminates the spam-toggle bug: rapid
clicks on the show/hide chevron previously left motion children in
inconsistent intermediate states (stuck opacity 0, phantom scale 0.8)
because AnimatePresence + Fragment + multiple keyed children breaks
when enter/exit cycles overlap. With variants, framer-motion's spring
solver picks up from the current animated value on each retarget, so
spam-clicking just settles smoothly toward whichever target is current.
Refactor details:
- containerVariants drops its 'exit' state (now lives in 'hidden').
- itemVariants drops 'exit' as well — animation target is the same as
hidden, and exit doesn't apply without AnimatePresence.
- New shellVariants for the backplate.
- pointer-events is animated per-variant ('auto' visible / 'none'
hidden) instead of pinned via a Tailwind class, so the hidden rows
don't intercept clicks.
- Wrapper variants are computed inside the component because
leftNavVariants.hidden depends on isInRoom (the nav slides in from
the side in-room, from the bottom otherwise).
- Variant inheritance: outer wrapper drives 'visible'/'hidden';
inner container (containerVariants) and items (itemVariants) inherit
via framer's variant propagation, so stagger runs in both directions
without needing AnimatePresence.
- Inner AnimatePresence around the Me popover stays — it has a single
keyed child with a clean conditional and doesn't suffer from the
Fragment-wrapping issue.
Cleanups while here:
- Dropped hasDesktopUnifiedShell: always equal to isToolbarOpen inside
the isInRoom-gated block, so the ternary was always picking one
branch. Inlined.
- Dropped showDesktopShell: same redundancy inside the (now removed)
AnimatePresence. The 'else' branch of its ternary was dead code.
- Extracted spring transition constants (SHELL_TRANSITION,
NAV_TRANSITION, ME_POPOVER_TRANSITION) so they're declared once.
- Removed pointer-events-auto from wrapper className strings where
the variant now owns it (mobile-nav, left-nav, right-nav).
Behaviour: identical to before for a single click cycle (open → close
animates with the same spring). The previously broken spam-click path
now settles cleanly. Tests still 193/193, typecheck 0 errors, prod
build unchanged.
This commit is contained in:
@@ -8,18 +8,33 @@ import { ToolbarItemView } from './ToolbarItemView';
|
||||
import { ToolbarMeView } from './ToolbarMeView';
|
||||
import { YouTubePlayerView } from './YouTubePlayerView';
|
||||
|
||||
// The 4 nav rows + backplate are ALWAYS mounted and animate between
|
||||
// hidden/visible via framer-motion variants. Rapid show/hide toggles
|
||||
// just retarget the in-flight spring instead of interrupting an
|
||||
// AnimatePresence enter/exit cycle, so the pre-refactor artifacts
|
||||
// (icons stuck at opacity 0 or scale 0.8 after spam-clicking the
|
||||
// toggle) can no longer happen — framer's spring solver picks up from
|
||||
// whatever the current animated value is.
|
||||
|
||||
const containerVariants: Variants = {
|
||||
hidden: {},
|
||||
visible: { transition: { staggerChildren: 0.05 } },
|
||||
exit: { transition: { staggerChildren: 0.03, staggerDirection: -1 } }
|
||||
hidden: { transition: { staggerChildren: 0.03, staggerDirection: -1 } },
|
||||
visible: { transition: { staggerChildren: 0.05 } }
|
||||
};
|
||||
|
||||
const itemVariants: Variants = {
|
||||
hidden: { opacity: 0, y: 10, scale: 0.8 },
|
||||
visible: { opacity: 1, y: 0, scale: 1, transition: { type: 'spring', stiffness: 400, damping: 22 } },
|
||||
exit: { opacity: 0, y: 6, scale: 0.85, transition: { duration: 0.1 } }
|
||||
visible: { opacity: 1, y: 0, scale: 1, transition: { type: 'spring', stiffness: 400, damping: 22 } }
|
||||
};
|
||||
|
||||
const shellVariants: Variants = {
|
||||
hidden: { opacity: 0, y: 8 },
|
||||
visible: { opacity: 1, y: 0 }
|
||||
};
|
||||
|
||||
const SHELL_TRANSITION = { type: 'spring' as const, stiffness: 260, damping: 26 };
|
||||
const NAV_TRANSITION = { type: 'spring' as const, stiffness: 300, damping: 28 };
|
||||
const ME_POPOVER_TRANSITION = { type: 'spring' as const, stiffness: 420, damping: 28 };
|
||||
|
||||
export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
|
||||
{
|
||||
const { isInRoom } = props;
|
||||
@@ -35,8 +50,9 @@ export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
|
||||
const { iconState = MessengerIconState.HIDDEN } = useMessenger();
|
||||
const { openMonitor, showToolbarButton } = useWiredTools();
|
||||
const isMod = GetSessionDataManager().isModerator;
|
||||
const hasDesktopUnifiedShell = (isInRoom && isToolbarOpen);
|
||||
const showDesktopShell = (isToolbarOpen || !isInRoom);
|
||||
const isVisible = (isToolbarOpen || !isInRoom);
|
||||
const visibilityVariant = isVisible ? 'visible' : 'hidden';
|
||||
|
||||
const desktopToolbarFrameClasses = isTouchLayout ? '' : 'md:left-1/2 md:right-auto md:h-[52px] md:w-[420px] md:-translate-x-1/2 md:items-center md:px-[6px] md:py-[4px] lg:w-[460px]';
|
||||
const desktopToolbarOpenClasses = isTouchLayout ? '' : 'md:rounded-none md:border-0 md:bg-transparent md:shadow-none';
|
||||
const desktopToggleClasses = isTouchLayout ? '' : 'md:mb-0';
|
||||
@@ -46,6 +62,22 @@ export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
|
||||
const desktopBlockClasses = isTouchLayout ? 'hidden' : 'hidden md:block';
|
||||
const desktopFlexClasses = isTouchLayout ? 'hidden' : 'hidden md:flex';
|
||||
|
||||
// Compute the wrapper variants. `isInRoom` affects the hidden-state
|
||||
// offset (the nav slides in from the side when in a room, from the
|
||||
// bottom otherwise) so the variant object is derived from props.
|
||||
const leftNavVariants: Variants = {
|
||||
hidden: { opacity: 0, x: isInRoom ? -10 : 0, y: isInRoom ? 0 : 8, pointerEvents: 'none' },
|
||||
visible: { opacity: 1, x: 0, y: 0, pointerEvents: 'auto' }
|
||||
};
|
||||
const rightNavVariants: Variants = {
|
||||
hidden: { opacity: 0, x: 10, pointerEvents: 'none' },
|
||||
visible: { opacity: 1, x: 0, pointerEvents: 'auto' }
|
||||
};
|
||||
const mobileNavVariants: Variants = {
|
||||
hidden: { opacity: 0, y: 8, pointerEvents: 'none' },
|
||||
visible: { opacity: 1, y: 0, pointerEvents: 'auto' }
|
||||
};
|
||||
|
||||
useMessageEvent<YouTubeRoomSettingsEvent>(YouTubeRoomSettingsEvent, event =>
|
||||
{
|
||||
const enabled = event.getParser().youtubeEnabled;
|
||||
@@ -123,7 +155,7 @@ export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
|
||||
{ youtubeEnabled && <YouTubePlayerView /> }
|
||||
|
||||
{ isInRoom &&
|
||||
<div className={ `fixed bottom-0 left-0 right-0 z-40 flex h-[52px] items-end px-0 pt-[2px] pb-0 pointer-events-none ${ desktopToolbarFrameClasses } ${ isToolbarOpen ? (hasDesktopUnifiedShell ? `${ desktopToolbarOpenClasses } rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] shadow-[0_-6px_18px_rgba(0,0,0,0.18)]` : 'rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] shadow-[0_-6px_18px_rgba(0,0,0,0.18)]') : `border-0 bg-transparent shadow-none ${ desktopToolbarOpenClasses }` }` }>
|
||||
<div className={ `fixed bottom-0 left-0 right-0 z-40 flex h-[52px] items-end px-0 pt-[2px] pb-0 pointer-events-none ${ desktopToolbarFrameClasses } ${ isToolbarOpen ? `${ desktopToolbarOpenClasses } rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] shadow-[0_-6px_18px_rgba(0,0,0,0.18)]` : `border-0 bg-transparent shadow-none ${ desktopToolbarOpenClasses }` }` }>
|
||||
<motion.div
|
||||
className={ `tb-toggle pointer-events-auto mr-2 mb-[4px] flex-shrink-0 ${ desktopToggleClasses }` }
|
||||
onClick={ () => setIsToolbarOpen(value => !value) }
|
||||
@@ -148,199 +180,211 @@ export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
|
||||
</div>
|
||||
</div> }
|
||||
|
||||
<AnimatePresence>
|
||||
{ (isToolbarOpen || !isInRoom) &&
|
||||
<>
|
||||
{ showDesktopShell &&
|
||||
{ /* Desktop backplate. Always mounted; opacity-driven. */ }
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate={ visibilityVariant }
|
||||
variants={ shellVariants }
|
||||
transition={ SHELL_TRANSITION }
|
||||
className={ `pointer-events-none fixed bottom-0 left-0 right-0 z-[39] h-[52px] rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] shadow-[0_-6px_18px_rgba(0,0,0,0.18)] ${ desktopBlockClasses }` } />
|
||||
|
||||
{ /* Left nav — desktop. Container variant inheritance staggers items in/out. */ }
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate={ visibilityVariant }
|
||||
variants={ leftNavVariants }
|
||||
transition={ NAV_TRANSITION }
|
||||
className={ `fixed bottom-0 left-0 z-40 h-[52px] max-w-[calc(50vw-242px)] items-center overflow-visible pl-3 ${ desktopFlexClasses }` }>
|
||||
<motion.div
|
||||
variants={ containerVariants }
|
||||
className="tb-open-shell flex h-[52px] max-w-full items-center gap-2 overflow-visible bg-transparent px-[8px] pt-[10px] pb-[2px]">
|
||||
<motion.div variants={ itemVariants }>
|
||||
{ isInRoom
|
||||
? <ToolbarItemView icon="habbo" onClick={ () => VisitDesktop() } className="tb-icon" />
|
||||
: <ToolbarItemView icon="house" onClick={ () => CreateLinkEvent('navigator/goto/home') } className="tb-icon" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="rooms" onClick={ () => CreateLinkEvent('navigator/toggle') } className="tb-icon" />
|
||||
</motion.div>
|
||||
{ GetConfigurationValue('game.center.enabled') &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="game" onClick={ () => CreateLinkEvent('games/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="catalog" onClick={ () => CreateLinkEvent('catalog/toggle/normal') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="buildersclub" onClick={ () => CreateLinkEvent('catalog/toggle/builder') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="inventory" onClick={ () => CreateLinkEvent('inventory/toggle') } className="tb-icon" />
|
||||
{ (getFullCount > 0) &&
|
||||
<LayoutItemCountView count={ getFullCount } className="absolute -right-1 top-0" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<AnimatePresence>
|
||||
{ isMeExpanded &&
|
||||
<motion.div
|
||||
initial={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
animate={ { opacity: 1, y: 0, scale: 1 } }
|
||||
exit={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
transition={ ME_POPOVER_TRANSITION }
|
||||
className="pointer-events-auto absolute bottom-[calc(100%+8px)] left-1/2 z-50 -translate-x-1/2">
|
||||
<ToolbarMeView setMeExpanded={ setMeExpanded } unseenAchievementCount={ getTotalUnseen } useGuideTool={ useGuideTool } />
|
||||
</motion.div> }
|
||||
</AnimatePresence>
|
||||
<motion.div
|
||||
className="cursor-pointer"
|
||||
whileHover={ { scale: 1.08 } }
|
||||
whileTap={ { scale: 0.95 } }
|
||||
onClick={ event =>
|
||||
{
|
||||
setMeExpanded(value => !value);
|
||||
event.stopPropagation();
|
||||
} }>
|
||||
<LayoutAvatarImageView headOnly={ true } direction={ 2 } figure={ userFigure } className="tb-icon !h-[44px] !w-[32px] !bg-center !bg-no-repeat" style={ { marginTop: '4px' } } />
|
||||
</motion.div>
|
||||
{ (getTotalUnseen > 0) &&
|
||||
<LayoutItemCountView count={ getTotalUnseen } className="pointer-events-none absolute -right-1 -top-1 z-10" /> }
|
||||
</motion.div>
|
||||
{ (isInRoom && showToolbarButton) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="wired-tools" onClick={ openMonitor } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isInRoom &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="camera" onClick={ () => CreateLinkEvent('camera/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ (isInRoom && youtubeEnabled) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="youtube" onClick={ openYouTubePlayer } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="modtools" onClick={ () => CreateLinkEvent('mod-tools/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="furnieditor" onClick={ () => CreateLinkEvent('furni-editor/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
{ /* Right nav — desktop */ }
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate={ visibilityVariant }
|
||||
variants={ rightNavVariants }
|
||||
transition={ NAV_TRANSITION }
|
||||
className={ `fixed bottom-0 z-40 h-[52px] max-w-[calc(50vw-242px)] items-center overflow-visible pr-3 ${ desktopFlexClasses } ${ isInRoom ? 'right-0' : 'right-3' }` }>
|
||||
<motion.div
|
||||
variants={ containerVariants }
|
||||
className="tb-open-shell flex h-[52px] max-w-full items-center gap-3 overflow-visible bg-transparent px-[8px] pt-[10px] pb-[2px]">
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="friendall" onClick={ () => CreateLinkEvent('friends/toggle') } className="tb-icon" />
|
||||
{ (requests.length > 0) &&
|
||||
<LayoutItemCountView count={ requests.length } className="absolute -right-2 -top-1" /> }
|
||||
</motion.div>
|
||||
{ ((iconState === MessengerIconState.SHOW) || (iconState === MessengerIconState.UNREAD)) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView className={ `tb-icon ${ iconState === MessengerIconState.UNREAD ? 'is-unseen animate-pulse' : '' }` } icon="message" onClick={ () => OpenMessengerChat() } />
|
||||
</motion.div> }
|
||||
<div className={ `mx-1 h-5 w-[1px] bg-white/20 ${ desktopBlockClasses }` } />
|
||||
<div className={ `h-full shrink-0 ${ desktopBlockClasses }` } id="toolbar-friend-bar-container-desktop" />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
{ /* Mobile nav. Two staggered halves split by the Me avatar. */ }
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate={ visibilityVariant }
|
||||
variants={ mobileNavVariants }
|
||||
transition={ NAV_TRANSITION }
|
||||
className={ `fixed left-1/2 z-40 flex w-[95vw] -translate-x-1/2 items-center overflow-visible ${ mobileOnlyClasses } ${ isInRoom ? 'bottom-[52px] rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] px-[6px] py-[4px] shadow-[0_-6px_18px_rgba(0,0,0,0.18)]' : 'bottom-0' }` }>
|
||||
<motion.div
|
||||
variants={ containerVariants }
|
||||
className="tb-bar-scroll flex h-full min-w-0 flex-1 items-center gap-2 overflow-x-auto overflow-y-visible px-1">
|
||||
<motion.div variants={ itemVariants }>
|
||||
{ isInRoom
|
||||
? <ToolbarItemView icon="habbo" onClick={ () => VisitDesktop() } className="tb-icon" />
|
||||
: <ToolbarItemView icon="house" onClick={ () => CreateLinkEvent('navigator/goto/home') } className="tb-icon" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="rooms" onClick={ () => CreateLinkEvent('navigator/toggle') } className="tb-icon" />
|
||||
</motion.div>
|
||||
{ GetConfigurationValue('game.center.enabled') &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="game" onClick={ () => CreateLinkEvent('games/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="catalog" onClick={ () => CreateLinkEvent('catalog/toggle/normal') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="buildersclub" onClick={ () => CreateLinkEvent('catalog/toggle/builder') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="inventory" onClick={ () => CreateLinkEvent('inventory/toggle') } className="tb-icon" />
|
||||
{ (getFullCount > 0) &&
|
||||
<LayoutItemCountView count={ getFullCount } className="absolute -right-1 top-0" /> }
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative mx-[2px] shrink-0">
|
||||
<AnimatePresence>
|
||||
{ isMeExpanded &&
|
||||
<motion.div
|
||||
key="desktop-unified-shell"
|
||||
initial={ { opacity: 0, y: 8 } }
|
||||
animate={ { opacity: 1, y: 0 } }
|
||||
exit={ { opacity: 0, y: 8 } }
|
||||
transition={ { type: 'spring', stiffness: 260, damping: 26 } }
|
||||
className={ `pointer-events-none fixed bottom-0 left-0 right-0 z-[39] h-[52px] rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] shadow-[0_-6px_18px_rgba(0,0,0,0.18)] ${ desktopBlockClasses }` } /> }
|
||||
|
||||
<motion.div
|
||||
key="left-nav"
|
||||
initial={ { opacity: 0, x: isInRoom ? -10 : 0, y: isInRoom ? 0 : 8 } }
|
||||
animate={ { opacity: 1, x: 0, y: 0 } }
|
||||
exit={ { opacity: 0, x: isInRoom ? -10 : 0, y: isInRoom ? 0 : 8 } }
|
||||
transition={ { type: 'spring', stiffness: 300, damping: 28 } }
|
||||
className={ `fixed bottom-0 left-0 z-40 h-[52px] max-w-[calc(50vw-242px)] items-center overflow-visible pl-3 pointer-events-auto ${ desktopFlexClasses }` }>
|
||||
<motion.div variants={ containerVariants } initial="hidden" animate="visible" exit="exit" className={ `tb-open-shell flex h-[52px] max-w-full items-center gap-2 overflow-visible px-[8px] pt-[10px] pb-[2px] ${ showDesktopShell ? 'bg-transparent' : 'rounded-t-[10px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] shadow-[0_-6px_18px_rgba(0,0,0,0.18)]' }` }>
|
||||
<motion.div variants={ itemVariants }>
|
||||
{ isInRoom
|
||||
? <ToolbarItemView icon="habbo" onClick={ () => VisitDesktop() } className="tb-icon" />
|
||||
: <ToolbarItemView icon="house" onClick={ () => CreateLinkEvent('navigator/goto/home') } className="tb-icon" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="rooms" onClick={ () => CreateLinkEvent('navigator/toggle') } className="tb-icon" />
|
||||
</motion.div>
|
||||
{ GetConfigurationValue('game.center.enabled') &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="game" onClick={ () => CreateLinkEvent('games/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="catalog" onClick={ () => CreateLinkEvent('catalog/toggle/normal') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="buildersclub" onClick={ () => CreateLinkEvent('catalog/toggle/builder') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="inventory" onClick={ () => CreateLinkEvent('inventory/toggle') } className="tb-icon" />
|
||||
{ (getFullCount > 0) &&
|
||||
<LayoutItemCountView count={ getFullCount } className="absolute -right-1 top-0" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<AnimatePresence>
|
||||
{ isMeExpanded &&
|
||||
<motion.div
|
||||
initial={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
animate={ { opacity: 1, y: 0, scale: 1 } }
|
||||
exit={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
transition={ { type: 'spring', stiffness: 420, damping: 28 } }
|
||||
className="pointer-events-auto absolute bottom-[calc(100%+8px)] left-1/2 z-50 -translate-x-1/2">
|
||||
<ToolbarMeView setMeExpanded={ setMeExpanded } unseenAchievementCount={ getTotalUnseen } useGuideTool={ useGuideTool } />
|
||||
</motion.div> }
|
||||
</AnimatePresence>
|
||||
<motion.div whileHover={ { scale: 1.08 } } whileTap={ { scale: 0.95 } } className="cursor-pointer" onClick={ event =>
|
||||
{
|
||||
setMeExpanded(value => !value); event.stopPropagation();
|
||||
} }>
|
||||
<LayoutAvatarImageView headOnly={ true } direction={ 2 } figure={ userFigure } className="tb-icon !h-[44px] !w-[32px] !bg-center !bg-no-repeat" style={ { marginTop: '4px' } } />
|
||||
</motion.div>
|
||||
{ (getTotalUnseen > 0) &&
|
||||
<LayoutItemCountView count={ getTotalUnseen } className="pointer-events-none absolute -right-1 -top-1 z-10" /> }
|
||||
</motion.div>
|
||||
{ (isInRoom && showToolbarButton) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="wired-tools" onClick={ openMonitor } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isInRoom &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="camera" onClick={ () => CreateLinkEvent('camera/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ (isInRoom && youtubeEnabled) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="youtube" onClick={ openYouTubePlayer } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="modtools" onClick={ () => CreateLinkEvent('mod-tools/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="furnieditor" onClick={ () => CreateLinkEvent('furni-editor/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
key="right-nav"
|
||||
initial={ { opacity: 0, x: 10 } }
|
||||
animate={ { opacity: 1, x: 0 } }
|
||||
exit={ { opacity: 0, x: 10 } }
|
||||
transition={ { type: 'spring', stiffness: 300, damping: 28 } }
|
||||
className={ `fixed bottom-0 z-40 h-[52px] max-w-[calc(50vw-242px)] items-center overflow-visible pr-3 pointer-events-auto ${ desktopFlexClasses } ${ isInRoom ? 'right-0' : 'right-3' }` }>
|
||||
<motion.div variants={ containerVariants } initial="hidden" animate="visible" exit="exit" className="tb-open-shell flex h-[52px] max-w-full items-center gap-3 overflow-visible bg-transparent px-[8px] pt-[10px] pb-[2px]">
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="friendall" onClick={ () => CreateLinkEvent('friends/toggle') } className="tb-icon" />
|
||||
{ (requests.length > 0) &&
|
||||
<LayoutItemCountView count={ requests.length } className="absolute -right-2 -top-1" /> }
|
||||
</motion.div>
|
||||
{ ((iconState === MessengerIconState.SHOW) || (iconState === MessengerIconState.UNREAD)) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView className={ `tb-icon ${ iconState === MessengerIconState.UNREAD ? 'is-unseen animate-pulse' : '' }` } icon="message" onClick={ () => OpenMessengerChat() } />
|
||||
</motion.div> }
|
||||
<div className={ `mx-1 h-5 w-[1px] bg-white/20 ${ desktopBlockClasses }` } />
|
||||
<div className={ `h-full shrink-0 ${ desktopBlockClasses }` } id="toolbar-friend-bar-container-desktop" />
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
key="mobile-nav"
|
||||
initial={ { opacity: 0, y: 8 } }
|
||||
animate={ { opacity: 1, y: 0 } }
|
||||
exit={ { opacity: 0, y: 8 } }
|
||||
transition={ { type: 'spring', stiffness: 300, damping: 28 } }
|
||||
className={ `fixed left-1/2 z-40 flex w-[95vw] -translate-x-1/2 items-center overflow-visible pointer-events-auto ${ mobileOnlyClasses } ${ isInRoom ? 'bottom-[52px] rounded-t-[12px] border border-b-0 border-white/8 bg-[rgba(10,10,12,0.58)] px-[6px] py-[4px] shadow-[0_-6px_18px_rgba(0,0,0,0.18)]' : 'bottom-0' }` }>
|
||||
<motion.div variants={ containerVariants } initial="hidden" animate="visible" exit="exit" className="tb-bar-scroll flex h-full min-w-0 flex-1 items-center gap-2 overflow-x-auto overflow-y-visible px-1">
|
||||
<motion.div variants={ itemVariants }>
|
||||
{ isInRoom
|
||||
? <ToolbarItemView icon="habbo" onClick={ () => VisitDesktop() } className="tb-icon" />
|
||||
: <ToolbarItemView icon="house" onClick={ () => CreateLinkEvent('navigator/goto/home') } className="tb-icon" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="rooms" onClick={ () => CreateLinkEvent('navigator/toggle') } className="tb-icon" />
|
||||
</motion.div>
|
||||
{ GetConfigurationValue('game.center.enabled') &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="game" onClick={ () => CreateLinkEvent('games/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="catalog" onClick={ () => CreateLinkEvent('catalog/toggle/normal') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="buildersclub" onClick={ () => CreateLinkEvent('catalog/toggle/builder') } className="tb-icon" />
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="inventory" onClick={ () => CreateLinkEvent('inventory/toggle') } className="tb-icon" />
|
||||
{ (getFullCount > 0) &&
|
||||
<LayoutItemCountView count={ getFullCount } className="absolute -right-1 top-0" /> }
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
<motion.div variants={ itemVariants } className="relative mx-[2px] shrink-0">
|
||||
<AnimatePresence>
|
||||
{ isMeExpanded &&
|
||||
<motion.div
|
||||
initial={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
animate={ { opacity: 1, y: 0, scale: 1 } }
|
||||
exit={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
transition={ { type: 'spring', stiffness: 420, damping: 28 } }
|
||||
className="pointer-events-auto absolute bottom-[calc(100%+10px)] left-1/2 z-[70] -translate-x-1/2">
|
||||
<ToolbarMeView setMeExpanded={ setMeExpanded } unseenAchievementCount={ getTotalUnseen } useGuideTool={ useGuideTool } />
|
||||
</motion.div> }
|
||||
</AnimatePresence>
|
||||
<motion.div whileHover={ { scale: 1.08 } } whileTap={ { scale: 0.95 } } className="cursor-pointer" onClick={ event =>
|
||||
{
|
||||
setMeExpanded(value => !value); event.stopPropagation();
|
||||
} }>
|
||||
<LayoutAvatarImageView headOnly={ true } direction={ 2 } figure={ userFigure } className="tb-icon !h-[44px] !w-[32px] !bg-center !bg-no-repeat" style={ { marginTop: '4px' } } />
|
||||
</motion.div>
|
||||
{ (getTotalUnseen > 0) &&
|
||||
<LayoutItemCountView count={ getTotalUnseen } className="pointer-events-none absolute -right-1 -top-1 z-10" /> }
|
||||
</motion.div>
|
||||
<motion.div variants={ containerVariants } initial="hidden" animate="visible" exit="exit" className="tb-bar-scroll flex h-full items-center gap-2 overflow-x-auto overflow-y-visible px-1">
|
||||
{ (isInRoom && showToolbarButton) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="wired-tools" onClick={ openMonitor } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isInRoom &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="camera" onClick={ () => CreateLinkEvent('camera/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ (isInRoom && youtubeEnabled) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="youtube" onClick={ openYouTubePlayer } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="modtools" onClick={ () => CreateLinkEvent('mod-tools/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="furnieditor" onClick={ () => CreateLinkEvent('furni-editor/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ !isInRoom &&
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="friendall" onClick={ () => CreateLinkEvent('friends/toggle') } className="tb-icon" />
|
||||
{ (requests.length > 0) &&
|
||||
<LayoutItemCountView count={ requests.length } className="absolute -right-2 -top-1" /> }
|
||||
</motion.div> }
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</> }
|
||||
</AnimatePresence>
|
||||
initial={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
animate={ { opacity: 1, y: 0, scale: 1 } }
|
||||
exit={ { opacity: 0, y: 6, scale: 0.97 } }
|
||||
transition={ ME_POPOVER_TRANSITION }
|
||||
className="pointer-events-auto absolute bottom-[calc(100%+10px)] left-1/2 z-[70] -translate-x-1/2">
|
||||
<ToolbarMeView setMeExpanded={ setMeExpanded } unseenAchievementCount={ getTotalUnseen } useGuideTool={ useGuideTool } />
|
||||
</motion.div> }
|
||||
</AnimatePresence>
|
||||
<motion.div
|
||||
className="cursor-pointer"
|
||||
whileHover={ { scale: 1.08 } }
|
||||
whileTap={ { scale: 0.95 } }
|
||||
onClick={ event =>
|
||||
{
|
||||
setMeExpanded(value => !value);
|
||||
event.stopPropagation();
|
||||
} }>
|
||||
<LayoutAvatarImageView headOnly={ true } direction={ 2 } figure={ userFigure } className="tb-icon !h-[44px] !w-[32px] !bg-center !bg-no-repeat" style={ { marginTop: '4px' } } />
|
||||
</motion.div>
|
||||
{ (getTotalUnseen > 0) &&
|
||||
<LayoutItemCountView count={ getTotalUnseen } className="pointer-events-none absolute -right-1 -top-1 z-10" /> }
|
||||
</motion.div>
|
||||
<motion.div
|
||||
variants={ containerVariants }
|
||||
className="tb-bar-scroll flex h-full items-center gap-2 overflow-x-auto overflow-y-visible px-1">
|
||||
{ (isInRoom && showToolbarButton) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="wired-tools" onClick={ openMonitor } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isInRoom &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="camera" onClick={ () => CreateLinkEvent('camera/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ (isInRoom && youtubeEnabled) &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="youtube" onClick={ openYouTubePlayer } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="modtools" onClick={ () => CreateLinkEvent('mod-tools/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ isMod &&
|
||||
<motion.div variants={ itemVariants }>
|
||||
<ToolbarItemView icon="furnieditor" onClick={ () => CreateLinkEvent('furni-editor/toggle') } className="tb-icon" />
|
||||
</motion.div> }
|
||||
{ !isInRoom &&
|
||||
<motion.div variants={ itemVariants } className="relative">
|
||||
<ToolbarItemView icon="friendall" onClick={ () => CreateLinkEvent('friends/toggle') } className="tb-icon" />
|
||||
{ (requests.length > 0) &&
|
||||
<LayoutItemCountView count={ requests.length } className="absolute -right-2 -top-1" /> }
|
||||
</motion.div> }
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user