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 { Button, Modal } from "@renderer/components";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import "./hydra-cloud-modal.scss";
export interface HydraCloudModalProps { export interface HydraCloudModalProps {
feature: string; feature: string;
@ -22,13 +22,8 @@ export const HydraCloudModal = ({
return ( return (
<Modal visible={visible} title={t("hydra_cloud")} onClose={onClose}> <Modal visible={visible} title={t("hydra_cloud")} onClose={onClose}>
<div <div
className="hydra-cloud-modal__container"
data-hydra-cloud-feature={feature} data-hydra-cloud-feature={feature}
style={{
display: "flex",
width: "500px",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
}}
> >
{t("hydra_cloud_feature_found")} {t("hydra_cloud_feature_found")}
<Button onClick={handleClickOpenCheckout}>{t("learn_more")}</Button> <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 { 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 { useTranslation } from "react-i18next";
import { Avatar } from "@renderer/components"; import { Avatar } from "@renderer/components";
import "./user-friend-item.scss";
export type UserFriendItemProps = { export type UserFriendItemProps = {
userId: string; userId: string;
@ -31,10 +30,9 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
const getRequestDescription = () => { const getRequestDescription = () => {
if (type === "ACCEPTED" || type === null) return null; if (type === "ACCEPTED" || type === null) return null;
return ( return (
<small> <small>
{type == "SENT" ? t("request_sent") : t("request_received")} {type === "SENT" ? t("request_sent") : t("request_received")}
</small> </small>
); );
}; };
@ -45,7 +43,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "SENT") { if (type === "SENT") {
return ( return (
<button <button
className={styles.cancelRequestButton} className="user-friend-item__cancel-button"
onClick={() => props.onClickCancelRequest(userId)} onClick={() => props.onClickCancelRequest(userId)}
title={t("cancel_request")} title={t("cancel_request")}
> >
@ -58,14 +56,14 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
return ( return (
<> <>
<button <button
className={styles.acceptRequestButton} className="user-friend-item__accept-button"
onClick={() => props.onClickAcceptRequest(userId)} onClick={() => props.onClickAcceptRequest(userId)}
title={t("accept_request")} title={t("accept_request")}
> >
<CheckCircleIcon size={28} /> <CheckCircleIcon size={28} />
</button> </button>
<button <button
className={styles.cancelRequestButton} className="user-friend-item__cancel-button"
onClick={() => props.onClickRefuseRequest(userId)} onClick={() => props.onClickRefuseRequest(userId)}
title={t("ignore_request")} title={t("ignore_request")}
> >
@ -78,7 +76,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "ACCEPTED") { if (type === "ACCEPTED") {
return ( return (
<button <button
className={styles.cancelRequestButton} className="user-friend-item__cancel-button"
onClick={() => props.onClickUndoFriendship(userId)} onClick={() => props.onClickUndoFriendship(userId)}
title={t("undo_friendship")} title={t("undo_friendship")}
> >
@ -90,7 +88,7 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "BLOCKED") { if (type === "BLOCKED") {
return ( return (
<button <button
className={styles.cancelRequestButton} className="user-friend-item__cancel-button"
onClick={() => props.onClickUnblock(userId)} onClick={() => props.onClickUnblock(userId)}
title={t("unblock")} title={t("unblock")}
> >
@ -104,10 +102,9 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
if (type === "BLOCKED") { if (type === "BLOCKED") {
return ( return (
<div className={styles.friendListContainer}> <div className="user-friend-item__container">
<div className={styles.friendListButton} style={{ cursor: "inherit" }}> <div className="user-friend-item__button" style={{ cursor: "inherit" }}>
<Avatar size={35} src={profileImageUrl} alt={displayName} /> <Avatar size={35} src={profileImageUrl} alt={displayName} />
<div <div
style={{ style={{
display: "flex", display: "flex",
@ -117,16 +114,15 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
minWidth: 0, minWidth: 0,
}} }}
> >
<p className={styles.friendListDisplayName}>{displayName}</p> <p className="user-friend-item__display-name">{displayName}</p>
</div> </div>
</div> </div>
<div <div
style={{ style={{
position: "absolute", position: "absolute",
right: "8px", right: "8px",
display: "flex", display: "flex",
gap: `${SPACING_UNIT}px`, gap: "8px",
}} }}
> >
{getRequestActions()} {getRequestActions()}
@ -136,10 +132,10 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
} }
return ( return (
<div className={styles.friendListContainer}> <div className="user-friend-item__container">
<button <button
type="button" type="button"
className={styles.friendListButton} className="user-friend-item__button"
onClick={() => props.onClickItem(userId)} onClick={() => props.onClickItem(userId)}
> >
<Avatar size={35} src={profileImageUrl} alt={displayName} /> <Avatar size={35} src={profileImageUrl} alt={displayName} />
@ -152,17 +148,16 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
minWidth: 0, minWidth: 0,
}} }}
> >
<p className={styles.friendListDisplayName}>{displayName}</p> <p className="user-friend-item__display-name">{displayName}</p>
{getRequestDescription()} {getRequestDescription()}
</div> </div>
</button> </button>
<div <div
style={{ style={{
position: "absolute", position: "absolute",
right: "8px", right: "8px",
display: "flex", display: "flex",
gap: `${SPACING_UNIT}px`, gap: "8px",
}} }}
> >
{getRequestActions()} {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 { Button, TextField } from "@renderer/components";
import { useToast, useUserDetails } from "@renderer/hooks"; import { useToast, useUserDetails } from "@renderer/hooks";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { UserFriendItem } from "./user-friend-item"; import { UserFriendItem } from "./user-friend-item";
import "./user-friend-modal-add-friend.scss";
export interface UserFriendModalAddFriendProps { export interface UserFriendModalAddFriendProps {
closeModal: () => void; closeModal: () => void;
@ -76,15 +76,7 @@ export const UserFriendModalAddFriend = ({
return ( return (
<> <>
<div <div className="user-friend-modal-add-friend__actions">
style={{
display: "flex",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
gap: `${SPACING_UNIT}px`,
}}
>
<TextField <TextField
label={t("friend_code")} label={t("friend_code")}
value={friendCode} value={friendCode}
@ -95,7 +87,7 @@ export const UserFriendModalAddFriend = ({
/> />
<Button <Button
disabled={isAddingFriend} disabled={isAddingFriend}
style={{ alignSelf: "end" }} className="user-friend-modal-add-friend__button"
type="button" type="button"
onClick={handleClickAddFriend} onClick={handleClickAddFriend}
> >
@ -105,20 +97,14 @@ export const UserFriendModalAddFriend = ({
<Button <Button
onClick={handleClickSeeProfile} onClick={handleClickSeeProfile}
disabled={isAddingFriend} disabled={isAddingFriend}
style={{ alignSelf: "end" }} className="user-friend-modal-add-friend__button"
type="button" type="button"
> >
{t("see_profile")} {t("see_profile")}
</Button> </Button>
</div> </div>
<div <div className="user-friend-modal-add-friend__pending-container">
style={{
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
}}
>
<h3>{t("pending")}</h3> <h3>{t("pending")}</h3>
{friendRequests.length === 0 && <p>{t("no_pending_invites")}</p>} {friendRequests.length === 0 && <p>{t("no_pending_invites")}</p>}
{friendRequests.map((request) => { {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 type { UserFriend } from "@types";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { UserFriendItem } from "./user-friend-item"; import { UserFriendItem } from "./user-friend-item";
@ -6,6 +5,7 @@ import { useNavigate } from "react-router-dom";
import { useToast, useUserDetails } from "@renderer/hooks"; import { useToast, useUserDetails } from "@renderer/hooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; import Skeleton, { SkeletonTheme } from "react-loading-skeleton";
import "./user-friend-modal-list.scss";
export interface UserFriendModalListProps { export interface UserFriendModalListProps {
userId: string; userId: string;
@ -94,20 +94,10 @@ export const UserFriendModalList = ({
}; };
return ( return (
<SkeletonTheme baseColor={vars.color.background} highlightColor="#444"> <SkeletonTheme baseColor="#1c1c1c" highlightColor="#444">
<div <div ref={listContainer} className="user-friend-modal-list">
ref={listContainer}
style={{
display: "flex",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
maxHeight: "400px",
overflowY: "scroll",
}}
>
{!isLoading && friends.length === 0 && <p>{t("no_friends_added")}</p>} {!isLoading && friends.length === 0 && <p>{t("no_friends_added")}</p>}
{friends.map((friend) => { {friends.map((friend) => (
return (
<UserFriendItem <UserFriendItem
userId={friend.id} userId={friend.id}
displayName={friend.displayName} displayName={friend.displayName}
@ -117,18 +107,8 @@ export const UserFriendModalList = ({
type={isMe ? "ACCEPTED" : null} type={isMe ? "ACCEPTED" : null}
key={"modal" + friend.id} key={"modal" + friend.id}
/> />
); ))}
})} {isLoading && <Skeleton className="user-friend-modal-list__skeleton" />}
{isLoading && (
<Skeleton
style={{
width: "100%",
height: "54px",
overflow: "hidden",
borderRadius: "4px",
}}
/>
)}
</div> </div>
</SkeletonTheme> </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 { Button, Modal } from "@renderer/components";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UserFriendModalAddFriend } from "./user-friend-modal-add-friend"; import { UserFriendModalAddFriend } from "./user-friend-modal-add-friend";
import { useToast, useUserDetails } from "@renderer/hooks"; import { useToast, useUserDetails } from "@renderer/hooks";
import { UserFriendModalList } from "./user-friend-modal-list"; import { UserFriendModalList } from "./user-friend-modal-list";
import { CopyIcon } from "@primer/octicons-react"; import { CopyIcon } from "@primer/octicons-react";
import * as styles from "./user-friend-modal.css"; import "./user-friend-modal.scss";
export enum UserFriendModalTab { export enum UserFriendModalTab {
FriendsList, FriendsList,
@ -27,15 +26,11 @@ export const UserFriendModal = ({
userId, userId,
}: UserFriendsModalProps) => { }: UserFriendsModalProps) => {
const { t } = useTranslation("user_profile"); const { t } = useTranslation("user_profile");
const tabs = [t("friends_list"), t("add_friends")]; const tabs = [t("friends_list"), t("add_friends")];
const [currentTab, setCurrentTab] = useState( const [currentTab, setCurrentTab] = useState(
initialTab || UserFriendModalTab.FriendsList initialTab || UserFriendModalTab.FriendsList
); );
const { showSuccessToast } = useToast(); const { showSuccessToast } = useToast();
const { userDetails } = useUserDetails(); const { userDetails } = useUserDetails();
const isMe = userDetails?.id == userId; const isMe = userDetails?.id == userId;
@ -64,35 +59,21 @@ export const UserFriendModal = ({
return ( return (
<Modal visible={visible} title={t("friends")} onClose={onClose}> <Modal visible={visible} title={t("friends")} onClose={onClose}>
<div <div className="user-friend-modal__container">
style={{
display: "flex",
width: "500px",
flexDirection: "column",
gap: `${SPACING_UNIT * 2}px`,
}}
>
{isMe && ( {isMe && (
<> <>
<div <div className="user-friend-modal__header">
style={{
display: "flex",
gap: `${SPACING_UNIT}px`,
alignItems: "center",
}}
>
<p>{t("your_friend_code")}</p> <p>{t("your_friend_code")}</p>
<button <button
className={styles.friendCodeButton} className="user-friend-modal__friend-code-button"
onClick={copyToClipboard} onClick={copyToClipboard}
> >
<h3>{userDetails.id}</h3> <h3>{userDetails.id}</h3>
<CopyIcon /> <CopyIcon />
</button> </button>
</div> </div>
<section style={{ display: "flex", gap: `${SPACING_UNIT}px` }}> <section className="user-friend-modal__tabs">
{tabs.map((tab, index) => { {tabs.map((tab, index) => (
return (
<Button <Button
key={tab} key={tab}
theme={index === currentTab ? "primary" : "outline"} theme={index === currentTab ? "primary" : "outline"}
@ -100,8 +81,7 @@ export const UserFriendModal = ({
> >
{tab} {tab}
</Button> </Button>
); ))}
})}
</section> </section>
</> </>
)} )}