refactor: polish the tailwind refactor
This commit is contained in:
@@ -42,11 +42,11 @@ export default function AddEntriesMenu() {
|
||||
setChannelModalVisible(false);
|
||||
};
|
||||
|
||||
const itemClass = "rounded flex items-center gap-2 text-sm font-semibold cursor-pointer px-2 py-2.5 hover:bg-gray-800/20";
|
||||
const iconClass = "w-5 h-5";
|
||||
const itemClass = "rounded flex items-center gap-2 text-sm font-semibold cursor-pointer px-2 py-2.5 hover:bg-gray-800/20 dark:hover:bg-gray-200/20";
|
||||
const iconClass = "w-5 h-5 dark:fill-gray-300";
|
||||
return (
|
||||
<>
|
||||
<ul className="flex flex-col rounded-xl drop-shadow p-1 select-none text-gray-500 bg-white">
|
||||
<ul className="flex flex-col rounded-xl drop-shadow p-1 select-none text-gray-500 dark:text-gray-300 bg-white dark:bg-black">
|
||||
{/* temp remove public channel */}
|
||||
{currentUser?.is_admin && (
|
||||
<li className={itemClass} onClick={handleOpenChannelModal.bind(null, false)}>
|
||||
|
||||
@@ -31,7 +31,7 @@ const Avatar: FC<Props> = ({
|
||||
}) => {
|
||||
const [avatarSrc, setAvatarSrc] = useState("");
|
||||
useEffect(() => {
|
||||
if (!src) {
|
||||
if (src) {
|
||||
setAvatarSrc(src);
|
||||
}
|
||||
}, [src]);
|
||||
|
||||
@@ -39,7 +39,7 @@ const Channel: FC<Props> = ({ interactive = true, id, compact = false, avatarSiz
|
||||
</div>
|
||||
{!compact && (
|
||||
<div className="flex text-sm text-gray-500 font-semibold">
|
||||
<span className="max-w-[140px] whitespace-nowrap overflow-hidden text-ellipsis">{name}</span> ({is_public ? totalMemberCount : members.length})
|
||||
<span className="max-w-[140px] truncate">{name}</span> ({is_public ? totalMemberCount : members.length})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -92,7 +92,7 @@ const FileBox: FC<Props> = ({
|
||||
<div className="w-full p-2 flex items-center justify-between gap-2">
|
||||
{icon}
|
||||
<div className="flex flex-col gap-1 w-full overflow-hidden">
|
||||
<span className="font-semibold text-sm text-gray-800 dark:text-gray-200 whitespace-nowrap text-ellipsis">{name}</span>
|
||||
<span className="font-semibold text-sm text-gray-800 dark:text-gray-200 truncate">{name}</span>
|
||||
<em className="text-xs text-gray-500 flex gap-4 not-italic">
|
||||
<span className="size">{formatBytes(size)}</span>
|
||||
<span className="time">{dayjs(created_at).fromNow()}</span>
|
||||
|
||||
@@ -23,7 +23,7 @@ const AudioMessage = ({ url, name, size, download }: Props) => {
|
||||
<div className="flex gap-2 ">
|
||||
<IconAudio className="w-9 h-auto" />
|
||||
<div className="flex flex-col gap-1 text-sm text-gray-700">
|
||||
<span title={name} className='font-bold whitespace-nowrap text-ellipsis w-[240px] overflow-hidden'>{name}</span>
|
||||
<span title={name} className='font-bold w-[240px] truncate'>{name}</span>
|
||||
<span>{_size}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ const VideoMessage = ({ url, name, size, download }: Props) => {
|
||||
<div className="flex gap-2 ">
|
||||
<IconVideo className="w-9 h-auto" />
|
||||
<div className="flex flex-col gap-1 text-sm text-white">
|
||||
<span title={name} className='font-bold whitespace-nowrap text-ellipsis w-[240px] overflow-hidden'>{name}</span>
|
||||
<span title={name} className='font-bold w-[240px] truncate'>{name}</span>
|
||||
<span>{_size}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -156,11 +156,11 @@ const FileMessage: FC<Props> = ({
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<div className={clsx(`file_message bg-slate-50 dark:bg-slate-900 border border-solid border-gray-300 dark:border-gray-500 box-border w-[370px] h-[66px] rounded-md`, sending && "opacity-90")}>
|
||||
<div className="p-2 flex items-center justify-between gap-2">
|
||||
<div className={clsx(`file_message bg-slate-50 dark:bg-slate-900 border border-solid border-gray-300 dark:border-gray-500 box-border w-[370px] rounded-md`, sending && "opacity-90")}>
|
||||
<div className="px-2 py-3 flex items-center justify-between gap-2">
|
||||
{icon}
|
||||
<div className="flex flex-col gap-1 w-full overflow-hidden">
|
||||
<span className="font-semibold text-sm text-gray-800 dark:text-gray-100 whitespace-nowrap text-ellipsis">{name}</span>
|
||||
<span className="font-semibold text-sm text-gray-800 dark:text-gray-100 truncate">{name}</span>
|
||||
<span className="whitespace-nowrap text-xs text-gray-500 dark:text-gray-300 flex gap-4">
|
||||
{sending ? (
|
||||
<Progress value={progress} width={"80%"} />
|
||||
|
||||
@@ -44,7 +44,7 @@ const GoogleLoginInner: FC<Props> = ({ type = "login", loaded, loadError }) => {
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<Button className=" group relative w-full bg-white dark:bg-[#384250] text-gray-600 dark:text-gray-200 !h-[46px] overflow-hidden border border-solid border-[#d0d5dd]" disabled={!loaded || isLoading}>
|
||||
<Button className=" group relative w-full !bg-white dark:!bg-[#384250] !text-gray-600 dark:!text-gray-200 !h-[46px] overflow-hidden border border-solid border-[#d0d5dd]" disabled={!loaded || isLoading}>
|
||||
<div className="absolute left-0 top-0 w-full flex-center gap-3 z-[998] h-10 bg-inherit">
|
||||
<IconGoogle className="w-6 h-6" />
|
||||
{loadError
|
||||
|
||||
@@ -14,7 +14,7 @@ const renderContent = (data: MessagePayload) => {
|
||||
switch (content_type) {
|
||||
case ContentTypes.text:
|
||||
res = (
|
||||
<span className="text-ellipsis overflow-hidden break-words break-all text-gray-800 dark:text-gray-100">
|
||||
<span className="truncate break-words break-all text-gray-800 dark:text-gray-100">
|
||||
<LinkifyText text={content} url={false} mentionTextOnly={true} mentionPopOver={false} />
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function URLPreview({ url = "" }) {
|
||||
const { title, description, ogImage } = data;
|
||||
|
||||
const containerClass = `flex items-center border border-solid border-gray-300 box-border rounded-md w-[380px]`;
|
||||
const dotsClass = `overflow-hidden whitespace-nowrap text-ellipsis`;
|
||||
const dotsClass = `truncate`;
|
||||
return ogImage ? (
|
||||
<a className={`${containerClass} flex-col !items-start p-3`} href={url} target="_blank" rel="noreferrer">
|
||||
<h3 className={`text-base text-primary-500 w-full ${dotsClass}`}>{title}</h3>
|
||||
|
||||
@@ -111,8 +111,8 @@ const Message: FC<IProps> = ({
|
||||
trigger="click"
|
||||
content={<Profile uid={fromUid || 0} type="card" cid={context == "user" ? 0 : contextId} />}
|
||||
>
|
||||
<div className="cursor-pointer" data-uid={fromUid} ref={avatarRef}>
|
||||
<Avatar width={40} height={40} src={currUser?.avatar} name={currUser?.name} />
|
||||
<div className="cursor-pointer w-10 h-10 flex-shrink-0" data-uid={fromUid} ref={avatarRef}>
|
||||
<Avatar className="w-10 h-10 rounded-full object-cover" width={40} height={40} src={currUser?.avatar} name={currUser?.name} />
|
||||
</div>
|
||||
</Tippy>
|
||||
<ContextMenu
|
||||
|
||||
@@ -44,7 +44,7 @@ const Profile: FC<Props> = ({ uid, type = "embed", cid }) => {
|
||||
const isCard = type == 'card';
|
||||
const canRemoveFromServer = !isCard && canRemove;
|
||||
const hasMore = email || canRemoveFromChannel || canRemoveFromServer;
|
||||
const iconClass = `cursor-pointer flex flex-col items-center gap-1 rounded-lg w-32 text-primary-400 bg-gray-100 dark:bg-gray-800 text-sm pt-3.5 pb-3`;
|
||||
const iconClass = `cursor-pointer flex flex-col items-center gap-1 rounded-lg w-32 text-primary-400 bg-gray-50 dark:bg-gray-800 text-sm pt-3.5 pb-3`;
|
||||
const containerClass = clsx(`flex-center flex-col gap-1 z-[99] mt-20 select-none`, isCard ? "p-4 w-[280px] bg-white dark:bg-gray-800 drop-shadow rounded-md" : "w-[432px]");
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
|
||||
@@ -13,10 +13,10 @@ const SaveTip: FC<Props> = ({ saveHandler, resetHandler }) => {
|
||||
const { t } = useTranslation("setting");
|
||||
// const btnClass=clsx("")
|
||||
return (
|
||||
<div className="w-full p-2 absolute bottom-16 left-0 flex items-center justify-between text-gray-500 border border-solid border-gray-200 dark:border-gray-400 dark:bg-gray-600 shadow-md rounded-full">
|
||||
<div className="w-full p-2 absolute bottom-16 left-0 flex items-center justify-between font-semibold text-gray-700 border border-solid border-gray-200 dark:border-gray-400 dark:bg-gray-600 shadow-md rounded-full">
|
||||
<span className="p-2 text-sm dark:text-gray-200">{t('save_tip')}</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button className="small ghost !border-none !text-gray-500 !shadow-none dark:!text-gray-100" onClick={resetHandler}>
|
||||
<Button className="small ghost border_less !text-gray-700 !shadow-none dark:!text-gray-100" onClick={resetHandler}>
|
||||
{t('reset')}
|
||||
</Button>
|
||||
<Button className="small !rounded-full" onClick={saveHandler}>
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function UploadFileList({
|
||||
<div className="flex-center w-40 h-40">
|
||||
{type.startsWith("image") ? <img className="w-full h-full object-cover" src={url} alt="image" /> : getFileIcon(type, name)}
|
||||
</div>
|
||||
<h4 className="w-40 mt-4 mb-0.5 font-semibold text-sm text-gray-800 dark:text-gray-100 whitespace-nowrap overflow-hidden text-ellipsis">{name}</h4>
|
||||
<h4 className="w-40 mt-4 mb-0.5 font-semibold text-sm text-gray-800 dark:text-gray-100 truncate">{name}</h4>
|
||||
<span className="text-xs text-gray-500">{formatBytes(size)}</span>
|
||||
<ul className="invisible group-hover:visible bg-inherit border border-solid border-black/10 box-border rounded-md flex items-center absolute -right-5 -top-2.5">
|
||||
<li className="p-1 cursor-pointer edit" onClick={handleOpenEditModal.bind(null, idx)}>
|
||||
|
||||
@@ -45,12 +45,12 @@ const StyledSettingContainer: FC<PropsWithChildren<Props>> = ({
|
||||
);
|
||||
})}
|
||||
{dangers.length ? (
|
||||
<ul className="flex flex-col gap-2 mb-9">
|
||||
<ul className="flex flex-col gap-2 mb-9 text-sm font-semibold text-red-500 dark:text-red-400">
|
||||
{dangers.map((d) => {
|
||||
if (typeof d === "boolean" || !d) return null;
|
||||
const { title, handler } = d;
|
||||
return (
|
||||
<li key={title} onClick={handler} className="text-sm text-red-500 dark:text-red-400 rounded cursor-pointer py-1.5 px-3">
|
||||
<li key={title} onClick={handler} className="rounded cursor-pointer py-1.5 px-3">
|
||||
{title}
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -44,7 +44,7 @@ const User: FC<Props> = ({
|
||||
if (!curr) return null;
|
||||
const online = curr.online || curr.uid == loginUid;
|
||||
const containerClass = clsx(`relative flex items-center justify-start gap-2 p-2 rounded-lg select-none `, interactive && "hover:bg-gray-500/10", compact && "p-0");
|
||||
const nameClass = clsx(`text-sm text-gray-500 max-w-[190px] overflow-hidden text-ellipsis font-semibold dark:text-white`);
|
||||
const nameClass = clsx(`text-sm text-gray-500 max-w-[190px] truncate font-semibold dark:text-white`);
|
||||
const statusClass = clsx(`absolute -bottom-0.5 -right-1.5 w-3 h-3 box-content rounded-full border-[2px] border-solid border-white dark:border-gray-300`,
|
||||
online ? "bg-[#22c55e]" : "bg-[#a1a1aa]",
|
||||
compact && "w-3.5 h-3.5");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import dayjs from "dayjs";
|
||||
import { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useGetServerVersionQuery } from "../../app/services/server";
|
||||
@@ -5,11 +6,12 @@ type Props = {};
|
||||
const Version: FC<Props> = () => {
|
||||
const { t } = useTranslation("setting", { keyPrefix: "version" });
|
||||
const { data: serverVersion } = useGetServerVersionQuery();
|
||||
const ts = process.env.REACT_APP_BUILD_TIME;
|
||||
return (
|
||||
<div className="flex flex-col gap-3 dark:text-white">
|
||||
<div className="item">{t("client_version")}: {process.env.VERSION}</div>
|
||||
<div className="item">{t("server_version")}: {serverVersion}</div>
|
||||
<div className="item">{t("build_time")}: {process.env.REACT_APP_BUILD_TIME}</div>
|
||||
<div className="item">{t("build_time")}: {ts} {dayjs(ts).fromNow()}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,8 +12,8 @@ const StyledButton = ({ children, className = '', ...rest }: Props) => {
|
||||
const isFull = className.includes('flex');
|
||||
return <button className={clsx(`text-sm text-white bg-primary-400 break-keep shadow rounded-lg box-border px-3.5 py-2.5 hover:bg-primary-500 active:bg-primary-500 disabled:bg-gray-300`,
|
||||
isFull && "w-full",
|
||||
isGhost && "text-primary-400 border border-primary-400 !bg-transparent",
|
||||
isCancel && "!bg-transparent text-gray-800 dark:text-gray-50 border border-gray-200",
|
||||
isGhost && "text-primary-400 border border-solid border-primary-400 !bg-transparent",
|
||||
isCancel && "!bg-transparent text-gray-800 dark:text-gray-50 border border-solid border-gray-200",
|
||||
isSmall && "!py-2",
|
||||
noBorder && "!shadow-none border-none",
|
||||
isMini && "!px-2.5 !py-1 !text-xs",
|
||||
|
||||
@@ -38,25 +38,25 @@ const Input: FC<Props> = ({ type = "text", prefix = "", className = "", ...rest
|
||||
// const noInner=!className.includes("inner");
|
||||
const isPwd = type == "password";
|
||||
const inputClass = clsx(`w-full bg-white dark:bg-gray-800 text-sm text-gray-800 dark:text-gray-200 p-2 outline-none disabled:text-gray-500 disabled:bg-gray-100 dark:disabled:text-gray-500 dark:disabled:bg-gray-800/50 placeholder:text-gray-400`,
|
||||
// noInner && 'rounded border border-gray-200 shadow',
|
||||
// noInner && 'rounded border border-solid border-gray-200 shadow',
|
||||
isLarge && 'py-3',
|
||||
isNone && "border-none bg-transparent shadow-none",
|
||||
isPwd && "pr-[30px]"
|
||||
);
|
||||
return type == "password" ? (
|
||||
<div className={`w-full relative flex overflow-hidden rounded border border-gray-300 dark:border-gray-400 shadow ${className}`}>
|
||||
<div className={`w-full relative flex overflow-hidden rounded border border-solid border-gray-300 dark:border-gray-400 shadow-sm ${className}`}>
|
||||
<input type={inputType} autoComplete={inputType == "password" ? "current-password" : "on"} className={`${inputClass} ${className}`} {...rest} />
|
||||
<div className="absolute top-1/2 right-2.5 -translate-y-1/2 cursor-pointer" onClick={togglePasswordVisible}>
|
||||
{inputType == "password" ? <IconEyeClose className="fill-gray-500" /> : <IconEyeOpen className="fill-gray-500" />}
|
||||
</div>
|
||||
</div>
|
||||
) : prefix ? (
|
||||
<div className={`w-full relative flex overflow-hidden rounded border border-gray-300 dark:border-gray-400 shadow ${className}`}>
|
||||
<span className="px-4 py-2 text-sm text-gray-500 dark:text-gray-300 bg-gray-200 dark:bg-gray-800 border dark:border-gray-600">{prefix}</span>
|
||||
<div className={`w-full relative flex overflow-hidden rounded border border-solid border-gray-300 dark:border-gray-400 shadow-sm ${className}`}>
|
||||
<span className="px-4 py-2 text-sm text-gray-500 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 shadow-[rgb(0_0_0_/_10%)_-1px_0px_0px_inset]">{prefix}</span>
|
||||
<input className={`${inputClass} ${className}`} type={inputType} {...rest} />
|
||||
</div>
|
||||
) : (
|
||||
<input type={inputType} className={`${inputClass} rounded border border-gray-200 dark:border-gray-400 shadow ${className}`} {...rest} />
|
||||
<input type={inputType} className={`${inputClass} rounded border border-solid border-gray-200 dark:border-gray-400 shadow-sm ${className}`} {...rest} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ const Radio: FC<Props> = ({
|
||||
}}
|
||||
id={`${id}-${index}`}
|
||||
/>
|
||||
<div className="drop-shadow px-2 py-3 border border-solid border-gray-300 dark:border-gray-400 rounded-lg w-full h-full bg-white dark:bg-gray-800 peer-checked:bg-primary-400 text-sm text-gray-500 dark:text-gray-300 peer-checked:text-white">
|
||||
<div className="drop-shadow-sm px-2 py-3 border border-solid border-gray-300 dark:border-gray-400 rounded-lg w-full h-full bg-white dark:bg-gray-800 peer-checked:bg-primary-400 text-sm text-gray-500 dark:text-gray-300 peer-checked:text-white">
|
||||
<label className="ml-6" htmlFor={`${id}-${index}`}>{item}</label>
|
||||
</div>
|
||||
<div className="absolute top-1/2 left-3 -translate-y-1/2 w-3.5 h-3.5 rounded-full border border-solid border-gray-300 peer-checked:hidden"></div>
|
||||
|
||||
@@ -2,8 +2,12 @@ import { TextareaHTMLAttributes } from "react";
|
||||
|
||||
type Props = TextareaHTMLAttributes<HTMLTextAreaElement>
|
||||
const StyledTextarea = ({ className, ...rest }: Props) => {
|
||||
return <textarea className={`rounded text-sm p-2 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 resize-none w-full shadow border border-gray-200 dark:border-gray-400
|
||||
disabled:bg-gray-100 dark:disabled:bg-gray-800/50 disabled:text-gray-400 dark:disabled:text-gray-500 disabled:pointer-events-none placeholder:text-gray-400 ${className}`}
|
||||
return <textarea className={`rounded text-sm p-2 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 resize-none w-full shadow-sm border border-gray-200 dark:border-gray-400
|
||||
disabled:bg-gray-100 dark:disabled:bg-gray-800/50
|
||||
disabled:text-gray-400 dark:disabled:text-gray-500
|
||||
disabled:pointer-events-none
|
||||
placeholder:text-gray-400
|
||||
${className}`}
|
||||
{...rest}></textarea>;
|
||||
};
|
||||
export default StyledTextarea;
|
||||
|
||||
Reference in New Issue
Block a user