refactor: migrate shared modals styles from VE to SCSS + BEM

This commit is contained in:
Hachi-R 2025-01-21 15:23:17 -03:00
parent c51b61501e
commit 19976da82e
10 changed files with 195 additions and 114 deletions

View File

@ -0,0 +1,10 @@
@use "../../../scss/globals.scss";
.hydra-cloud-modal {
&__container {
display: flex;
width: 500px;
flex-direction: column;
gap: calc(globals.$spacing-unit * 2);
}
}

View File

@ -1,6 +1,6 @@
import { Button, Modal } from "@renderer/components";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useTranslation } from "react-i18next";
import "./hydra-cloud-modal.scss";
export interface HydraCloudModalProps {
feature: string;
@ -22,13 +22,8 @@ export const HydraCloudModal = ({
return (
<Modal visible={visible} title={t("hydra_cloud")} onClose={onClose}>
<div
className="hydra-cloud-modal__container"
data-hydra-cloud-feature={feature}
style={{
display: "flex",
width: "500px",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
}}
>
{t("hydra_cloud_feature_found")}
<Button onClick={handleClickOpenCheckout}>{t("learn_more")}</Button>

View File

@ -0,0 +1,64 @@
@use "../../../scss/globals.scss";
.user-friend-item {
&__container {
display: flex;
gap: calc(globals.$spacing-unit * 3);
align-items: center;
border-radius: 4px;
border: solid 1px globals.$border-color;
width: 100%;
height: 54px;
min-height: 54px;
transition: all ease 0.2s;
position: relative;
&:hover {
background-color: rgba(255, 255, 255, 0.15);
}
}
&__button {
display: flex;
align-items: center;
position: absolute;
cursor: pointer;
height: 100%;
width: 100%;
flex-direction: row;
color: globals.$body-color;
gap: calc(globals.$spacing-unit + globals.$spacing-unit / 2);
padding: 0 globals.$spacing-unit;
}
&__display-name {
font-weight: bold;
font-size: globals.$body-font-size;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&__accept-button {
cursor: pointer;
color: globals.$body-color;
width: 28px;
height: 28px;
&:hover {
color: globals.$success-color;
}
}
&__cancel-button {
cursor: pointer;
color: globals.$body-color;
width: 28px;
height: 28px;
&:hover {
color: globals.$danger-color;
}
}
}

View File

@ -1,8 +1,7 @@
import { CheckCircleIcon, XCircleIcon } from "@primer/octicons-react";
import * as styles from "./user-friend-modal.css";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useTranslation } from "react-i18next";
import { Avatar } from "@renderer/components";
import "./user-friend-item.scss";
export type UserFriendItemProps = {
userId: string;
@ -31,10 +30,9 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
const getRequestDescription = () => {
if (type === "ACCEPTED" || type === null) return null;
return (
<small>
{type == "SENT" ? t("request_sent") : t("request_received")}
{type === "SENT" ? t("request_sent") : t("request_received")}
</small>
);
};
@ -45,7 +43,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "SENT") {
return (
<button
className={styles.cancelRequestButton}
className="user-friend-item__cancel-button"
onClick={() => props.onClickCancelRequest(userId)}
title={t("cancel_request")}
>
@ -58,14 +56,14 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
return (
<>
<button
className={styles.acceptRequestButton}
className="user-friend-item__accept-button"
onClick={() => props.onClickAcceptRequest(userId)}
title={t("accept_request")}
>
<CheckCircleIcon size={28} />
</button>
<button
className={styles.cancelRequestButton}
className="user-friend-item__cancel-button"
onClick={() => props.onClickRefuseRequest(userId)}
title={t("ignore_request")}
>
@ -78,7 +76,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "ACCEPTED") {
return (
<button
className={styles.cancelRequestButton}
className="user-friend-item__cancel-button"
onClick={() => props.onClickUndoFriendship(userId)}
title={t("undo_friendship")}
>
@ -90,7 +88,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "BLOCKED") {
return (
<button
className={styles.cancelRequestButton}
className="user-friend-item__cancel-button"
onClick={() => props.onClickUnblock(userId)}
title={t("unblock")}
>
@ -104,10 +102,9 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "BLOCKED") {
return (
<div className={styles.friendListContainer}>
<div className={styles.friendListButton} style={{ cursor: "inherit" }}>
<div className="user-friend-item__container">
<div className="user-friend-item__button" style={{ cursor: "inherit" }}>
<Avatar size={35} src={profileImageUrl} alt={displayName} />
<div
style={{
display: "flex",
@ -117,16 +114,15 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
minWidth: 0,
}}
>
<p className={styles.friendListDisplayName}>{displayName}</p>
<p className="user-friend-item__display-name">{displayName}</p>
</div>
</div>
<div
style={{
position: "absolute",
right: "8px",
display: "flex",
gap: `${SPACING_UNIT}px`,
gap: "8px",
}}
>
{getRequestActions()}
@ -136,10 +132,10 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
}
return (
<div className={styles.friendListContainer}>
<div className="user-friend-item__container">
<button
type="button"
className={styles.friendListButton}
className="user-friend-item__button"
onClick={() => props.onClickItem(userId)}
>
<Avatar size={35} src={profileImageUrl} alt={displayName} />
@ -152,17 +148,16 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
minWidth: 0,
}}
>
<p className={styles.friendListDisplayName}>{displayName}</p>
<p className="user-friend-item__display-name">{displayName}</p>
{getRequestDescription()}
</div>
</button>
<div
style={{
position: "absolute",
right: "8px",
display: "flex",
gap: `${SPACING_UNIT}px`,
gap: "8px",
}}
>
{getRequestActions()}

View File

@ -0,0 +1,21 @@
@use "../../../scss/globals.scss";
.user-friend-modal-add-friend {
&__actions {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: globals.$spacing-unit;
}
&__button {
align-self: end;
}
&__pending-container {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 2);
}
}

View File

@ -1,10 +1,10 @@
import { Button, TextField } from "@renderer/components";
import { useToast, useUserDetails } from "@renderer/hooks";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { UserFriendItem } from "./user-friend-item";
import "./user-friend-modal-add-friend.scss";
export interface UserFriendModalAddFriendProps {
closeModal: () => void;
@ -76,15 +76,7 @@ export const UserFriendModalAddFriend = ({
return (
<>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
gap: `${SPACING_UNIT}px`,
}}
>
<div className="user-friend-modal-add-friend__actions">
<TextField
label={t("friend_code")}
value={friendCode}
@ -95,7 +87,7 @@ export const UserFriendModalAddFriend = ({
/>
<Button
disabled={isAddingFriend}
style={{ alignSelf: "end" }}
className="user-friend-modal-add-friend__button"
type="button"
onClick={handleClickAddFriend}
>
@ -105,20 +97,14 @@ export const UserFriendModalAddFriend = ({
<Button
onClick={handleClickSeeProfile}
disabled={isAddingFriend}
style={{ alignSelf: "end" }}
className="user-friend-modal-add-friend__button"
type="button"
>
{t("see_profile")}
</Button>
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
}}
>
<div className="user-friend-modal-add-friend__pending-container">
<h3>{t("pending")}</h3>
{friendRequests.length === 0 && <p>{t("no_pending_invites")}</p>}
{friendRequests.map((request) => {

View File

@ -0,0 +1,16 @@
@use "../../../scss/globals.scss";
.user-friend-modal-list {
display: flex;
flex-direction: column;
gap: calc(globals.$spacing-unit * 2);
max-height: 400px;
overflow-y: scroll;
&__skeleton {
width: 100%;
height: 54px;
overflow: hidden;
border-radius: 4px;
}
}

View File

@ -1,4 +1,3 @@
import { SPACING_UNIT, vars } from "@renderer/theme.css";
import type { UserFriend } from "@types";
import { useEffect, useRef, useState } from "react";
import { UserFriendItem } from "./user-friend-item";
@ -6,6 +5,7 @@ import { useNavigate } from "react-router-dom";
import { useToast, useUserDetails } from "@renderer/hooks";
import { useTranslation } from "react-i18next";
import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
import "./user-friend-modal-list.scss";
export interface UserFriendModalListProps {
userId: string;
@ -94,41 +94,21 @@ export const UserFriendModalList = ({
};
return (
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444">
<div
ref={listContainer}
style={{
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
maxHeight: "400px",
overflowY: "scroll",
}}
>
<SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
<div ref={listContainer} className="user-friend-modal-list">
{!isLoading && friends.length === 0 && <p>{t("no_friends_added")}</p>}
{friends.map((friend) => {
return (
<UserFriendItem
userId={friend.id}
displayName={friend.displayName}
profileImageUrl={friend.profileImageUrl}
onClickItem={handleClickFriend}
onClickUndoFriendship={handleUndoFriendship}
type={isMe ? "ACCEPTED" : null}
key={"modal" + friend.id}
/>
);
})}
{isLoading && (
<Skeleton
style={{
width: "100%",
height: "54px",
overflow: "hidden",
borderRadius: "4px",
}}
{friends.map((friend) => (
<UserFriendItem
userId={friend.id}
displayName={friend.displayName}
profileImageUrl={friend.profileImageUrl}
onClickItem={handleClickFriend}
onClickUndoFriendship={handleUndoFriendship}
type={isMe ? "ACCEPTED" : null}
key={"modal" + friend.id}
/>
)}
))}
{isLoading && <Skeleton className="user-friend-modal-list__skeleton" />}
</div>
</SkeletonTheme>
);

View File

@ -0,0 +1,34 @@
@use "../../../scss/globals.scss";
.user-friend-modal {
&__container {
display: flex;
width: 500px;
flex-direction: column;
gap: calc(globals.$spacing-unit * 2);
}
&__header {
display: flex;
gap: globals.$spacing-unit;
align-items: center;
}
&__friend-code-button {
color: globals.$body-color;
cursor: pointer;
display: flex;
gap: calc(globals.$spacing-unit / 2);
align-items: center;
transition: all ease 0.2s;
&:hover {
color: globals.$muted-color;
}
}
&__tabs {
display: flex;
gap: globals.$spacing-unit;
}
}

View File

@ -1,12 +1,11 @@
import { Button, Modal } from "@renderer/components";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { UserFriendModalAddFriend } from "./user-friend-modal-add-friend";
import { useToast, useUserDetails } from "@renderer/hooks";
import { UserFriendModalList } from "./user-friend-modal-list";
import { CopyIcon } from "@primer/octicons-react";
import * as styles from "./user-friend-modal.css";
import "./user-friend-modal.scss";
export enum UserFriendModalTab {
FriendsList,
@ -27,15 +26,11 @@ export const UserFriendModal = ({
userId,
}: UserFriendsModalProps) => {
const { t } = useTranslation("user_profile");
const tabs = [t("friends_list"), t("add_friends")];
const [currentTab, setCurrentTab] = useState(
initialTab || UserFriendModalTab.FriendsList
);
const { showSuccessToast } = useToast();
const { userDetails } = useUserDetails();
const isMe = userDetails?.id == userId;
@ -64,44 +59,29 @@ export const UserFriendModal = ({
return (
<Modal visible={visible} title={t("friends")} onClose={onClose}>
<div
style={{
display: "flex",
width: "500px",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
}}
>
<div className="user-friend-modal__container">
{isMe && (
<>
<div
style={{
display: "flex",
gap: `${SPACING_UNIT}px`,
alignItems: "center",
}}
>
<div className="user-friend-modal__header">
<p>{t("your_friend_code")}</p>
<button
className={styles.friendCodeButton}
className="user-friend-modal__friend-code-button"
onClick={copyToClipboard}
>
<h3>{userDetails.id}</h3>
<CopyIcon />
</button>
</div>
<section style={{ display: "flex", gap: `${SPACING_UNIT}px` }}>
{tabs.map((tab, index) => {
return (
<Button
key={tab}
theme={index === currentTab ? "primary" : "outline"}
onClick={() => setCurrentTab(index)}
>
{tab}
</Button>
);
})}
<section className="user-friend-modal__tabs">
{tabs.map((tab, index) => (
<Button
key={tab}
theme={index === currentTab ? "primary" : "outline"}
onClick={() => setCurrentTab(index)}
>
{tab}
</Button>
))}
</section>
</>
)}