feat(horseshoe): add 12px void gap between chat and profile pane with rounded TL/BL on profile, drop page-nav right rounding
This commit is contained in:
parent
de2354f1da
commit
785b679b61
4 changed files with 86 additions and 42 deletions
|
|
@ -62,28 +62,12 @@ export function PageRoot({ nav, children }: PageRootProps) {
|
||||||
if (horseshoe) {
|
if (horseshoe) {
|
||||||
return (
|
return (
|
||||||
<Box grow="Yes" className={ContainerColor({ variant: 'Background' })}>
|
<Box grow="Yes" className={ContainerColor({ variant: 'Background' })}>
|
||||||
{/* Page-nav slot — paints two void squares at its top-right and
|
{/* Page-nav slot — its right edge stays square (the original
|
||||||
bottom-right via `background-image` (the
|
TR / BR rounding was reverted). The 12px void gap below
|
||||||
`linear-gradient(SAME, SAME)` idiom = a solid-colour image).
|
still creates a visible seam between page-nav and the chat
|
||||||
The wrapper's bg sits naturally beneath its in-flow
|
panel, and the resize handle inside `ResizablePageNav` is
|
||||||
children, so PageNavHeader and the bottom row paint their
|
shifted into that void via the inline style below. */}
|
||||||
normal Background colour over the void everywhere except in
|
|
||||||
their own rounded TR / BR carves — which expose the void.
|
|
||||||
No absolute positioning, no z-index gymnastics, and the
|
|
||||||
resize handle inside `ResizablePageNav` stays a sibling of
|
|
||||||
the clipped inner column so it isn't clipped. */}
|
|
||||||
<Box
|
|
||||||
shrink="No"
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
backgroundImage: `linear-gradient(${VOJO_HORSESHOE_VOID_COLOR}, ${VOJO_HORSESHOE_VOID_COLOR}), linear-gradient(${VOJO_HORSESHOE_VOID_COLOR}, ${VOJO_HORSESHOE_VOID_COLOR})`,
|
|
||||||
backgroundSize: `${horseshoeRadius} ${horseshoeRadius}, ${horseshoeRadius} ${horseshoeRadius}`,
|
|
||||||
backgroundPosition: 'top right, bottom right',
|
|
||||||
backgroundRepeat: 'no-repeat, no-repeat',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{nav}
|
{nav}
|
||||||
</Box>
|
|
||||||
<Box
|
<Box
|
||||||
shrink="No"
|
shrink="No"
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
|
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
|
||||||
import { DefaultReset, color, config, toRem } from 'folds';
|
import { DefaultReset, color, config, toRem } from 'folds';
|
||||||
import { VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe';
|
|
||||||
|
|
||||||
export const PageNavResizable = style({
|
export const PageNavResizable = style({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
|
@ -89,20 +88,16 @@ export const PageNav = recipe({
|
||||||
export type PageNavVariants = RecipeVariants<typeof PageNav>;
|
export type PageNavVariants = RecipeVariants<typeof PageNav>;
|
||||||
|
|
||||||
// Web-only horseshoe shell wrapping every page-nav's inner column.
|
// Web-only horseshoe shell wrapping every page-nav's inner column.
|
||||||
// `overflow:hidden + border-radius` clips the nav's content into a
|
// Previously carved rounded TR / BR corners against a void backdrop;
|
||||||
// shape with rounded TR and BR corners; the wrapper PageRoot puts
|
// the rounding was reverted while keeping the 12px void gap between
|
||||||
// behind the nav paints `#090909` at those same corners via background-
|
// page-nav and chat panel, so this class now just enforces an opaque
|
||||||
// image, so the carved area reads as the horseshoe void. An explicit
|
// Background bg + clips overflow. The bg keeps the page-nav header /
|
||||||
// `Background.Container` bg is required: folds `<Header>` is fully
|
// footer painted even on routes (Bots, Channels) that don't paint
|
||||||
// transparent (it only sets color/border), and some routes (Bots,
|
// anything of their own; `overflow:hidden` is defensive against any
|
||||||
// Channels) don't paint anything at the bottom — without this bg the
|
// child that might bleed past the inner column. Applied conditionally
|
||||||
// PageRoot void would bleed through the entire header / footer instead
|
// in `PageNav` / `ResizablePageNav` below; native skips it entirely.
|
||||||
// of just the carved corner. Applied conditionally in `PageNav` /
|
|
||||||
// `ResizablePageNav` below; native skips it entirely.
|
|
||||||
export const PageNavInnerWebHorseshoe = style({
|
export const PageNavInnerWebHorseshoe = style({
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
borderTopRightRadius: toRem(VOJO_HORSESHOE_RADIUS_PX),
|
|
||||||
borderBottomRightRadius: toRem(VOJO_HORSESHOE_RADIUS_PX),
|
|
||||||
backgroundColor: color.Background.Container,
|
backgroundColor: color.Background.Container,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { Box, Line } from 'folds';
|
import { Box, Line, toRem } from 'folds';
|
||||||
import { useMatch, useParams } from 'react-router-dom';
|
import { useMatch, useParams } from 'react-router-dom';
|
||||||
import { isKeyHotkey } from 'is-hotkey';
|
import { isKeyHotkey } from 'is-hotkey';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { RoomView } from './RoomView';
|
import { RoomView } from './RoomView';
|
||||||
|
import { userRoomProfileAtom } from '../../state/userRoomProfile';
|
||||||
|
import { ContainerColor } from '../../styles/ContainerColor.css';
|
||||||
|
import { VOJO_HORSESHOE_GAP_PX, VOJO_HORSESHOE_VOID_COLOR } from '../../styles/horseshoe';
|
||||||
import { MembersDrawer } from './MembersDrawer';
|
import { MembersDrawer } from './MembersDrawer';
|
||||||
import { ThreadDrawer } from './ThreadDrawer';
|
import { ThreadDrawer } from './ThreadDrawer';
|
||||||
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
|
||||||
|
|
@ -91,6 +94,24 @@ export function Room({ renderRoomView }: RoomProps) {
|
||||||
|
|
||||||
const unreadThreadingEnabled = useUnreadThreadingEnabled();
|
const unreadThreadingEnabled = useUnreadThreadingEnabled();
|
||||||
|
|
||||||
|
// Profile horseshoe: when the right-side profile pane is open on
|
||||||
|
// desktop/tablet, paint a 12px void seam between the chat column and
|
||||||
|
// the profile, and carve rounded TL/BL corners on the profile pane.
|
||||||
|
// The chat column itself keeps a straight right edge (no rounding) —
|
||||||
|
// only the profile carves, so the void gap reads as one black bar
|
||||||
|
// with a single rounded contour on the right side of the seam.
|
||||||
|
// Mirrors the page-nav <-> chat split from PageRoot (commit 363bd9d)
|
||||||
|
// minus the chat-side rounding. Mobile and the thread-drawer case
|
||||||
|
// don't mount the side pane, so the wrap is gated on both.
|
||||||
|
//
|
||||||
|
// Void colour is painted on the parent flex row (covering everything
|
||||||
|
// not painted by an opaque child) so the profile pane's TL/BL carves
|
||||||
|
// expose void rather than the chat-panel-inner's Background from
|
||||||
|
// PageRoot. The chat column applies an explicit Background bg so the
|
||||||
|
// parent void can't bleed through any transparent slivers.
|
||||||
|
const profileOpen = !!useAtomValue(userRoomProfileAtom);
|
||||||
|
const showProfileHorseshoe = profileOpen && !isMobile && !showThreadDrawer;
|
||||||
|
|
||||||
useKeyDown(
|
useKeyDown(
|
||||||
window,
|
window,
|
||||||
useCallback(
|
useCallback(
|
||||||
|
|
@ -120,9 +141,22 @@ export function Room({ renderRoomView }: RoomProps) {
|
||||||
return (
|
return (
|
||||||
<PowerLevelsContextProvider value={powerLevels}>
|
<PowerLevelsContextProvider value={powerLevels}>
|
||||||
<ThreadDrawerOpenProvider value={showThreadDrawer}>
|
<ThreadDrawerOpenProvider value={showThreadDrawer}>
|
||||||
<Box grow="Yes">
|
<Box
|
||||||
|
grow="Yes"
|
||||||
|
style={
|
||||||
|
showProfileHorseshoe
|
||||||
|
? { backgroundColor: VOJO_HORSESHOE_VOID_COLOR }
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
{callView && (screenSize === ScreenSize.Desktop || !chat) && (
|
{callView && (screenSize === ScreenSize.Desktop || !chat) && (
|
||||||
<Box grow="Yes" direction="Column">
|
<Box
|
||||||
|
grow="Yes"
|
||||||
|
direction="Column"
|
||||||
|
className={
|
||||||
|
showProfileHorseshoe ? ContainerColor({ variant: 'Background' }) : undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
<RoomViewProfilePanel header={<RoomViewHeader callView />}>
|
<RoomViewProfilePanel header={<RoomViewHeader callView />}>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
<CallView />
|
<CallView />
|
||||||
|
|
@ -131,7 +165,13 @@ export function Room({ renderRoomView }: RoomProps) {
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{!callView && !drawerHidesChat && (
|
{!callView && !drawerHidesChat && (
|
||||||
<Box grow="Yes" direction="Column">
|
<Box
|
||||||
|
grow="Yes"
|
||||||
|
direction="Column"
|
||||||
|
className={
|
||||||
|
showProfileHorseshoe ? ContainerColor({ variant: 'Background' }) : undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
<RoomViewProfilePanel header={<RoomViewHeader />}>
|
<RoomViewProfilePanel header={<RoomViewHeader />}>
|
||||||
<Box grow="Yes">
|
<Box grow="Yes">
|
||||||
{renderRoomView?.({ eventId }) ?? <RoomView eventId={eventId} />}
|
{renderRoomView?.({ eventId }) ?? <RoomView eventId={eventId} />}
|
||||||
|
|
@ -143,8 +183,23 @@ export function Room({ renderRoomView }: RoomProps) {
|
||||||
{/* Tablet / Desktop: profile renders as a third pane to the
|
{/* Tablet / Desktop: profile renders as a third pane to the
|
||||||
right of the chat. Mobile uses the top horseshoe inside
|
right of the chat. Mobile uses the top horseshoe inside
|
||||||
`RoomViewProfilePanel`, so we don't mount the side pane
|
`RoomViewProfilePanel`, so we don't mount the side pane
|
||||||
there. */}
|
there. The 12px void gap (same as page-nav <-> chat split
|
||||||
{!isMobile && !showThreadDrawer && <RoomViewProfileSidePanel />}
|
from PageRoot) sits between the chat column and the pane
|
||||||
|
so the seam reads identically to the rest of the app. */}
|
||||||
|
{!isMobile && !showThreadDrawer && (
|
||||||
|
<>
|
||||||
|
{showProfileHorseshoe && (
|
||||||
|
<Box
|
||||||
|
shrink="No"
|
||||||
|
style={{
|
||||||
|
width: toRem(VOJO_HORSESHOE_GAP_PX),
|
||||||
|
backgroundColor: VOJO_HORSESHOE_VOID_COLOR,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<RoomViewProfileSidePanel />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{callView && chat && (
|
{callView && chat && (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,28 @@
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
import { color, config, toRem } from 'folds';
|
import { color, config, toRem } from 'folds';
|
||||||
|
import { VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe';
|
||||||
|
|
||||||
// Right-side profile pane sized like the members drawer family —
|
// Right-side profile pane sized like the members drawer family —
|
||||||
// wide enough for the identity card + chips without dominating the
|
// wide enough for the identity card + chips without dominating the
|
||||||
// chat. Clamp keeps it readable on narrow desktops and prevents
|
// chat. Clamp keeps it readable on narrow desktops and prevents
|
||||||
// runaway width on ultra-wide displays.
|
// runaway width on ultra-wide displays.
|
||||||
|
//
|
||||||
|
// Left edge is rounded (TL + BL) to mirror the chat column's TR / BR
|
||||||
|
// carves across the 12px horseshoe void gap rendered by Room.tsx —
|
||||||
|
// same design language as the page-nav <-> chat split. `overflow:
|
||||||
|
// hidden` keeps the rounded corners clean against the header /
|
||||||
|
// scroll content; the void colour beneath is painted by the parent
|
||||||
|
// flex row, not by the panel itself.
|
||||||
export const panel = style({
|
export const panel = style({
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
width: `clamp(${toRem(300)}, 25%, ${toRem(380)})`,
|
width: `clamp(${toRem(300)}, 25%, ${toRem(380)})`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
backgroundColor: color.Surface.Container,
|
backgroundColor: color.Surface.Container,
|
||||||
borderLeft: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
|
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
|
overflow: 'hidden',
|
||||||
|
borderTopLeftRadius: toRem(VOJO_HORSESHOE_RADIUS_PX),
|
||||||
|
borderBottomLeftRadius: toRem(VOJO_HORSESHOE_RADIUS_PX),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Match the chat header's left gutter (RoomViewHeaderDm uses S200
|
// Match the chat header's left gutter (RoomViewHeaderDm uses S200
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue