refactor: style with tailwind
This commit is contained in:
@@ -1,43 +0,0 @@
|
|||||||
import { useState, useEffect, memo, SyntheticEvent, FC, ImgHTMLAttributes } from "react";
|
|
||||||
import { getInitials, getInitialsAvatar } from "../utils";
|
|
||||||
|
|
||||||
interface Props extends ImgHTMLAttributes<HTMLImageElement> {
|
|
||||||
// className?: string;
|
|
||||||
// alt?: string;
|
|
||||||
// src?: string;
|
|
||||||
name?: string;
|
|
||||||
type?: "user" | "channel";
|
|
||||||
}
|
|
||||||
|
|
||||||
const Avatar: FC<Props> = ({ src = "", name = "Deleted User", type = "user", ...rest }) => {
|
|
||||||
const [url, setUrl] = useState("");
|
|
||||||
const handleError = (err: SyntheticEvent<HTMLImageElement>) => {
|
|
||||||
console.error("load avatar error", err);
|
|
||||||
const tmp = getInitialsAvatar({
|
|
||||||
initials: getInitials(name),
|
|
||||||
background: type == "channel" ? "#EAECF0" : undefined,
|
|
||||||
foreground: type == "channel" ? "#475467" : undefined
|
|
||||||
});
|
|
||||||
setUrl(tmp);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!src) {
|
|
||||||
const tmp = getInitialsAvatar({
|
|
||||||
initials: getInitials(name),
|
|
||||||
background: type == "channel" ? "#EAECF0" : undefined,
|
|
||||||
foreground: type == "channel" ? "#475467" : undefined
|
|
||||||
});
|
|
||||||
setUrl(tmp);
|
|
||||||
} else {
|
|
||||||
setUrl(src);
|
|
||||||
}
|
|
||||||
}, [src, name]);
|
|
||||||
if (!url) return null;
|
|
||||||
|
|
||||||
return <img src={url} onError={handleError} {...rest} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(Avatar, (prev, next) => {
|
|
||||||
return prev.src == next.src;
|
|
||||||
});
|
|
||||||
@@ -1,36 +1,12 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
const StyledDivider = styled.hr`
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
border: 0;
|
|
||||||
border-top: 1px solid #e3e5e8;
|
|
||||||
margin: 25px 0;
|
|
||||||
overflow: visible;
|
|
||||||
&:before {
|
|
||||||
background: #fff;
|
|
||||||
padding: 2px 4px;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate3d(-50%, -50%, 0);
|
|
||||||
content: attr(data-content);
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 18px;
|
|
||||||
color: #78787c;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Divider: FC<Props> = ({ content }) => {
|
const Divider: FC<Props> = ({ content }) => {
|
||||||
if (!content) return null;
|
return <div className="relative border-none h-[1px] bg-[#e3e5e8] my-6 overflow-visible">
|
||||||
return <StyledDivider data-content={content}></StyledDivider>;
|
<span className="p-1 absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-xs text-gray-500 font-semibold bg-white">{content}</span>
|
||||||
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Divider;
|
export default Divider;
|
||||||
|
|||||||
@@ -1,21 +1,4 @@
|
|||||||
import { FC, ReactEventHandler, useState } from "react";
|
import { FC, ReactEventHandler, useState } from "react";
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
.err {
|
|
||||||
padding: 18px;
|
|
||||||
/* font-weight: bold; */
|
|
||||||
font-size: 16px;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
audio {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -31,13 +14,13 @@ const Audio: FC<Props> = ({ url }) => {
|
|||||||
|
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
return (
|
return (
|
||||||
<Styled>
|
<div className="flex items-center justify-center h-full">
|
||||||
{err ? (
|
{err ? (
|
||||||
<div className="err">Unable to play this audio</div>
|
<div className="p-[18px] text-base text-gray-500">Unable to play this audio</div>
|
||||||
) : (
|
) : (
|
||||||
<audio controls src={url} onError={handleError} />
|
<audio className="w-full" controls src={url} onError={handleError} />
|
||||||
)}
|
)}
|
||||||
</Styled>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,4 @@
|
|||||||
import { useState, useEffect, FC } from "react";
|
import { useState, useEffect, FC } from "react";
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
height: 218px;
|
|
||||||
padding: 15px 15px 0 15px;
|
|
||||||
line-height: 1.4;
|
|
||||||
overflow: scroll;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
background-color: #000;
|
|
||||||
color: #eee;
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
@@ -29,7 +17,7 @@ const Code: FC<Props> = ({ url }) => {
|
|||||||
}, [url]);
|
}, [url]);
|
||||||
if (!content) return null;
|
if (!content) return null;
|
||||||
|
|
||||||
return <Styled>{content}</Styled>;
|
return <div className="h-[218px] p-[15px] pb-0 bg-black text-white overflow-scroll whitespace-pre-wrap break-all leading-snug">{content}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Code;
|
export default Code;
|
||||||
|
|||||||
@@ -1,27 +1,14 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
height: 218px;
|
|
||||||
overflow: hidden;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
alt?: string;
|
alt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Image: FC<Props> = ({ url, alt }) => {
|
const Image: FC<Props> = ({ url, alt }) => {
|
||||||
if (!url) return null;
|
|
||||||
return (
|
return (
|
||||||
<Styled>
|
<div className="h-[218px] overflow-hidden">
|
||||||
<img src={url} alt={alt} />
|
<img className="w-full h-full object-cover" src={url} alt={alt} />
|
||||||
</Styled>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,14 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
padding: 8px;
|
|
||||||
/* height: 218px; */
|
|
||||||
overflow: hidden;
|
|
||||||
embed {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Pdf: FC<Props> = ({ url }) => {
|
const Pdf: FC<Props> = ({ url }) => {
|
||||||
// const [content, setContent] = useState("");
|
|
||||||
// const [pageNumber, setPageNumber] = useState(1);
|
|
||||||
// const [numPages, setNumPages] = useState(null);
|
|
||||||
// const onDocumentLoadSuccess = ({ numPages }) => {
|
|
||||||
// setNumPages(numPages);
|
|
||||||
// };
|
|
||||||
return (
|
return (
|
||||||
<Styled>
|
<div className="p-2 overflow-hidden">
|
||||||
<embed src={url} type="application/pdf" />
|
<embed className="w-full h-full" src={url} type="application/pdf" />
|
||||||
{/* <Document file={url} onLoadSuccess={onDocumentLoadSuccess}>
|
</div>
|
||||||
<Page pageNumber={pageNumber} />
|
|
||||||
</Document> */}
|
|
||||||
</Styled>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
height: 218px;
|
|
||||||
video {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Video: FC<Props> = ({ url }) => {
|
const Video: FC<Props> = ({ url }) => {
|
||||||
return (
|
return (
|
||||||
<Styled>
|
<div className="h-[218px]">
|
||||||
<video controls src={url} />
|
<video className="w-full h-full object-cover" controls src={url} />
|
||||||
</Styled>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,4 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
background: #ecfdff;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
.progress {
|
|
||||||
transition: all 0.25s ease;
|
|
||||||
height: 8px;
|
|
||||||
background: #088ab2;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: number;
|
value: number;
|
||||||
width?: string;
|
width?: string;
|
||||||
@@ -21,9 +6,9 @@ interface Props {
|
|||||||
|
|
||||||
const Progress: FC<Props> = ({ value, width = "100%" }) => {
|
const Progress: FC<Props> = ({ value, width = "100%" }) => {
|
||||||
return (
|
return (
|
||||||
<Styled style={{ width }}>
|
<div className="bg-[#ecfdff] rounded h-2 overflow-hidden" style={{ width }}>
|
||||||
<div className="progress" style={{ width: `${value}%` }}></div>
|
<div className="h-2 bg-[#088ab2] rounded transition-all" style={{ width: `${value}%` }}></div>
|
||||||
</Styled>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
import InviteByEmail from "./InviteByEmail";
|
import InviteByEmail from "./InviteByEmail";
|
||||||
import AddMembers from "./AddMembers";
|
import AddMembers from "./AddMembers";
|
||||||
import CloseIcon from "../../../assets/icons/close.svg";
|
import CloseIcon from "../../../assets/icons/close.svg";
|
||||||
@@ -7,28 +6,6 @@ import { useAppSelector } from "../../../app/store";
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const Styled = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0 25px 50px rgba(31, 41, 55, 0.25);
|
|
||||||
border-radius: var(--br);
|
|
||||||
padding: 16px;
|
|
||||||
min-width: 408px;
|
|
||||||
> .title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 28px;
|
|
||||||
color: #374151;
|
|
||||||
.close {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type?: "server" | "channel";
|
type?: "server" | "channel";
|
||||||
@@ -48,14 +25,14 @@ const InviteModal: FC<Props> = ({ type = "server", cid, title = "", closeModal }
|
|||||||
const finalTitle = type == "server" ? server.name : `#${title || channel?.name}`;
|
const finalTitle = type == "server" ? server.name : `#${title || channel?.name}`;
|
||||||
return (
|
return (
|
||||||
<Modal>
|
<Modal>
|
||||||
<Styled>
|
<div className="flex flex-col bg-white rounded-lg p-4 min-w-[408px]">
|
||||||
<h2 className="title">
|
<h2 className="flex items-center justify-between text-lg text-gray-700 ">
|
||||||
{t("invite_title", { name: finalTitle })}
|
{t("invite_title", { name: finalTitle })}
|
||||||
<CloseIcon className="close" onClick={closeModal} />
|
<CloseIcon className="cursor-pointer" onClick={closeModal} />
|
||||||
</h2>
|
</h2>
|
||||||
{!channel?.is_public && <AddMembers cid={cid} closeModal={closeModal} />}
|
{!channel?.is_public && <AddMembers cid={cid} closeModal={closeModal} />}
|
||||||
<InviteByEmail cid={channel?.is_public ? undefined : cid} />
|
<InviteByEmail cid={channel?.is_public ? undefined : cid} />
|
||||||
</Styled>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import styled from "styled-components";
|
|
||||||
import { NavLink, useLocation } from "react-router-dom";
|
import { NavLink, useLocation } from "react-router-dom";
|
||||||
import Tippy from "@tippyjs/react";
|
import Tippy from "@tippyjs/react";
|
||||||
import addIcon from "../../assets/icons/add.svg?url";
|
import addIcon from "../../assets/icons/add.svg?url";
|
||||||
@@ -7,50 +6,6 @@ import AddEntriesMenu from "./AddEntriesMenu";
|
|||||||
import { useAppSelector } from "../../app/store";
|
import { useAppSelector } from "../../app/store";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const StyledWrapper = styled.div`
|
|
||||||
min-height: 56px;
|
|
||||||
position: relative;
|
|
||||||
padding: 10px 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 8px;
|
|
||||||
.server {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
.logo {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
.name {
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 100%;
|
|
||||||
color: #374151;
|
|
||||||
}
|
|
||||||
.desc {
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 100%;
|
|
||||||
color: #78787c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.add {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
type Props = {
|
type Props = {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
};
|
};
|
||||||
@@ -67,42 +22,42 @@ export default function Server({ readonly = false }: Props) {
|
|||||||
const { name, description, logo } = server;
|
const { name, description, logo } = server;
|
||||||
if (readonly)
|
if (readonly)
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<div className="relative flex items-center justify-between gap-2 px-4 py-2">
|
||||||
<div className="server">
|
<div className="flex items-center gap-2">
|
||||||
<div className="logo">
|
<div className="w-8 h-8">
|
||||||
<img alt={`${name} logo`} src={logo} />
|
<img alt={`${name} logo`} className="w-full h-full object-cover rounded-full" src={logo} />
|
||||||
</div>
|
</div>
|
||||||
<div className="info">
|
<div className="flex flex-col gap-1">
|
||||||
<h3 className="name" title={description}>
|
<h3 className="text-sm text-gray-600" title={description}>
|
||||||
{name}
|
{name}
|
||||||
</h3>
|
</h3>
|
||||||
<span className="desc">{userCount} {t("members")}</span>
|
<span className="text-xs text-gray-500">{userCount} {t("members")}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</StyledWrapper>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<div className="relative flex items-center justify-between gap-2 px-4 py-2">
|
||||||
<NavLink to={`/setting?f=${pathname}`}>
|
<NavLink to={`/setting?f=${pathname}`}>
|
||||||
<div className="server">
|
<div className="flex items-center gap-2">
|
||||||
<div className="logo">
|
<div className="w-8 h-8">
|
||||||
<img alt={`${name} logo`} src={logo} />
|
<img alt={`${name} logo`} className="w-full h-full object-cover rounded-full" src={logo} />
|
||||||
</div>
|
</div>
|
||||||
<div className="info">
|
<div className="flex flex-col gap-1">
|
||||||
<h3 className="name" title={description}>
|
<h3 className="text-sm text-gray-600 font-bold" title={description}>
|
||||||
{name}
|
{name}
|
||||||
</h3>
|
</h3>
|
||||||
<span className="desc">{userCount} {t("members")}</span>
|
<span className="text-xs text-gray-500">{userCount} {t("members")}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<Tooltip tip={t("more")} placement="bottom">
|
<Tooltip tip={t("more")} placement="bottom">
|
||||||
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
<Tippy interactive placement="bottom-end" trigger="click" content={<AddEntriesMenu />}>
|
||||||
<img src={addIcon} alt="add icon" className="add" />
|
<img src={addIcon} alt="add icon" className="cursor-pointer" />
|
||||||
</Tippy>
|
</Tippy>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</StyledWrapper>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user