Fix avatar focus highlight
This commit is contained in:
parent
f5cce73611
commit
f4b0bade80
6 changed files with 74 additions and 90 deletions
|
@ -570,14 +570,17 @@
|
||||||
@mixin avatar-colors {
|
@mixin avatar-colors {
|
||||||
@each $color, $value in $avatar-colors {
|
@each $color, $value in $avatar-colors {
|
||||||
&--#{$color} {
|
&--#{$color} {
|
||||||
background-color: map-get($value, 'bg');
|
--bg: #{map-get($value, 'bg')};
|
||||||
color: map-get($value, 'fg');
|
--fg: #{map-get($value, 'fg')};
|
||||||
|
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--fg);
|
||||||
|
|
||||||
&--icon {
|
&--icon {
|
||||||
background-color: map-get($value, 'fg');
|
background-color: var(--fg);
|
||||||
@include dark-theme {
|
@include dark-theme {
|
||||||
// For specificity
|
// For specificity
|
||||||
background-color: map-get($value, 'fg');
|
background-color: var(--fg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1267,22 +1267,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-message__author-avatar {
|
|
||||||
@include button-reset;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
.module-Avatar {
|
|
||||||
@include keyboard-mode {
|
|
||||||
box-shadow: 0 0 0 3px $color-ultramarine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.module-message__typing-container {
|
.module-message__typing-container {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
|
||||||
|
|
|
@ -2,39 +2,46 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
.module-Avatar {
|
.module-Avatar {
|
||||||
align-items: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
&__button {
|
&__contents {
|
||||||
@include button-reset;
|
@include avatar-colors;
|
||||||
|
position: relative;
|
||||||
align-items: center;
|
overflow: hidden;
|
||||||
display: flex;
|
border-radius: 100%;
|
||||||
height: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
@include keyboard-mode {
|
@at-root button#{&} {
|
||||||
&:focus {
|
@include keyboard-mode {
|
||||||
box-shadow: 0px 0px 0px 2px $color-ultramarine;
|
&:focus {
|
||||||
|
box-shadow: 0 0 0 3px $color-ultramarine;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__image,
|
||||||
|
&__label,
|
||||||
|
&__icon,
|
||||||
|
&__spinner-container,
|
||||||
|
&__click-to-view {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
&__image {
|
&__image {
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
transition: filter 100ms ease-out;
|
transition: filter 100ms ease-out;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__click-to-view {
|
&__click-to-view {
|
||||||
|
@ -44,12 +51,10 @@
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
left: 0;
|
left: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
@include color-svg(
|
@include color-svg(
|
||||||
|
@ -78,27 +83,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
@mixin avatar-icon($icon) {
|
-webkit-mask-repeat: no-repeat;
|
||||||
-webkit-mask: url($icon) no-repeat center;
|
-webkit-mask-position: center;
|
||||||
-webkit-mask-size: 100%;
|
-webkit-mask-size: 62%;
|
||||||
}
|
background-color: var(--fg);
|
||||||
|
|
||||||
&--direct {
|
&--direct {
|
||||||
@include avatar-icon('../images/icons/v2/profile-outline-20.svg');
|
-webkit-mask-image: url('../images/icons/v2/profile-outline-20.svg');
|
||||||
height: 60%;
|
-webkit-mask-size: 60%;
|
||||||
width: 60%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--group {
|
&--group {
|
||||||
@include avatar-icon('../images/icons/v2/group-outline-24.svg');
|
-webkit-mask-image: url('../images/icons/v2/group-outline-24.svg');
|
||||||
height: 62%;
|
|
||||||
width: 62%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--note-to-self {
|
&--note-to-self {
|
||||||
@include avatar-icon('../images/icons/v2/note-24.svg');
|
-webkit-mask-image: url('../images/icons/v2/note-24.svg');
|
||||||
height: 62%;
|
|
||||||
width: 62%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +106,6 @@
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include avatar-colors();
|
|
||||||
|
|
||||||
&--undefined {
|
&--undefined {
|
||||||
background-color: $color-gray-15;
|
background-color: $color-gray-15;
|
||||||
color: $color-gray-75;
|
color: $color-gray-75;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@include font-title-2;
|
@include font-title-2;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
FunctionComponent,
|
FunctionComponent,
|
||||||
|
MouseEvent,
|
||||||
|
ReactChild,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useEffect,
|
useEffect,
|
||||||
useState,
|
useState,
|
||||||
|
@ -53,7 +55,7 @@ export type Props = {
|
||||||
title: string;
|
title: string;
|
||||||
unblurredAvatarPath?: string;
|
unblurredAvatarPath?: string;
|
||||||
|
|
||||||
onClick?: () => unknown;
|
onClick?: (event: MouseEvent<HTMLButtonElement>) => unknown;
|
||||||
|
|
||||||
// Matches Popper's RefHandler type
|
// Matches Popper's RefHandler type
|
||||||
innerRef?: React.Ref<HTMLDivElement>;
|
innerRef?: React.Ref<HTMLDivElement>;
|
||||||
|
@ -118,10 +120,10 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
const shouldUseInitials =
|
const shouldUseInitials =
|
||||||
!hasImage && conversationType === 'direct' && Boolean(initials);
|
!hasImage && conversationType === 'direct' && Boolean(initials);
|
||||||
|
|
||||||
let contents: ReactNode;
|
let contentsChildren: ReactNode;
|
||||||
if (loading) {
|
if (loading) {
|
||||||
const svgSize = size < 40 ? 'small' : 'normal';
|
const svgSize = size < 40 ? 'small' : 'normal';
|
||||||
contents = (
|
contentsChildren = (
|
||||||
<div className="module-Avatar__spinner-container">
|
<div className="module-Avatar__spinner-container">
|
||||||
<Spinner
|
<Spinner
|
||||||
size={`${size - 8}px`}
|
size={`${size - 8}px`}
|
||||||
|
@ -141,7 +143,7 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
const isBlurred =
|
const isBlurred =
|
||||||
blur === AvatarBlur.BlurPicture ||
|
blur === AvatarBlur.BlurPicture ||
|
||||||
blur === AvatarBlur.BlurPictureWithClickToView;
|
blur === AvatarBlur.BlurPictureWithClickToView;
|
||||||
contents = (
|
contentsChildren = (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className="module-Avatar__image"
|
className="module-Avatar__image"
|
||||||
|
@ -156,17 +158,16 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else if (noteToSelf) {
|
} else if (noteToSelf) {
|
||||||
contents = (
|
contentsChildren = (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'module-Avatar__icon',
|
'module-Avatar__icon',
|
||||||
`module-Avatar--${color}--icon`,
|
|
||||||
'module-Avatar__icon--note-to-self'
|
'module-Avatar__icon--note-to-self'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (shouldUseInitials) {
|
} else if (shouldUseInitials) {
|
||||||
contents = (
|
contentsChildren = (
|
||||||
<div
|
<div
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="module-Avatar__label"
|
className="module-Avatar__label"
|
||||||
|
@ -176,23 +177,29 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
contents = (
|
contentsChildren = (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'module-Avatar__icon',
|
'module-Avatar__icon',
|
||||||
`module-Avatar--${color}--icon`,
|
|
||||||
`module-Avatar__icon--${conversationType}`
|
`module-Avatar__icon--${conversationType}`
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let contents: ReactChild;
|
||||||
|
const contentsClassName = classNames(
|
||||||
|
'module-Avatar__contents',
|
||||||
|
`module-Avatar__contents--${color}`
|
||||||
|
);
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
contents = (
|
contents = (
|
||||||
<button className="module-Avatar__button" type="button" onClick={onClick}>
|
<button className={contentsClassName} type="button" onClick={onClick}>
|
||||||
{contents}
|
{contentsChildren}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
contents = <div className={contentsClassName}>{contentsChildren}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -201,9 +208,6 @@ export const Avatar: FunctionComponent<Props> = ({
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'module-Avatar',
|
'module-Avatar',
|
||||||
hasImage ? 'module-Avatar--with-image' : 'module-Avatar--no-image',
|
hasImage ? 'module-Avatar--with-image' : 'module-Avatar--no-image',
|
||||||
{
|
|
||||||
[`module-Avatar--${color}`]: !hasImage,
|
|
||||||
},
|
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -1206,33 +1206,27 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
'module-message__author-avatar-container--with-reactions': this.hasReactions(),
|
'module-message__author-avatar-container--with-reactions': this.hasReactions(),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button
|
<Avatar
|
||||||
type="button"
|
acceptedMessageRequest={author.acceptedMessageRequest}
|
||||||
className="module-message__author-avatar"
|
avatarPath={author.avatarPath}
|
||||||
onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
color={author.color}
|
||||||
|
conversationType="direct"
|
||||||
|
i18n={i18n}
|
||||||
|
isMe={author.isMe}
|
||||||
|
name={author.name}
|
||||||
|
onClick={event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
showContactModal(author.id);
|
showContactModal(author.id);
|
||||||
}}
|
}}
|
||||||
tabIndex={0}
|
phoneNumber={author.phoneNumber}
|
||||||
>
|
profileName={author.profileName}
|
||||||
<Avatar
|
sharedGroupNames={author.sharedGroupNames}
|
||||||
acceptedMessageRequest={author.acceptedMessageRequest}
|
size={28}
|
||||||
avatarPath={author.avatarPath}
|
title={author.title}
|
||||||
color={author.color}
|
unblurredAvatarPath={author.unblurredAvatarPath}
|
||||||
conversationType="direct"
|
/>
|
||||||
i18n={i18n}
|
|
||||||
isMe={author.isMe}
|
|
||||||
name={author.name}
|
|
||||||
phoneNumber={author.phoneNumber}
|
|
||||||
profileName={author.profileName}
|
|
||||||
sharedGroupNames={author.sharedGroupNames}
|
|
||||||
size={28}
|
|
||||||
title={author.title}
|
|
||||||
unblurredAvatarPath={author.unblurredAvatarPath}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue