Compact context menus on desktop (#1726)

This commit is contained in:
Alexander Zinchuk 2022-02-25 22:52:51 +02:00
parent 9c789cdcb8
commit c81ed0eb61
19 changed files with 130 additions and 22 deletions

View File

@ -11,17 +11,13 @@
background: #181f27; background: #181f27;
} }
.Menu .bubble { .Menu {
--color-background: #232a34; --color-text: white;
--color-chat-hover: #2f363e; --color-background-compact-menu: #212121DD;
--color-item-active: #2f363e; --color-background-compact-menu-hover: #00000066;
--color-text: #fff; .bubble {
box-shadow: 0 0.25rem 0.5rem 0.125rem rgba(16, 16, 16, 0.3); box-shadow: 0 0.25rem 0.5rem 0.125rem rgba(16, 16, 16, 0.3);
} }
// Compact menu items
.MenuItem {
padding: 0.75rem 1rem !important;
} }
&.single-column { &.single-column {

View File

@ -2,14 +2,18 @@
.participant-menu { .participant-menu {
position: absolute; position: absolute;
--color-text: white;
--color-background-compact-menu: #212121DD;
--color-background-compact-menu-hover: #00000066;
.bubble { .bubble {
background: none; background: none !important;
border-radius: 0; border-radius: 0;
padding: 0; padding: 0;
border: none !important; border: none !important;
box-shadow: none !important; box-shadow: none !important;
overflow: visible; overflow: visible;
color: #ffffff; color: var(--color-text);
.group { .group {
box-shadow: 0 0.25rem 0.5rem 0.125rem rgba(16, 16, 16, 0.3); box-shadow: 0 0.25rem 0.5rem 0.125rem rgba(16, 16, 16, 0.3);
@ -20,6 +24,12 @@
} }
} }
&.compact {
.group {
background: var(--color-background-compact-menu);
}
}
.volume-control { .volume-control {
height: 3rem; height: 3rem;

View File

@ -69,6 +69,16 @@
flex-shrink: 0; flex-shrink: 0;
} }
.MenuItem.compact .archived-badge {
background: none;
padding: 0;
color: var(--color-text-secondary);
}
.MenuItem.compact .Switcher {
transform: scale(0.75);
}
[dir="rtl"] .archived-badge { [dir="rtl"] .archived-badge {
margin-left: 0; margin-left: 0;
margin-right: auto; margin-right: auto;

View File

@ -43,6 +43,7 @@ const BotCommandMenu: FC<OwnProps> = ({
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined} onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined} onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
noCloseOnBackdrop={!IS_TOUCH_ENV} noCloseOnBackdrop={!IS_TOUCH_ENV}
noCompact
> >
{botCommands.map((botCommand) => ( {botCommands.map((botCommand) => (
<BotCommand <BotCommand

View File

@ -56,6 +56,7 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
onCloseAnimationEnd={handleClose} onCloseAnimationEnd={handleClose}
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined} onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined} onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
noCompact
> >
<div className="content"> <div className="content">
{message.keyboardButtons.map((row) => ( {message.keyboardButtons.map((row) => (

View File

@ -86,6 +86,7 @@ const SendAsMenu: FC<OwnProps> = ({
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined} onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined} onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
noCloseOnBackdrop={!IS_TOUCH_ENV} noCloseOnBackdrop={!IS_TOUCH_ENV}
noCompact
> >
<div className="send-as-title" dir="auto">{lang('SendMessageAsTitle')}</div> <div className="send-as-title" dir="auto">{lang('SendMessageAsTitle')}</div>
{usersById && chatsById && sendAsIds?.map((id, index) => { {usersById && chatsById && sendAsIds?.map((id, index) => {

View File

@ -232,6 +232,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined} onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined} onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
noCloseOnBackdrop={!IS_TOUCH_ENV} noCloseOnBackdrop={!IS_TOUCH_ENV}
noCompact
> >
{content} {content}
</Menu> </Menu>

View File

@ -9,11 +9,15 @@
overscroll-behavior: contain; overscroll-behavior: contain;
} }
&.compact .scrollable-content {
padding: 0.25rem 0;
}
.bubble { .bubble {
transition: opacity 0.15s cubic-bezier(0.2, 0, 0.2, 1), transform 0.15s cubic-bezier(0.2, 0, 0.2, 1) !important; transition: opacity 0.15s cubic-bezier(0.2, 0, 0.2, 1), transform 0.15s cubic-bezier(0.2, 0, 0.2, 1) !important;
transform: scale(0.7); transform: scale(0.7);
overflow: initial; overflow: initial;
padding: 0; padding: 0 !important;
} }
&.with-reactions .bubble { &.with-reactions .bubble {

View File

@ -11,6 +11,7 @@
top: -3.5rem; top: -3.5rem;
&__bubble-big { &__bubble-big {
border: 0.5rem solid var(--color-background);
position: absolute; position: absolute;
display: block; display: block;
content: ""; content: "";
@ -18,8 +19,10 @@
bottom: -0.5rem; bottom: -0.5rem;
width: 1rem; width: 1rem;
height: 1rem; height: 1rem;
border-radius: 50%; border-top: 0;
background: var(--color-background); border-left: 0;
border-right: 0;
border-radius: 0 0 50% 50%;
z-index: -1; z-index: -1;
} }
@ -64,4 +67,22 @@
align-items: center; align-items: center;
border-radius: 3rem; border-radius: 3rem;
} }
&--compact {
background: var(--color-background-compact-menu-reactions);
height: 2rem;
top: -2.5rem;
}
&--compact &__items {
padding: 0 0.5rem;
}
&--compact &__bubble-big {
border-color: var(--color-background-compact-menu-reactions);
}
&--compact &__bubble-small {
background: var(--color-background-compact-menu-reactions);
}
} }

View File

@ -8,6 +8,8 @@ import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import { getTouchY } from '../../../util/scrollLock'; import { getTouchY } from '../../../util/scrollLock';
import { createClassNameBuilder } from '../../../util/buildClassName'; import { createClassNameBuilder } from '../../../util/buildClassName';
import { IS_COMPACT_MENU } from '../../../util/environment';
import ReactionSelectorReaction from './ReactionSelectorReaction'; import ReactionSelectorReaction from './ReactionSelectorReaction';
import './ReactionSelector.scss'; import './ReactionSelector.scss';
@ -50,7 +52,7 @@ const ReactionSelector: FC<OwnProps> = ({
if ((!isPrivate && !enabledReactions?.length) || !availableReactions) return undefined; if ((!isPrivate && !enabledReactions?.length) || !availableReactions) return undefined;
return ( return (
<div className={cn('&')} onWheelCapture={handleWheel} onTouchMove={handleWheel}> <div className={cn('&', IS_COMPACT_MENU && 'compact')} onWheelCapture={handleWheel} onTouchMove={handleWheel}>
<div className={cn('bubble-big')} /> <div className={cn('bubble-big')} />
<div className={cn('bubble-small')} /> <div className={cn('bubble-small')} />
<div className={cn('items-wrapper')}> <div className={cn('items-wrapper')}>

View File

@ -23,4 +23,9 @@
top: 0; top: 0;
left: 0; left: 0;
} }
&--compact {
min-width: 1.5rem;
min-height: 1.5rem;
}
} }

View File

@ -8,12 +8,13 @@ import useMedia from '../../../hooks/useMedia';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useShowTransition from '../../../hooks/useShowTransition'; import useShowTransition from '../../../hooks/useShowTransition';
import { createClassNameBuilder } from '../../../util/buildClassName'; import { createClassNameBuilder } from '../../../util/buildClassName';
import { IS_COMPACT_MENU } from '../../../util/environment';
import AnimatedSticker from '../../common/AnimatedSticker'; import AnimatedSticker from '../../common/AnimatedSticker';
import './ReactionSelectorReaction.scss'; import './ReactionSelectorReaction.scss';
const REACTION_SIZE = 32; const REACTION_SIZE = IS_COMPACT_MENU ? 24 : 32;
type OwnProps = { type OwnProps = {
reaction: ApiAvailableReaction; reaction: ApiAvailableReaction;
@ -48,7 +49,7 @@ const ReactionSelectorReaction: FC<OwnProps> = ({ reaction, previewIndex, onSend
return ( return (
<div <div
className={cn('&')} className={cn('&', IS_COMPACT_MENU && 'compact')}
onClick={handleClick} onClick={handleClick}
ref={containerRef} ref={containerRef}
onMouseEnter={isReady ? activate : undefined} onMouseEnter={isReady ? activate : undefined}
@ -59,7 +60,7 @@ const ReactionSelectorReaction: FC<OwnProps> = ({ reaction, previewIndex, onSend
'static', 'static',
isReady ? [staticClassNames] : undefined, isReady ? [staticClassNames] : undefined,
)} )}
style={`background-position-x: ${previewIndex * -32}px;`} style={`background-position-x: ${previewIndex * -REACTION_SIZE}px;`}
/> />
)} )}
{shouldRenderAnimated && ( {shouldRenderAnimated && (

View File

@ -78,4 +78,16 @@
font-size: 0.8125rem; font-size: 0.8125rem;
text-align: center; text-align: center;
} }
&.compact {
.bubble {
background: var(--color-background-compact-menu);
backdrop-filter: blur(10px);
padding: 0.25rem 0;
}
.footer {
background: none;
}
}
} }

View File

@ -10,6 +10,7 @@ import buildClassName from '../../util/buildClassName';
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import useHistoryBack from '../../hooks/useHistoryBack'; import useHistoryBack from '../../hooks/useHistoryBack';
import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMessageInputBlur'; import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMessageInputBlur';
import { IS_COMPACT_MENU } from '../../util/environment';
import './Menu.scss'; import './Menu.scss';
@ -28,6 +29,7 @@ type OwnProps = {
shouldSkipTransition?: boolean; shouldSkipTransition?: boolean;
footer?: string; footer?: string;
noCloseOnBackdrop?: boolean; noCloseOnBackdrop?: boolean;
noCompact?: boolean;
onKeyDown?: (e: React.KeyboardEvent<any>) => void; onKeyDown?: (e: React.KeyboardEvent<any>) => void;
onCloseAnimationEnd?: () => void; onCloseAnimationEnd?: () => void;
onClose?: () => void; onClose?: () => void;
@ -53,6 +55,7 @@ const Menu: FC<OwnProps> = ({
autoClose = false, autoClose = false,
footer, footer,
noCloseOnBackdrop = false, noCloseOnBackdrop = false,
noCompact,
onCloseAnimationEnd, onCloseAnimationEnd,
onClose, onClose,
onMouseEnter, onMouseEnter,
@ -110,7 +113,7 @@ const Menu: FC<OwnProps> = ({
return ( return (
<div <div
className={buildClassName('Menu no-selection', className)} className={buildClassName('Menu no-selection', !noCompact && IS_COMPACT_MENU && 'compact', className)}
onKeyDown={isOpen ? handleKeyDown : undefined} onKeyDown={isOpen ? handleKeyDown : undefined}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={isOpen ? onMouseLeave : undefined} onMouseLeave={isOpen ? onMouseLeave : undefined}

View File

@ -64,7 +64,7 @@
} }
} }
&:not(.has-ripple):not(.disabled):active { &:not(.has-ripple):not(.disabled):not(.compact):active {
background-color: var(--color-item-active); background-color: var(--color-item-active);
transition: none !important; transition: none !important;
} }
@ -89,4 +89,35 @@
margin-right: auto; margin-right: auto;
} }
} }
&.compact {
align-items: center;
font-size: 0.875rem;
margin: 0.125rem 0.25rem;
padding: 0.25rem;
border-radius: 0.375rem;
width: auto;
font-weight: 500;
transform: scale(1);
transition: 0.15s ease-in-out transform;
i {
font-size: 1.25rem;
margin-left: 0.5rem;
margin-right: 1.25rem;
}
@media (hover: hover) {
&:hover,
&:focus,
&:active {
background: var(--color-background-compact-menu-hover);
text-decoration: none;
}
&:active {
transform: scale(0.98);
}
}
}
} }

View File

@ -2,6 +2,7 @@ import React, { FC, useCallback } from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
import { IS_COMPACT_MENU } from '../../util/environment';
import './MenuItem.scss'; import './MenuItem.scss';
@ -64,6 +65,7 @@ const MenuItem: FC<OwnProps> = (props) => {
className, className,
disabled && 'disabled', disabled && 'disabled',
destructive && 'destructive', destructive && 'destructive',
IS_COMPACT_MENU && 'compact',
); );
const content = ( const content = (

View File

@ -65,6 +65,9 @@ $color-message-reaction-own-hover: #b5e0a4;
:root { :root {
--color-background: #{$color-white}; --color-background: #{$color-white};
--color-background-compact-menu: #FFFFFFBB;
--color-background-compact-menu-reactions: #FFFFFFEB;
--color-background-compact-menu-hover: #000000B2;
--color-background-selected: #f4f4f5; --color-background-selected: #f4f4f5;
--color-background-secondary: #f4f4f5; --color-background-secondary: #f4f4f5;
--color-background-secondary-accent: #e4e4e5; --color-background-secondary-accent: #e4e4e5;

View File

@ -3,6 +3,9 @@
"--color-primary-opacity": ["#50A2E980", "#8378DB80"], "--color-primary-opacity": ["#50A2E980", "#8378DB80"],
"--color-primary-shade": ["#4a95d6", "#7b71c6"], "--color-primary-shade": ["#4a95d6", "#7b71c6"],
"--color-background": ["#FFFFFF", "#212121"], "--color-background": ["#FFFFFF", "#212121"],
"--color-background-compact-menu": ["#FFFFFFBB", "#212121DD"],
"--color-background-compact-menu-reactions": ["#FFFFFFEB", "#212121DD"],
"--color-background-compact-menu-hover": ["#00000011", "#00000066"],
"--color-background-secondary": ["#f4f4f5", "#0F0F0F"], "--color-background-secondary": ["#f4f4f5", "#0F0F0F"],
"--color-background-secondary-accent": ["#E4E4E5", "#100f10"], "--color-background-secondary-accent": ["#E4E4E5", "#100f10"],
"--color-background-own": ["#EEFFDE", "#8774E1"], "--color-background-own": ["#EEFFDE", "#8774E1"],

View File

@ -86,6 +86,7 @@ export const DPR = window.devicePixelRatio || 1;
export const MASK_IMAGE_DISABLED = true; export const MASK_IMAGE_DISABLED = true;
export const IS_COMPACT_MENU = !IS_TOUCH_ENV;
export const IS_SCROLL_PATCH_NEEDED = !IS_MAC_OS && !IS_IOS && !IS_ANDROID; export const IS_SCROLL_PATCH_NEEDED = !IS_MAC_OS && !IS_IOS && !IS_ANDROID;
// Smaller area reduces scroll jumps caused by `patchChromiumScroll` // Smaller area reduces scroll jumps caused by `patchChromiumScroll`