Skip to content

Commit

Permalink
Readiness bubble for rest users (#2169)
Browse files Browse the repository at this point in the history
* Create progressCircle component

* Add color variable for progress circle

* Delete boardUser, move to userAvatar + boardUsers

* Refactor boardUsers scss

* Implement progressCircle

* Rewrite progressCircle

* WIP color changes

* Use variables

* Add new colors

* Use currentColor keyword

* Show check icon with animation

* Update color, and increase font size

* Apply review changes

Co-authored-by: Sebastian Schwarzer <[email protected]>
  • Loading branch information
CronJorian and Sebastian Schwarzer authored Sep 16, 2022
1 parent 275c63c commit b3f3a0b
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 147 deletions.
23 changes: 23 additions & 0 deletions src/components/BoardHeader/ParticipantsList/ProgressCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type ProgressCircleProps = {
className?: string;
percentage: number;
};

export const ProgressCircle = ({percentage, className}: ProgressCircleProps) => {
// clamp between 0 and 1
percentage = Math.min(Math.max(percentage, 0), 1);
return (
<svg viewBox="0 0 100 100" className={className}>
<circle
cx="50%"
cy="50%"
r="50%"
stroke="currentColor"
strokeWidth="100%"
strokeDasharray={`${Math.PI * 100}%`}
strokeDashoffset={`${Math.PI * 100 * (1 - percentage)}%`}
transform="rotate (-90 50 50)"
/>
</svg>
);
};
146 changes: 75 additions & 71 deletions src/components/BoardUsers/BoardUsers.scss
Original file line number Diff line number Diff line change
@@ -1,114 +1,118 @@
@import "constants/style.scss";

.board-users {
list-style-type: none;
align-self: center;
display: flex;
flex-direction: row;
width: max-content;
padding: 0;
margin: 0;
gap: $margin--default;
}

.board-users li {
display: inline;
}

.board-users__other-avatars {
box-sizing: border-box;
.board-users__button {
display: flex;
justify-content: center;
align-items: center;
padding: 4px;
background: none;
border: none;
outline: none;
display: flex;
flex-direction: row-reverse;
align-items: center;

cursor: pointer;
padding-left: $padding--default;
border-radius: 16px;

& .user-avatar,
& .rest-users {
margin-left: -12px;
transition: all 0.08s ease-out;
}

&:hover {
& > .rest-users {
transform: scale(1.16);
}
& .user-avatar {
filter: brightness(1.1);
transform: scale(1.08);
}
&--others {
flex-direction: row-reverse;
padding-left: $padding--default;
}

&:focus-visible {
box-shadow: 0 0 0 2px rgba($color-backlog-blue, 0.48);
}

&:active {
& > .rest-users,
& > :not(.rest-users) {
transform: scale(1);
&:hover {
.board-users__avatar {
filter: brightness(1.1);
transform: scale(1.08);
}
}
}

.board-users__my-avatar {
display: contents;
margin-left: $margin--small;

& > button {
border: none;
outline: none;
background: none;
border-radius: 16px;
cursor: pointer;
.board-users__avatar {
transition: all 0.08s ease-out;

&:focus-visible {
box-shadow: 0 0 0 2px rgba($color-backlog-blue, 0.48);
}
&--others {
margin-left: -12px;
}
}

.board-users__my-avatar .user-avatar {
transition: all 0.08s ease-out;
margin: 0;

&:hover {
filter: brightness(1.1);
transform: scale(1.08);
.rest-users {
position: relative;
font-size: $text-size--medium;
height: $icon--extralarge;
width: $icon--extralarge;
border-radius: $border-radius--round;
background-color: $menu-icon-background-color--dark;
padding: 2px;
color: $color-white;
font-weight: 700;

&__readiness {
color: $color-progress-circle;
border-radius: $border-radius--round;

circle {
transition: stroke-dashoffset 0.45s ease-in-out;
fill: transparent;
}
}

&:active {
transform: scale(1);
&__count,
&__all-ready {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
// line-height: 36px;
// font-size: $text-size--medium;
}
}

@media screen and (max-width: 768px) {
.board-users {
margin-right: $margin--small;
&__all-ready {
color: $color-white;
width: 100%;
height: 100%;

.circle {
display: none;
}

.check {
--dasharray: 20;
stroke-dasharray: var(--dasharray);
stroke-dashoffset: 0;
animation: draw 0.6s ease-in-out, scale 0.3s ease-out;
transform-origin: 37% 40%;
}
}
}

@media screen and (max-width: 500px) {
.board-users {
margin-right: 0;
@media #{$mini-smartphone} {
.board-users__button--others {
display: none;
}
}

.board-users__other-avatars {
display: none;
[theme="dark"] {
.rest-users {
background-color: $color-icon-light-blue;
color: $color-dark-two;
}

.board-users__my-avatar {
margin-right: 0;
.board-users__button {
&:focus-visible {
box-shadow: 0 0 0 2px rgba($color-planning-pink, 0.48);
}
}
}

[theme="dark"] {
.board-users__other-avatars:focus-visible,
.board-users__my-avatar > button:focus-visible {
box-shadow: 0 0 0 2px rgba($color-planning-pink, 0.48);
@keyframes draw {
0% {
stroke-dashoffset: var(--dasharray);
}
}
94 changes: 49 additions & 45 deletions src/components/BoardUsers/BoardUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {useEffect, useState} from "react";
import {useNavigate} from "react-router";
import {useTranslation} from "react-i18next";
import {ParticipantsList} from "components/BoardHeader/ParticipantsList";
import {ProgressCircle} from "components/BoardHeader/ParticipantsList/ProgressCircle";
import {ReactComponent as CheckIcon} from "assets/icon-check.svg";
import {UserAvatar} from "./UserAvatar";

const getWindowDimensions = () => {
Expand Down Expand Up @@ -43,53 +45,55 @@ export const BoardUsers = () => {
me: state.participants!.self,
}));

const usersToShow = them.slice().splice(0, them.length > NUM_OF_DISPLAYED_USERS ? NUM_OF_DISPLAYED_USERS - 1 : NUM_OF_DISPLAYED_USERS);
const usersRest = them.slice();
const usersToShow = usersRest.splice(0, them.length > NUM_OF_DISPLAYED_USERS ? NUM_OF_DISPLAYED_USERS - 1 : NUM_OF_DISPLAYED_USERS);

return (
<div>
<ul className="board-users">
{them.length > 0 && (
<button
className="board-users__other-avatars"
aria-label={t("BoardHeader.showParticipants")}
aria-haspopup
aria-pressed={showParticipants}
onClick={() => setShowParticipants(!showParticipants)}
>
{them.length > usersToShow.length && (
<li className="rest-users">
<div className="rest-users__count">{them.length - usersToShow.length}</div>
</li>
)}
{usersToShow
.sort((parA, parB) => parA.user.name.localeCompare(parB.user.name))
.map((participant) => (
<li key={participant.user.id}>
<UserAvatar
id={participant.user.id}
avatar={participant.user.avatar}
ready={participant.ready}
raisedHand={participant.raisedHand}
name={participant.user.name}
className="board-users__avatar"
/>
</li>
))}
</button>
)}
{!!me && (
<li className="board-users__my-avatar">
<button
onClick={(e) => {
e.stopPropagation();
navigate("settings/profile");
}}
>
<UserAvatar id={me.user.id} avatar={me.user.avatar} ready={me.ready} raisedHand={me.raisedHand} name={me.user.name} className="board-users__avatar" />
</button>
</li>
)}
</ul>
<div className="board-users">
{them.length > 0 && (
<button
className="board-users__button board-users__button--others"
aria-label={t("BoardHeader.showParticipants")}
aria-haspopup
aria-pressed={showParticipants}
onClick={() => setShowParticipants(!showParticipants)}
>
{usersRest.length && (
<div className="board-users__avatar board-users__avatar--others rest-users">
<ProgressCircle className="rest-users__readiness" percentage={usersRest.filter((participant) => participant.ready).length / usersRest.length} />
{usersRest.filter((participant) => participant.ready).length / usersRest.length < 1 ? (
<span className="rest-users__count">{usersRest.length}</span>
) : (
<CheckIcon className="rest-users__all-ready" />
)}
</div>
)}
{usersToShow
.sort((parA, parB) => parA.user.name.localeCompare(parB.user.name))
.map((participant) => (
<UserAvatar
key={participant.user.id}
id={participant.user.id}
avatar={participant.user.avatar}
ready={participant.ready}
raisedHand={participant.raisedHand}
name={participant.user.name}
className="board-users__avatar board-users__avatar--others"
/>
))}
</button>
)}
{!!me && (
<button
className="board-users__button board-users__button--me"
onClick={(e) => {
e.stopPropagation();
navigate("settings/profile");
}}
>
<UserAvatar id={me.user.id} avatar={me.user.avatar} ready={me.ready} raisedHand={me.raisedHand} name={me.user.name} className="board-users__avatar" />
</button>
)}
<ParticipantsList open={showParticipants} onClose={() => setShowParticipants(false)} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,19 @@
@import "constants/style.scss";

.user-avatar,
.rest-users {
.user-avatar {
box-sizing: border-box;
height: 48px;
width: 48px;
border-radius: 100px;
display: inline-block;
position: relative;
}

.user-avatar {
width: 48px;

#Circle-Background {
stroke: $color-white-one;
stroke-width: 24px;
}
}

.rest-users {
background-color: $menu-icon-background-color--dark;
color: $color-white;
height: 36px;
width: 36px;
}

.user__initials,
.rest-users__count {
position: relative;
color: var(--accent-color);
font-weight: bold;
line-height: 36px;
font-size: $text-size--medium;
padding-left: 4px;
}

.user-avatar__ready {
position: absolute;
top: -8px;
Expand Down Expand Up @@ -96,11 +75,6 @@
}

[theme="dark"] {
.user__initials,
.rest-users {
background-color: #9ebfff;
color: #333948;
}
.user-avatar__raised-hand {
color: $color-planning-pink;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/BoardUsers/UserAvatar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useEffect, useRef, useState} from "react";
import "./BoardUser.scss";
import "./UserAvatar.scss";
import classNames from "classnames";
import {ReactComponent as IconCheck} from "assets/icon-check.svg";
import {ReactComponent as RaisedHand} from "assets/icon-hand.svg";
Expand Down
4 changes: 2 additions & 2 deletions src/components/Infobar/Infobar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
}

.info-bar__return-to-focused-note-button > svg {
background-color: #333948;
background-color: $menu-icon-background-color--dark;
color: $color-white;
}
[theme="dark"] .info-bar__return-to-focused-note-button > svg {
background-color: $color-white;
color: #333948;
color: $menu-icon-background-color--dark;
}
Loading

0 comments on commit b3f3a0b

Please sign in to comment.