Fix minor UI issues with composer

This commit is contained in:
Fedor Indutny 2022-06-15 10:53:08 -07:00 committed by GitHub
parent 3b2000a0ba
commit 4b8cb9f040
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 172 additions and 114 deletions

View file

@ -6178,6 +6178,7 @@ button.module-image__border-overlay:focus {
.module-sticker-button__button {
border: 0;
border-radius: 4px;
background: none;
width: 32px;
height: 32px;
@ -6187,7 +6188,7 @@ button.module-image__border-overlay:focus {
@include keyboard-mode {
&:focus {
outline: 1px solid $color-ultramarine;
outline: 2px solid $color-ultramarine;
}
}
@ -6682,6 +6683,7 @@ button.module-image__border-overlay:focus {
.module-emoji-button__button {
border: 0;
border-radius: 4px;
background: none;
width: 32px;
height: 32px;
@ -6691,7 +6693,7 @@ button.module-image__border-overlay:focus {
@include keyboard-mode {
&:focus {
outline: 1px solid $color-ultramarine;
outline: 2px solid $color-ultramarine;
}
}
@ -6829,6 +6831,8 @@ button.module-image__border-overlay:focus {
}
&__input {
$border-size: 1px;
border-radius: 18px;
overflow: hidden;
word-break: break-word;
@ -6836,8 +6840,6 @@ button.module-image__border-overlay:focus {
// Override Quill styles
.ql-container {
@include font-body-1;
line-height: 21px;
font-size: 15px;
}
.ql-blank::before {
@ -6863,9 +6865,11 @@ button.module-image__border-overlay:focus {
}
&__scroller {
padding: 5px 11px 6px 11px;
min-height: 32px;
max-height: 80px;
$padding-top: 5px;
padding: $padding-top 10px $padding-top 10px;
min-height: calc(32px - 2 * $border-size);
max-height: calc(72px - 2 * $border-size);
overflow: auto;
&::-webkit-scrollbar-thumb {
@ -6878,22 +6882,21 @@ button.module-image__border-overlay:focus {
}
&--large {
max-height: 227px;
min-height: 227px;
max-height: calc(212px - 2 * $border-size);
min-height: calc(212px - 2 * $border-size);
.DraftEditor-root {
height: 227px - 2 * 7px; // subtract padding
height: calc(212px - 2 * $padding-top - 2 * $border-size);
}
}
}
&:focus-within {
@include light-theme() {
outline: 1px solid $color-ultramarine;
}
border: $border-size solid transparent;
@include dark-theme() {
outline: 1px solid $color-ultramarine;
&:focus-within {
outline: 0;
@include keyboard-mode {
border: $border-size solid $color-ultramarine;
}
}
}
@ -6925,7 +6928,7 @@ button.module-image__border-overlay:focus {
padding: 0 12px;
display: flex;
flex-direction: row;
align-items: center;
align-items: end;
justify-content: flex-start;
background: none;
border: none;

View file

@ -12,6 +12,7 @@
&__microphone {
height: 32px;
width: 32px;
border-radius: 4px;
text-align: center;
background: none;
display: flex;
@ -23,7 +24,7 @@
@include keyboard-mode {
&:focus {
outline: 1px solid $color-ultramarine;
outline: 2px solid $color-ultramarine;
}
}

View file

@ -4,7 +4,7 @@
.CompositionArea {
position: relative;
min-height: 42px;
padding-top: 6px;
padding: 10px 0 10px 0;
&__placeholder {
flex-grow: 1;
@ -16,7 +16,7 @@
&__row {
display: flex;
flex-direction: row;
align-items: center;
align-items: end;
&--center {
justify-content: center;
@ -25,7 +25,7 @@
padding: 0 12px;
}
&--control-row {
margin-top: 8px;
margin-top: 12px;
}
&--column {
flex-direction: column;
@ -82,8 +82,8 @@
position: absolute;
left: calc(50% - $width / 2);
// 6px coming from padding-top, 1px from outline
top: calc(0px - $height / 2 - 6px - 1px);
// 6px coming from padding-top of .module-composition-input__input__scroller
top: calc(0px - $height / 2 - 6px);
border-radius: 12px 12px 0 0;
pointer-events: none;
@ -194,6 +194,7 @@
&__attach-file {
width: 32px;
height: 32px;
border-radius: 4px;
padding: 0;
border: none;
background: transparent;
@ -203,7 +204,7 @@
@include keyboard-mode {
&:focus {
outline: 1px solid $color-ultramarine;
outline: 2px solid $color-ultramarine;
}
}

View file

@ -272,10 +272,7 @@
}
&--audio {
@include normal-button(
'../images/icons/v2/phone-right-outline-24.svg',
24px
);
@include normal-button('../images/icons/v2/phone-outline-24.svg', 24px);
}
&--search {

View file

@ -35,22 +35,12 @@
}
&__composition-area {
margin-bottom: 6px;
// We need to use the wrapper because the conversation view calculates the height of all
// things in the composition area. A margin on an inner div won't be included in that
// height calculation.
.quote-wrapper {
margin-left: 18px;
margin-right: 18px;
margin-top: 3px;
}
.quote-wrapper,
.preview-wrapper {
margin-top: 3px;
margin-left: 12px;
margin-right: 12px;
margin-bottom: 2px;
margin: 0 16px 10px 16px;
}
}
}

View file

@ -16,18 +16,20 @@
&__button {
@include button-reset();
align-items: center;
border-radius: 16px;
border-radius: 4px;
display: flex;
height: 32px;
justify-content: center;
opacity: 0.5;
width: 32px;
&:focus,
&:hover {
opacity: 1;
@include keyboard-mode {
&:focus {
outline: 2px solid $color-ultramarine;
}
}
outline: none;
&::after {
content: '';
display: block;

View file

@ -3,11 +3,13 @@
.module-quote {
&__container {
margin: {
left: -6px;
right: -6px;
top: 3px;
bottom: 5px;
.module-message & {
margin: {
left: -6px;
right: -6px;
top: 3px;
bottom: 5px;
}
}
}
@ -91,17 +93,30 @@
border-left-color: $color-white;
}
}
.module-quote--compose-#{$color} {
background-color: scale-color($value, $lightness: 60%);
border-left-color: $value;
@include dark-theme {
background-color: scale-color($value, $lightness: -40%);
border-left-color: $color-white;
}
}
}
.module-quote--compose-custom,
.module-quote--incoming-custom,
.module-quote--outgoing-custom {
background-attachment: fixed;
}
@each $color, $value in $conversation-colors-gradient {
.module-quote--compose-#{$color},
.module-quote--incoming-#{$color} {
border-left-color: map-get($value, 'start');
}
.module-quote--compose-#{$color},
.module-quote--incoming-#{$color},
.module-quote--outgoing-#{$color} {
background-attachment: fixed;

View file

@ -77,7 +77,9 @@
}
&:focus {
border: solid 1px $color-ultramarine;
@include keyboard-mode {
border: solid 1px $color-ultramarine;
}
outline: none;
}

View file

@ -618,6 +618,7 @@ export const CompositionArea = ({
{quotedMessageProps && (
<div className="quote-wrapper">
<Quote
isCompose
{...quotedMessageProps}
i18n={i18n}
onClick={onClickQuotedMessage}

View file

@ -188,7 +188,6 @@ export function ContextMenu<T>({
}
};
// We use regular MouseEvent below, and this one uses React.MouseEvent
const handleClick = (ev: KeyboardEvent | React.MouseEvent) => {
setMenuShowing(true);
ev.stopPropagation();

View file

@ -40,6 +40,8 @@ type StateType = {
};
export class MainHeader extends React.Component<PropsType, StateType> {
public containerRef: React.RefObject<HTMLDivElement> = React.createRef();
constructor(props: PropsType) {
super(props);
@ -55,18 +57,14 @@ export class MainHeader extends React.Component<PropsType, StateType> {
if (
showingAvatarPopup &&
popperRoot &&
!popperRoot.contains(target as Node)
!popperRoot.contains(target as Node) &&
!this.containerRef.current?.contains(target as Node)
) {
this.hideAvatarPopup();
}
};
public showAvatarPopup = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
): void => {
event.preventDefault();
event.stopPropagation();
public showAvatarPopup = (): void => {
const popperRoot = document.createElement('div');
document.body.appendChild(popperRoot);
@ -142,7 +140,10 @@ export class MainHeader extends React.Component<PropsType, StateType> {
<Manager>
<Reference>
{({ ref }) => (
<div className="module-main-header__avatar--container">
<div
className="module-main-header__avatar--container"
ref={this.containerRef}
>
<Avatar
acceptedMessageRequest
avatarPath={avatarPath}

View file

@ -8,6 +8,7 @@ import { createPortal } from 'react-dom';
import classNames from 'classnames';
import { Manager, Popper, Reference } from 'react-popper';
import type { LocalizerType } from '../types/Util';
import { useRefMerger } from '../hooks/useRefMerger';
export type PropsType = {
i18n: LocalizerType;
@ -26,11 +27,11 @@ export const MediaQualitySelector = ({
undefined
);
// We use regular MouseEvent below, and this one uses React.MouseEvent
const handleClick = (ev: KeyboardEvent | React.MouseEvent) => {
const buttonRef = React.useRef<HTMLButtonElement | null>(null);
const refMerger = useRefMerger();
const handleClick = () => {
setMenuShowing(true);
ev.stopPropagation();
ev.preventDefault();
};
const handleKeyDown = (ev: KeyboardEvent) => {
@ -66,7 +67,10 @@ export const MediaQualitySelector = ({
setPopperRoot(root);
document.body.appendChild(root);
const handleOutsideClick = (event: MouseEvent) => {
if (!root.contains(event.target as Node)) {
if (
!root.contains(event.target as Node) &&
event.target !== buttonRef.current
) {
handleClose();
event.stopPropagation();
event.preventDefault();
@ -97,7 +101,7 @@ export const MediaQualitySelector = ({
})}
onClick={handleClick}
onKeyDown={handleKeyDown}
ref={ref}
ref={refMerger(buttonRef, ref)}
type="button"
/>
)}

View file

@ -31,6 +31,7 @@ export type Props = {
i18n: LocalizerType;
isFromMe: boolean;
isIncoming?: boolean;
isCompose?: boolean;
isStoryReply?: boolean;
moduleClassName?: string;
onClick?: () => void;
@ -505,6 +506,7 @@ export class Quote extends React.Component<Props, State> {
const {
conversationColor,
customColor,
isCompose,
isIncoming,
onClick,
rawAttachment,
@ -516,6 +518,16 @@ export class Quote extends React.Component<Props, State> {
return null;
}
let directionClassName: string;
if (isCompose) {
directionClassName = this.getClassName('--compose');
} else if (isIncoming) {
directionClassName = this.getClassName('--incoming');
} else {
directionClassName = this.getClassName('--outgoing');
}
const colorClassName = `${directionClassName}-${conversationColor}`;
return (
<div className={this.getClassName('__container')}>
<button
@ -524,12 +536,8 @@ export class Quote extends React.Component<Props, State> {
onKeyDown={this.handleKeyDown}
className={classNames(
this.getClassName(''),
isIncoming
? this.getClassName('--incoming')
: this.getClassName('--outgoing'),
isIncoming
? this.getClassName(`--incoming-${conversationColor}`)
: this.getClassName(`--outgoing-${conversationColor}`),
directionClassName,
colorClassName,
!onClick && this.getClassName('--no-click'),
referencedMessageNotFound &&
this.getClassName('--with-reference-warning')

View file

@ -10,6 +10,7 @@ import { Emoji } from './Emoji';
import type { Props as EmojiPickerProps } from './EmojiPicker';
import { EmojiPicker } from './EmojiPicker';
import type { LocalizerType } from '../../types/Util';
import { useRefMerger } from '../../hooks/useRefMerger';
import * as KeyboardLayout from '../../services/keyboardLayout';
export type OwnProps = {
@ -43,19 +44,16 @@ export const EmojiButton = React.memo(
const [popperRoot, setPopperRoot] = React.useState<HTMLElement | null>(
null
);
const buttonRef = React.useRef<HTMLButtonElement | null>(null);
const refMerger = useRefMerger();
const handleClickButton = React.useCallback(
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
event.preventDefault();
event.stopPropagation();
if (popperRoot) {
setOpen(false);
} else {
setOpen(true);
}
},
[popperRoot, setOpen]
);
const handleClickButton = React.useCallback(() => {
if (popperRoot) {
setOpen(false);
} else {
setOpen(true);
}
}, [popperRoot, setOpen]);
const handleClose = React.useCallback(() => {
setOpen(false);
@ -71,7 +69,10 @@ export const EmojiButton = React.memo(
setPopperRoot(root);
document.body.appendChild(root);
const handleOutsideClick = (event: MouseEvent) => {
if (!root.contains(event.target as Node)) {
if (
!root.contains(event.target as Node) &&
event.target !== buttonRef.current
) {
handleClose();
event.stopPropagation();
event.preventDefault();
@ -124,7 +125,7 @@ export const EmojiButton = React.memo(
{({ ref }) => (
<button
type="button"
ref={ref}
ref={refMerger(buttonRef, ref)}
onClick={handleClickButton}
className={classNames(className, {
'module-emoji-button__button': true,

View file

@ -15,6 +15,7 @@ import { countStickers } from './lib';
import { offsetDistanceModifier } from '../../util/popperUtil';
import { themeClassName } from '../../util/theme';
import * as KeyboardLayout from '../../services/keyboardLayout';
import { useRefMerger } from '../../hooks/useRefMerger';
export type OwnProps = {
readonly className?: string;
@ -66,34 +67,30 @@ export const StickerButton = React.memo(
const [popperRoot, setPopperRoot] = React.useState<HTMLElement | null>(
null
);
const buttonRef = React.useRef<HTMLButtonElement | null>(null);
const refMerger = useRefMerger();
const handleClickButton = React.useCallback(
(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
event.preventDefault();
event.stopPropagation();
const handleClickButton = React.useCallback(() => {
// Clear tooltip state
clearInstalledStickerPack();
clearShowIntroduction();
// Clear tooltip state
clearInstalledStickerPack();
clearShowIntroduction();
// Handle button click
if (installedPacks.length === 0) {
onClickAddPack?.();
} else if (popperRoot) {
setOpen(false);
} else {
setOpen(true);
}
},
[
clearInstalledStickerPack,
clearShowIntroduction,
installedPacks,
onClickAddPack,
popperRoot,
setOpen,
]
);
// Handle button click
if (installedPacks.length === 0) {
onClickAddPack?.();
} else if (popperRoot) {
setOpen(false);
} else {
setOpen(true);
}
}, [
clearInstalledStickerPack,
clearShowIntroduction,
installedPacks,
onClickAddPack,
popperRoot,
setOpen,
]);
const handlePickSticker = React.useCallback(
(packId: string, stickerId: number, url: string) => {
@ -139,7 +136,11 @@ export const StickerButton = React.memo(
targetClassName.indexOf('module-sticker-picker__header__button') <
0;
if (!root.contains(targetElement) && isMissingButtonClass) {
if (
!root.contains(targetElement) &&
isMissingButtonClass &&
targetElement !== buttonRef.current
) {
setOpen(false);
}
};
@ -214,7 +215,7 @@ export const StickerButton = React.memo(
{({ ref }) => (
<button
type="button"
ref={ref}
ref={refMerger(buttonRef, ref)}
onClick={handleClickButton}
className={classNames(
{

View file

@ -8687,6 +8687,22 @@
"reasonCategory": "usageTrusted",
"updated": "2021-10-11T21:21:08.188Z"
},
{
"rule": "React-createRef",
"path": "ts/components/MainHeader.tsx",
"line": " public containerRef: React.RefObject<HTMLDivElement> = React.createRef();",
"reasonCategory": "usageTrusted",
"updated": "2022-06-14T22:04:43.988Z",
"reasonDetail": "Handling outside click"
},
{
"rule": "React-useRef",
"path": "ts/components/MediaQualitySelector.tsx",
"line": " const buttonRef = React.useRef<HTMLButtonElement | null>(null);",
"reasonCategory": "usageTrusted",
"updated": "2022-06-14T22:04:43.988Z",
"reasonDetail": "Handling outside click"
},
{
"rule": "React-useRef",
"path": "ts/components/Modal.tsx",
@ -8899,6 +8915,14 @@
"updated": "2019-11-01T22:46:33.013Z",
"reasonDetail": "Used for setting focus only"
},
{
"rule": "React-useRef",
"path": "ts/components/emoji/EmojiButton.tsx",
"line": " const buttonRef = React.useRef<HTMLButtonElement | null>(null);",
"reasonCategory": "usageTrusted",
"updated": "2022-06-14T22:04:43.988Z",
"reasonDetail": "Handling outside click"
},
{
"rule": "React-useRef",
"path": "ts/components/installScreen/InstallScreenChoosingDeviceNameStep.tsx",
@ -8907,6 +8931,14 @@
"updated": "2021-12-06T23:07:28.947Z",
"reasonDetail": "Doesn't touch the DOM."
},
{
"rule": "React-useRef",
"path": "ts/components/stickers/StickerButton.tsx",
"line": " const buttonRef = React.useRef<HTMLButtonElement | null>(null);",
"reasonCategory": "usageTrusted",
"updated": "2022-06-14T22:04:43.988Z",
"reasonDetail": "Handling outside click"
},
{
"rule": "React-useRef",
"path": "ts/hooks/useIntersectionObserver.ts",