perf: reduce bundle size by 21% with direct icon imports (#3844)

This commit is contained in:
Michel Heusschen 2024-08-28 16:55:28 +02:00 committed by GitHub
parent d11bd30ec6
commit 2dbf92f7f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
63 changed files with 222 additions and 217 deletions

View File

@ -1,7 +1,7 @@
import { Option, Select } from "@mui/joy"; import { Option, Select } from "@mui/joy";
import { SunIcon, MoonIcon, SmileIcon } from "lucide-react";
import { FC } from "react"; import { FC } from "react";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "./Icon";
interface Props { interface Props {
value: Appearance; value: Appearance;
@ -18,11 +18,11 @@ const AppearanceSelect: FC<Props> = (props: Props) => {
const getPrefixIcon = (appearance: Appearance) => { const getPrefixIcon = (appearance: Appearance) => {
const className = "w-4 h-auto"; const className = "w-4 h-auto";
if (appearance === "light") { if (appearance === "light") {
return <Icon.Sun className={className} />; return <SunIcon className={className} />;
} else if (appearance === "dark") { } else if (appearance === "dark") {
return <Icon.Moon className={className} />; return <MoonIcon className={className} />;
} else { } else {
return <Icon.Smile className={className} />; return <SmileIcon className={className} />;
} }
}; };

View File

@ -1,11 +1,11 @@
import { Button, IconButton, Input } from "@mui/joy"; import { Button, IconButton, Input } from "@mui/joy";
import { XIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useUserStore } from "@/store/v1"; import { useUserStore } from "@/store/v1";
import { User } from "@/types/proto/api/v1/user_service"; import { User } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
interface Props extends DialogProps { interface Props extends DialogProps {
user: User; user: User;
@ -71,7 +71,7 @@ const ChangeMemberPasswordDialog: React.FC<Props> = (props: Props) => {
{t("setting.account-section.change-password")} ({user.nickname}) {t("setting.account-section.change-password")} ({user.nickname})
</p> </p>
<IconButton size="sm" onClick={handleCloseBtnClick}> <IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container"> <div className="dialog-content-container">

View File

@ -1,4 +1,5 @@
import { Button, IconButton, Input } from "@mui/joy"; import { Button, IconButton, Input } from "@mui/joy";
import { XIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
@ -6,7 +7,6 @@ import { useCommonContext } from "@/layouts/CommonContextProvider";
import { useUserStore } from "@/store/v1"; import { useUserStore } from "@/store/v1";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
type Props = DialogProps; type Props = DialogProps;
@ -70,7 +70,7 @@ const ChangePasswordDialog: React.FC<Props> = ({ destroy }: Props) => {
<div className="dialog-header-container !w-64"> <div className="dialog-header-container !w-64">
<p className="title-text">{t("setting.account-section.change-password")}</p> <p className="title-text">{t("setting.account-section.change-password")}</p>
<IconButton size="sm" onClick={handleCloseBtnClick}> <IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container"> <div className="dialog-content-container">

View File

@ -1,4 +1,5 @@
import { Button, IconButton, Input, Radio, RadioGroup } from "@mui/joy"; import { Button, IconButton, Input, Radio, RadioGroup } from "@mui/joy";
import { XIcon } from "lucide-react";
import React, { useState } from "react"; import React, { useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { userServiceClient } from "@/grpcweb"; import { userServiceClient } from "@/grpcweb";
@ -6,7 +7,6 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import useLoading from "@/hooks/useLoading"; import useLoading from "@/hooks/useLoading";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
interface Props extends DialogProps { interface Props extends DialogProps {
onConfirm: () => void; onConfirm: () => void;
@ -87,7 +87,7 @@ const CreateAccessTokenDialog: React.FC<Props> = (props: Props) => {
<div className="dialog-header-container"> <div className="dialog-header-container">
<p className="title-text">Create access token</p> <p className="title-text">Create access token</p>
<IconButton size="sm" onClick={() => destroy()}> <IconButton size="sm" onClick={() => destroy()}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container !w-80"> <div className="dialog-content-container !w-80">

View File

@ -1,4 +1,5 @@
import { Button, Divider, IconButton, Input, Option, Select, Typography } from "@mui/joy"; import { Button, Divider, IconButton, Input, Option, Select, Typography } from "@mui/joy";
import { XIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { identityProviderServiceClient } from "@/grpcweb"; import { identityProviderServiceClient } from "@/grpcweb";
@ -6,7 +7,6 @@ import { absolutifyLink } from "@/helpers/utils";
import { FieldMapping, IdentityProvider, IdentityProvider_Type, OAuth2Config } from "@/types/proto/api/v1/idp_service"; import { FieldMapping, IdentityProvider, IdentityProvider_Type, OAuth2Config } from "@/types/proto/api/v1/idp_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
const templateList: IdentityProvider[] = [ const templateList: IdentityProvider[] = [
{ {
@ -245,7 +245,7 @@ const CreateIdentityProviderDialog: React.FC<Props> = (props: Props) => {
<div className="dialog-header-container"> <div className="dialog-header-container">
<p>{t(isCreating ? "setting.sso-section.create-sso" : "setting.sso-section.update-sso")}</p> <p>{t(isCreating ? "setting.sso-section.create-sso" : "setting.sso-section.update-sso")}</p>
<IconButton size="sm" onClick={handleCloseBtnClick}> <IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container min-w-[19rem]"> <div className="dialog-content-container min-w-[19rem]">

View File

@ -1,11 +1,11 @@
import { Button, IconButton, Input } from "@mui/joy"; import { Button, IconButton, Input } from "@mui/joy";
import { XIcon } from "lucide-react";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { webhookServiceClient } from "@/grpcweb"; import { webhookServiceClient } from "@/grpcweb";
import useLoading from "@/hooks/useLoading"; import useLoading from "@/hooks/useLoading";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
interface Props extends DialogProps { interface Props extends DialogProps {
webhookId?: number; webhookId?: number;
@ -97,7 +97,7 @@ const CreateWebhookDialog: React.FC<Props> = (props: Props) => {
<div className="dialog-header-container"> <div className="dialog-header-container">
<p className="title-text">{isCreating ? "Create webhook" : "Edit webhook"}</p> <p className="title-text">{isCreating ? "Create webhook" : "Edit webhook"}</p>
<IconButton size="sm" onClick={() => destroy()}> <IconButton size="sm" onClick={() => destroy()}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container !w-80"> <div className="dialog-content-container !w-80">

View File

@ -1,9 +1,9 @@
import Icon from "./Icon"; import { BirdIcon } from "lucide-react";
const Empty = () => { const Empty = () => {
return ( return (
<div className="mx-auto"> <div className="mx-auto">
<Icon.Bird strokeWidth={1} className="w-24 h-auto text-gray-500 dark:text-gray-400" /> <BirdIcon strokeWidth={1} className="w-24 h-auto text-gray-500 dark:text-gray-400" />
</div> </div>
); );
}; };

View File

@ -1,7 +1,7 @@
import { Drawer, IconButton } from "@mui/joy"; import { Drawer, IconButton } from "@mui/joy";
import { SearchIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import Icon from "../Icon";
import ExploreSidebar from "./ExploreSidebar"; import ExploreSidebar from "./ExploreSidebar";
const ExploreSidebarDrawer = () => { const ExploreSidebarDrawer = () => {
@ -23,7 +23,7 @@ const ExploreSidebarDrawer = () => {
return ( return (
<> <>
<IconButton onClick={toggleDrawer(true)}> <IconButton onClick={toggleDrawer(true)}>
<Icon.Search className="w-5 h-auto dark:text-gray-400" /> <SearchIcon className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">

View File

@ -1,7 +1,7 @@
import { Drawer, IconButton } from "@mui/joy"; import { Drawer, IconButton } from "@mui/joy";
import { SearchIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import Icon from "../Icon";
import HomeSidebar from "./HomeSidebar"; import HomeSidebar from "./HomeSidebar";
const HomeSidebarDrawer = () => { const HomeSidebarDrawer = () => {
@ -23,7 +23,7 @@ const HomeSidebarDrawer = () => {
return ( return (
<> <>
<IconButton onClick={toggleDrawer(true)}> <IconButton onClick={toggleDrawer(true)}>
<Icon.Search className="w-5 h-auto dark:text-gray-400" /> <SearchIcon className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">

View File

@ -1,5 +1,6 @@
import { Dropdown, Menu, MenuButton, MenuItem, Switch } from "@mui/joy"; import { Dropdown, Menu, MenuButton, MenuItem, Switch } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { Edit3Icon, HashIcon, MoreVerticalIcon, TagsIcon, TrashIcon } from "lucide-react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import useDebounce from "react-use/lib/useDebounce"; import useDebounce from "react-use/lib/useDebounce";
@ -8,7 +9,6 @@ import { memoServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { useMemoFilterStore, useMemoList, useTagStore } from "@/store/v1"; import { useMemoFilterStore, useMemoList, useTagStore } from "@/store/v1";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
import showRenameTagDialog from "../RenameTagDialog"; import showRenameTagDialog from "../RenameTagDialog";
import TagTree from "../TagTree"; import TagTree from "../TagTree";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/Popover"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/Popover";
@ -66,7 +66,7 @@ const TagsSection = (props: Props) => {
{tagAmounts.length > 0 && ( {tagAmounts.length > 0 && (
<Popover> <Popover>
<PopoverTrigger> <PopoverTrigger>
<Icon.MoreVertical className="w-4 h-auto shrink-0 opacity-60" /> <MoreVerticalIcon className="w-4 h-auto shrink-0 opacity-60" />
</PopoverTrigger> </PopoverTrigger>
<PopoverContent align="end" alignOffset={-12}> <PopoverContent align="end" alignOffset={-12}>
<div className="w-auto flex flex-row justify-between items-center gap-2"> <div className="w-auto flex flex-row justify-between items-center gap-2">
@ -90,17 +90,17 @@ const TagsSection = (props: Props) => {
<Dropdown> <Dropdown>
<MenuButton slots={{ root: "div" }}> <MenuButton slots={{ root: "div" }}>
<div className="shrink-0 group"> <div className="shrink-0 group">
<Icon.Hash className="group-hover:hidden w-4 h-auto shrink-0 opacity-40" /> <HashIcon className="group-hover:hidden w-4 h-auto shrink-0 opacity-40" />
<Icon.MoreVertical className="hidden group-hover:block w-4 h-auto shrink-0 opacity-60" /> <MoreVerticalIcon className="hidden group-hover:block w-4 h-auto shrink-0 opacity-60" />
</div> </div>
</MenuButton> </MenuButton>
<Menu size="sm" placement="bottom-start"> <Menu size="sm" placement="bottom-start">
<MenuItem onClick={() => showRenameTagDialog({ tag: tag })}> <MenuItem onClick={() => showRenameTagDialog({ tag: tag })}>
<Icon.Edit3 className="w-4 h-auto" /> <Edit3Icon className="w-4 h-auto" />
{t("common.rename")} {t("common.rename")}
</MenuItem> </MenuItem>
<MenuItem color="danger" onClick={() => handleDeleteTag(tag)}> <MenuItem color="danger" onClick={() => handleDeleteTag(tag)}>
<Icon.Trash className="w-4 h-auto" /> <TrashIcon className="w-4 h-auto" />
{t("common.delete")} {t("common.delete")}
</MenuItem> </MenuItem>
</Menu> </Menu>
@ -119,7 +119,7 @@ const TagsSection = (props: Props) => {
) : ( ) : (
!props.readonly && ( !props.readonly && (
<div className="p-2 border border-dashed dark:border-zinc-800 rounded-md flex flex-row justify-start items-start gap-1 text-gray-400 dark:text-gray-500"> <div className="p-2 border border-dashed dark:border-zinc-800 rounded-md flex flex-row justify-start items-start gap-1 text-gray-400 dark:text-gray-500">
<Icon.Tags /> <TagsIcon />
<p className="mt-0.5 text-sm leading-snug italic">{t("tag.create-tags-guide")}</p> <p className="mt-0.5 text-sm leading-snug italic">{t("tag.create-tags-guide")}</p>
</div> </div>
) )

View File

@ -1,3 +0,0 @@
import * as Icon from "lucide-react";
export default Icon;

View File

@ -1,5 +1,6 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { InboxIcon, MessageCircleIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { activityServiceClient } from "@/grpcweb"; import { activityServiceClient } from "@/grpcweb";
@ -9,7 +10,6 @@ import { Inbox, Inbox_Status } from "@/types/proto/api/v1/inbox_service";
import { Memo } from "@/types/proto/api/v1/memo_service"; import { Memo } from "@/types/proto/api/v1/memo_service";
import { User } from "@/types/proto/api/v1/user_service"; import { User } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
interface Props { interface Props {
inbox: Inbox; inbox: Inbox;
@ -81,7 +81,7 @@ const MemoCommentMessage = ({ inbox }: Props) => {
)} )}
> >
<Tooltip title={"Comment"} placement="bottom"> <Tooltip title={"Comment"} placement="bottom">
<Icon.MessageCircle className="w-4 sm:w-5 h-auto" /> <MessageCircleIcon className="w-4 sm:w-5 h-auto" />
</Tooltip> </Tooltip>
</div> </div>
<div <div
@ -95,10 +95,7 @@ const MemoCommentMessage = ({ inbox }: Props) => {
<div> <div>
{inbox.status === Inbox_Status.UNREAD && ( {inbox.status === Inbox_Status.UNREAD && (
<Tooltip title={t("common.archive")} placement="top"> <Tooltip title={t("common.archive")} placement="top">
<Icon.Inbox <InboxIcon className="w-4 h-auto cursor-pointer text-gray-400 hover:text-blue-600" onClick={() => handleArchiveMessage()} />
className="w-4 h-auto cursor-pointer text-gray-400 hover:text-blue-600"
onClick={() => handleArchiveMessage()}
/>
</Tooltip> </Tooltip>
)} )}
</div> </div>

View File

@ -1,5 +1,6 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { ArrowUpIcon, InboxIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { activityServiceClient } from "@/grpcweb"; import { activityServiceClient } from "@/grpcweb";
@ -7,7 +8,6 @@ import { activityNamePrefix, useInboxStore } from "@/store/v1";
import { Activity } from "@/types/proto/api/v1/activity_service"; import { Activity } from "@/types/proto/api/v1/activity_service";
import { Inbox, Inbox_Status } from "@/types/proto/api/v1/inbox_service"; import { Inbox, Inbox_Status } from "@/types/proto/api/v1/inbox_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
interface Props { interface Props {
inbox: Inbox; inbox: Inbox;
@ -66,7 +66,7 @@ const VersionUpdateMessage = ({ inbox }: Props) => {
)} )}
> >
<Tooltip title={"Update"} placement="bottom"> <Tooltip title={"Update"} placement="bottom">
<Icon.ArrowUp className="w-4 sm:w-5 h-auto" /> <ArrowUpIcon className="w-4 sm:w-5 h-auto" />
</Tooltip> </Tooltip>
</div> </div>
<div <div
@ -80,10 +80,7 @@ const VersionUpdateMessage = ({ inbox }: Props) => {
<div> <div>
{inbox.status === Inbox_Status.UNREAD && ( {inbox.status === Inbox_Status.UNREAD && (
<Tooltip title={t("common.archive")} placement="top"> <Tooltip title={t("common.archive")} placement="top">
<Icon.Inbox <InboxIcon className="w-4 h-auto cursor-pointer text-gray-400 hover:text-blue-600" onClick={() => handleArchiveMessage()} />
className="w-4 h-auto cursor-pointer text-gray-400 hover:text-blue-600"
onClick={() => handleArchiveMessage()}
/>
</Tooltip> </Tooltip>
)} )}
</div> </div>

View File

@ -1,6 +1,6 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import { ExternalLinkIcon } from "lucide-react";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "./Icon";
interface Props { interface Props {
className?: string; className?: string;
@ -15,7 +15,7 @@ const LearnMore: React.FC<Props> = (props: Props) => {
return ( return (
<Tooltip title={title ?? t("common.learn-more")} placement="top"> <Tooltip title={title ?? t("common.learn-more")} placement="top">
<a className={`text-gray-500 dark:text-gray-400 hover:text-blue-600 ${className}`} href={url} target="_blank"> <a className={`text-gray-500 dark:text-gray-400 hover:text-blue-600 ${className}`} href={url} target="_blank">
<Icon.ExternalLink className="w-4 h-auto" /> <ExternalLinkIcon className="w-4 h-auto" />
</a> </a>
</Tooltip> </Tooltip>
); );

View File

@ -1,7 +1,7 @@
import { Option, Select } from "@mui/joy"; import { Option, Select } from "@mui/joy";
import { GlobeIcon } from "lucide-react";
import { FC } from "react"; import { FC } from "react";
import { locales } from "@/i18n"; import { locales } from "@/i18n";
import Icon from "./Icon";
interface Props { interface Props {
value: Locale; value: Locale;
@ -19,7 +19,7 @@ const LocaleSelect: FC<Props> = (props: Props) => {
return ( return (
<Select <Select
className={`!min-w-[10rem] w-auto whitespace-nowrap ${className ?? ""}`} className={`!min-w-[10rem] w-auto whitespace-nowrap ${className ?? ""}`}
startDecorator={<Icon.Globe className="w-4 h-auto" />} startDecorator={<GlobeIcon className="w-4 h-auto" />}
value={value} value={value}
onChange={(_, value) => handleSelectChange(value as Locale)} onChange={(_, value) => handleSelectChange(value as Locale)}
> >

View File

@ -1,9 +1,18 @@
import { Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy"; import { Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import {
ArchiveIcon,
ArchiveRestoreIcon,
BookmarkMinusIcon,
BookmarkPlusIcon,
CopyIcon,
Edit3Icon,
MoreVerticalIcon,
TrashIcon,
} from "lucide-react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import Icon from "@/components/Icon";
import useNavigateTo from "@/hooks/useNavigateTo"; import useNavigateTo from "@/hooks/useNavigateTo";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
import { RowStatus } from "@/types/proto/api/v1/common"; import { RowStatus } from "@/types/proto/api/v1/common";
@ -108,34 +117,34 @@ const MemoActionMenu = (props: Props) => {
<Dropdown> <Dropdown>
<MenuButton slots={{ root: "div" }}> <MenuButton slots={{ root: "div" }}>
<span className={clsx("flex justify-center items-center rounded-full hover:opacity-70", props.className)}> <span className={clsx("flex justify-center items-center rounded-full hover:opacity-70", props.className)}>
<Icon.MoreVertical className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" /> <MoreVerticalIcon className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" />
</span> </span>
</MenuButton> </MenuButton>
<Menu className="text-sm" size="sm" placement="bottom-end"> <Menu className="text-sm" size="sm" placement="bottom-end">
{!hiddenActions?.includes("pin") && ( {!hiddenActions?.includes("pin") && (
<MenuItem onClick={handleTogglePinMemoBtnClick}> <MenuItem onClick={handleTogglePinMemoBtnClick}>
{memo.pinned ? <Icon.BookmarkMinus className="w-4 h-auto" /> : <Icon.BookmarkPlus className="w-4 h-auto" />} {memo.pinned ? <BookmarkMinusIcon className="w-4 h-auto" /> : <BookmarkPlusIcon className="w-4 h-auto" />}
{memo.pinned ? t("common.unpin") : t("common.pin")} {memo.pinned ? t("common.unpin") : t("common.pin")}
</MenuItem> </MenuItem>
)} )}
{!hiddenActions?.includes("edit") && props.onEdit && ( {!hiddenActions?.includes("edit") && props.onEdit && (
<MenuItem onClick={handleEditMemoClick}> <MenuItem onClick={handleEditMemoClick}>
<Icon.Edit3 className="w-4 h-auto" /> <Edit3Icon className="w-4 h-auto" />
{t("common.edit")} {t("common.edit")}
</MenuItem> </MenuItem>
)} )}
{!hiddenActions?.includes("share") && ( {!hiddenActions?.includes("share") && (
<MenuItem onClick={handleCopyLink}> <MenuItem onClick={handleCopyLink}>
<Icon.Copy className="w-4 h-auto" /> <CopyIcon className="w-4 h-auto" />
{t("memo.copy-link")} {t("memo.copy-link")}
</MenuItem> </MenuItem>
)} )}
<MenuItem color="warning" onClick={handleToggleMemoStatusClick}> <MenuItem color="warning" onClick={handleToggleMemoStatusClick}>
{memo.rowStatus === RowStatus.ARCHIVED ? <Icon.ArchiveRestore className="w-4 h-auto" /> : <Icon.Archive className="w-4 h-auto" />} {memo.rowStatus === RowStatus.ARCHIVED ? <ArchiveRestoreIcon className="w-4 h-auto" /> : <ArchiveIcon className="w-4 h-auto" />}
{memo.rowStatus === RowStatus.ARCHIVED ? t("common.restore") : t("common.archive")} {memo.rowStatus === RowStatus.ARCHIVED ? t("common.restore") : t("common.archive")}
</MenuItem> </MenuItem>
<MenuItem color="danger" onClick={handleDeleteMemoClick}> <MenuItem color="danger" onClick={handleDeleteMemoClick}>
<Icon.Trash className="w-4 h-auto" /> <TrashIcon className="w-4 h-auto" />
{t("common.delete")} {t("common.delete")}
</MenuItem> </MenuItem>
</Menu> </Menu>

View File

@ -2,9 +2,9 @@ import clsx from "clsx";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import hljs from "highlight.js"; import hljs from "highlight.js";
import { CopyIcon } from "lucide-react";
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import Icon from "../Icon";
import MermaidBlock from "./MermaidBlock"; import MermaidBlock from "./MermaidBlock";
import { BaseProps } from "./types"; import { BaseProps } from "./types";
@ -57,7 +57,7 @@ const CodeBlock: React.FC<Props> = ({ language, content }: Props) => {
<div className="w-full my-1 bg-amber-100 border-l-4 border-amber-400 rounded hover:shadow dark:bg-zinc-600 dark:border-zinc-400 relative"> <div className="w-full my-1 bg-amber-100 border-l-4 border-amber-400 rounded hover:shadow dark:bg-zinc-600 dark:border-zinc-400 relative">
<div className="w-full px-2 py-1 flex flex-row justify-between items-center text-amber-500 dark:text-zinc-400"> <div className="w-full px-2 py-1 flex flex-row justify-between items-center text-amber-500 dark:text-zinc-400">
<span className="text-sm font-mono">{formatedLanguage}</span> <span className="text-sm font-mono">{formatedLanguage}</span>
<Icon.Copy className="w-4 h-auto cursor-pointer hover:opacity-80" onClick={handleCopyButtonClick} /> <CopyIcon className="w-4 h-auto cursor-pointer hover:opacity-80" onClick={handleCopyButtonClick} />
</div> </div>
<div className="overflow-auto"> <div className="overflow-auto">

View File

@ -1,9 +1,9 @@
import clsx from "clsx"; import clsx from "clsx";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { ArrowUpRightIcon } from "lucide-react";
import { useContext, useEffect } from "react"; import { useContext, useEffect } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Icon from "@/components/Icon";
import MemoResourceListView from "@/components/MemoResourceListView"; import MemoResourceListView from "@/components/MemoResourceListView";
import useLoading from "@/hooks/useLoading"; import useLoading from "@/hooks/useLoading";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
@ -76,7 +76,7 @@ const EmbeddedMemo = ({ resourceId: uid, params: paramsStr }: Props) => {
{memo.uid.slice(0, 8)} {memo.uid.slice(0, 8)}
</span> </span>
<Link className="opacity-60 hover:opacity-80" to={`/m/${memo.uid}`} unstable_viewTransition> <Link className="opacity-60 hover:opacity-80" to={`/m/${memo.uid}`} unstable_viewTransition>
<Icon.ArrowUpRight className="w-5 h-auto" /> <ArrowUpRightIcon className="w-5 h-auto" />
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -1,8 +1,8 @@
import clsx from "clsx"; import clsx from "clsx";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { CheckCircleIcon, Code2Icon, HashIcon, LinkIcon } from "lucide-react";
import { Memo, MemoProperty } from "@/types/proto/api/v1/memo_service"; import { Memo, MemoProperty } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
interface Props { interface Props {
memo: Memo; memo: Memo;
@ -45,7 +45,7 @@ const MemoDetailSidebar = ({ memo, className }: Props) => {
{property.hasLink && ( {property.hasLink && (
<div className="w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center"> <div className="w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center">
<div className="w-auto flex justify-start items-center mr-1"> <div className="w-auto flex justify-start items-center mr-1">
<Icon.Link className="w-4 h-auto mr-1" /> <LinkIcon className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.links")}</span> <span className="block text-sm">{t("memo.links")}</span>
</div> </div>
</div> </div>
@ -53,7 +53,7 @@ const MemoDetailSidebar = ({ memo, className }: Props) => {
{property.hasTaskList && ( {property.hasTaskList && (
<div className="w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center"> <div className="w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center">
<div className="w-auto flex justify-start items-center mr-1"> <div className="w-auto flex justify-start items-center mr-1">
<Icon.CheckCircle className="w-4 h-auto mr-1" /> <CheckCircleIcon className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.to-do")}</span> <span className="block text-sm">{t("memo.to-do")}</span>
</div> </div>
</div> </div>
@ -61,7 +61,7 @@ const MemoDetailSidebar = ({ memo, className }: Props) => {
{property.hasCode && ( {property.hasCode && (
<div className="w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center"> <div className="w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center">
<div className="w-auto flex justify-start items-center mr-1"> <div className="w-auto flex justify-start items-center mr-1">
<Icon.Code2 className="w-4 h-auto mr-1" /> <Code2Icon className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.code")}</span> <span className="block text-sm">{t("memo.code")}</span>
</div> </div>
</div> </div>
@ -81,7 +81,7 @@ const MemoDetailSidebar = ({ memo, className }: Props) => {
key={tag} key={tag}
className="shrink-0 w-auto max-w-full text-sm rounded-md leading-6 flex flex-row justify-start items-center select-none hover:opacity-80 text-gray-600 dark:text-gray-400 dark:border-zinc-800" className="shrink-0 w-auto max-w-full text-sm rounded-md leading-6 flex flex-row justify-start items-center select-none hover:opacity-80 text-gray-600 dark:text-gray-400 dark:border-zinc-800"
> >
<Icon.Hash className="group-hover:hidden w-4 h-auto shrink-0 opacity-40" /> <HashIcon className="group-hover:hidden w-4 h-auto shrink-0 opacity-40" />
<div className={clsx("inline-flex flex-nowrap ml-0.5 gap-0.5 cursor-pointer max-w-[calc(100%-16px)]")}> <div className={clsx("inline-flex flex-nowrap ml-0.5 gap-0.5 cursor-pointer max-w-[calc(100%-16px)]")}>
<span className="truncate dark:opacity-80">{tag}</span> <span className="truncate dark:opacity-80">{tag}</span>
</div> </div>

View File

@ -1,8 +1,8 @@
import { Drawer, IconButton } from "@mui/joy"; import { Drawer, IconButton } from "@mui/joy";
import { GanttChartIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { Memo } from "@/types/proto/api/v1/memo_service"; import { Memo } from "@/types/proto/api/v1/memo_service";
import Icon from "../Icon";
import MemoDetailSidebar from "./MemoDetailSidebar"; import MemoDetailSidebar from "./MemoDetailSidebar";
interface Props { interface Props {
@ -27,7 +27,7 @@ const MemoDetailSidebarDrawer = ({ memo }: Props) => {
return ( return (
<> <>
<IconButton onClick={toggleDrawer(true)}> <IconButton onClick={toggleDrawer(true)}>
<Icon.GanttChart className="w-5 h-auto dark:text-gray-400" /> <GanttChartIcon className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="right" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full px-4 bg-zinc-100 dark:bg-zinc-900">

View File

@ -1,7 +1,7 @@
import { Option, Select } from "@mui/joy"; import { Option, Select } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { Settings2Icon } from "lucide-react";
import { useMemoFilterStore } from "@/store/v1"; import { useMemoFilterStore } from "@/store/v1";
import Icon from "./Icon";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover";
interface Props { interface Props {
@ -17,7 +17,7 @@ const MemoDisplaySettingMenu = ({ className }: Props) => {
<PopoverTrigger <PopoverTrigger
className={clsx(className, isApplying ? "text-teal-600 bg-teal-50 dark:text-teal-500 dark:bg-teal-900 rounded-sm" : "opacity-40")} className={clsx(className, isApplying ? "text-teal-600 bg-teal-50 dark:text-teal-500 dark:bg-teal-900 rounded-sm" : "opacity-40")}
> >
<Icon.Settings2 className="w-4 h-auto shrink-0" /> <Settings2Icon className="w-4 h-auto shrink-0" />
</PopoverTrigger> </PopoverTrigger>
<PopoverContent align="end" alignOffset={-12} sideOffset={14}> <PopoverContent align="end" alignOffset={-12} sideOffset={14}>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">

View File

@ -1,9 +1,9 @@
import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy"; import { Autocomplete, AutocompleteOption, Button, Checkbox, Chip, IconButton } from "@mui/joy";
import { uniqBy } from "lodash-es"; import { uniqBy } from "lodash-es";
import { LinkIcon } from "lucide-react";
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import useDebounce from "react-use/lib/useDebounce"; import useDebounce from "react-use/lib/useDebounce";
import Icon from "@/components/Icon";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/Popover"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/Popover";
import { memoServiceClient } from "@/grpcweb"; import { memoServiceClient } from "@/grpcweb";
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts"; import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
@ -127,7 +127,7 @@ const AddMemoRelationPopover = (props: Props) => {
<Popover open={popoverOpen} onOpenChange={setPopoverOpen}> <Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
<PopoverTrigger> <PopoverTrigger>
<IconButton size="sm" component="div"> <IconButton size="sm" component="div">
<Icon.Link className="w-5 h-5 mx-auto" /> <LinkIcon className="w-5 h-5 mx-auto" />
</IconButton> </IconButton>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent align="center"> <PopoverContent align="center">

View File

@ -1,6 +1,6 @@
import { Dropdown, IconButton, Menu, MenuButton, MenuItem } from "@mui/joy"; import { Dropdown, IconButton, Menu, MenuButton, MenuItem } from "@mui/joy";
import { Link } from "@mui/joy"; import { Link } from "@mui/joy";
import Icon from "@/components/Icon"; import { CheckSquareIcon, Code2Icon, SquareSlashIcon } from "lucide-react";
import { EditorRefActions } from "../Editor"; import { EditorRefActions } from "../Editor";
interface Props { interface Props {
@ -67,15 +67,15 @@ const MarkdownMenu = (props: Props) => {
}, },
}} }}
> >
<Icon.SquareSlash className="w-5 h-5 mx-auto" /> <SquareSlashIcon className="w-5 h-5 mx-auto" />
</MenuButton> </MenuButton>
<Menu className="text-sm" size="sm" placement="bottom-start"> <Menu className="text-sm" size="sm" placement="bottom-start">
<MenuItem onClick={handleCodeBlockClick}> <MenuItem onClick={handleCodeBlockClick}>
<Icon.Code2 className="w-4 h-auto" /> <Code2Icon className="w-4 h-auto" />
<span>Code block</span> <span>Code block</span>
</MenuItem> </MenuItem>
<MenuItem onClick={handleCheckboxClick}> <MenuItem onClick={handleCheckboxClick}>
<Icon.CheckSquare className="w-4 h-auto" /> <CheckSquareIcon className="w-4 h-auto" />
<span>Checkbox</span> <span>Checkbox</span>
</MenuItem> </MenuItem>
<div className="-mt-0.5 pl-2"> <div className="-mt-0.5 pl-2">

View File

@ -1,7 +1,7 @@
import { Dropdown, IconButton, Menu, MenuButton } from "@mui/joy"; import { Dropdown, IconButton, Menu, MenuButton } from "@mui/joy";
import { HashIcon } from "lucide-react";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import useClickAway from "react-use/lib/useClickAway"; import useClickAway from "react-use/lib/useClickAway";
import Icon from "@/components/Icon";
import OverflowTip from "@/components/kit/OverflowTip"; import OverflowTip from "@/components/kit/OverflowTip";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { useTagStore } from "@/store/v1"; import { useTagStore } from "@/store/v1";
@ -58,7 +58,7 @@ const TagSelector = (props: Props) => {
}, },
}} }}
> >
<Icon.Hash className="w-5 h-5 mx-auto" /> <HashIcon className="w-5 h-5 mx-auto" />
</MenuButton> </MenuButton>
<Menu className="relative text-sm" component="div" size="sm" placement="bottom-start"> <Menu className="relative text-sm" component="div" size="sm" placement="bottom-start">
<div ref={containerRef}> <div ref={containerRef}>

View File

@ -1,7 +1,7 @@
import { IconButton } from "@mui/joy"; import { IconButton } from "@mui/joy";
import { PaperclipIcon } from "lucide-react";
import { useContext, useRef, useState } from "react"; import { useContext, useRef, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import Icon from "@/components/Icon";
import { useResourceStore } from "@/store/v1"; import { useResourceStore } from "@/store/v1";
import { Resource } from "@/types/proto/api/v1/resource_service"; import { Resource } from "@/types/proto/api/v1/resource_service";
import { MemoEditorContext } from "../types"; import { MemoEditorContext } from "../types";
@ -67,7 +67,7 @@ const UploadResourceButton = () => {
return ( return (
<IconButton size="sm" disabled={state.uploadingFlag}> <IconButton size="sm" disabled={state.uploadingFlag}>
<Icon.Paperclip className="w-5 h-5 mx-auto" /> <PaperclipIcon className="w-5 h-5 mx-auto" />
<input <input
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer" className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
ref={fileInputRef} ref={fileInputRef}

View File

@ -1,8 +1,8 @@
import { LinkIcon, XIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service"; import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service";
import { Memo } from "@/types/proto/api/v1/memo_service"; import { Memo } from "@/types/proto/api/v1/memo_service";
import Icon from "../Icon";
interface Props { interface Props {
relationList: MemoRelation[]; relationList: MemoRelation[];
@ -41,9 +41,9 @@ const RelationListView = (props: Props) => {
className="w-auto max-w-xs overflow-hidden flex flex-row justify-start items-center bg-zinc-100 dark:bg-zinc-900 hover:opacity-80 rounded-md text-sm p-1 px-2 text-gray-500 dark:text-gray-400 cursor-pointer hover:line-through" className="w-auto max-w-xs overflow-hidden flex flex-row justify-start items-center bg-zinc-100 dark:bg-zinc-900 hover:opacity-80 rounded-md text-sm p-1 px-2 text-gray-500 dark:text-gray-400 cursor-pointer hover:line-through"
onClick={() => handleDeleteRelation(memo)} onClick={() => handleDeleteRelation(memo)}
> >
<Icon.Link className="w-4 h-auto shrink-0 opacity-80" /> <LinkIcon className="w-4 h-auto shrink-0 opacity-80" />
<span className="mx-1 max-w-full text-ellipsis whitespace-nowrap overflow-hidden">{memo.snippet}</span> <span className="mx-1 max-w-full text-ellipsis whitespace-nowrap overflow-hidden">{memo.snippet}</span>
<Icon.X className="w-4 h-auto cursor-pointer shrink-0 opacity-60 hover:opacity-100" /> <XIcon className="w-4 h-auto cursor-pointer shrink-0 opacity-60 hover:opacity-100" />
</div> </div>
); );
})} })}

View File

@ -1,7 +1,7 @@
import { DndContext, closestCenter, MouseSensor, TouchSensor, useSensor, useSensors, DragEndEvent } from "@dnd-kit/core"; import { DndContext, closestCenter, MouseSensor, TouchSensor, useSensor, useSensors, DragEndEvent } from "@dnd-kit/core";
import { arrayMove, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"; import { arrayMove, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { XIcon } from "lucide-react";
import { Resource } from "@/types/proto/api/v1/resource_service"; import { Resource } from "@/types/proto/api/v1/resource_service";
import Icon from "../Icon";
import ResourceIcon from "../ResourceIcon"; import ResourceIcon from "../ResourceIcon";
import SortableItem from "./SortableItem"; import SortableItem from "./SortableItem";
@ -45,7 +45,7 @@ const ResourceListView = (props: Props) => {
<span className="text-sm max-w-[8rem] truncate">{resource.filename}</span> <span className="text-sm max-w-[8rem] truncate">{resource.filename}</span>
</SortableItem> </SortableItem>
<button className="shrink-0" onClick={() => handleDeleteResource(resource.name)}> <button className="shrink-0" onClick={() => handleDeleteResource(resource.name)}>
<Icon.X className="w-4 h-auto cursor-pointer opacity-60 hover:opacity-100" /> <XIcon className="w-4 h-auto cursor-pointer opacity-60 hover:opacity-100" />
</button> </button>
</div> </div>
); );

View File

@ -1,5 +1,6 @@
import { Select, Option, Button, Divider } from "@mui/joy"; import { Select, Option, Button, Divider } from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { SendIcon } from "lucide-react";
import React, { useEffect, useMemo, useRef, useState } from "react"; import React, { useEffect, useMemo, useRef, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -18,7 +19,6 @@ import { WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_sett
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { convertVisibilityFromString, convertVisibilityToString } from "@/utils/memo"; import { convertVisibilityFromString, convertVisibilityToString } from "@/utils/memo";
import Icon from "../Icon";
import VisibilityIcon from "../VisibilityIcon"; import VisibilityIcon from "../VisibilityIcon";
import AddMemoRelationPopover from "./ActionButton/AddMemoRelationPopover"; import AddMemoRelationPopover from "./ActionButton/AddMemoRelationPopover";
import MarkdownMenu from "./ActionButton/MarkdownMenu"; import MarkdownMenu from "./ActionButton/MarkdownMenu";
@ -473,7 +473,7 @@ const MemoEditor = (props: Props) => {
className="!font-normal" className="!font-normal"
disabled={!allowSave} disabled={!allowSave}
loading={state.isRequesting} loading={state.isRequesting}
endDecorator={<Icon.Send className="w-4 h-auto" />} endDecorator={<SendIcon className="w-4 h-auto" />}
onClick={handleSaveBtnClick} onClick={handleSaveBtnClick}
> >
{t("editor.save")} {t("editor.save")}

View File

@ -1,6 +1,6 @@
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, FilterIcon, LinkIcon, SearchIcon, TagIcon, XIcon } from "lucide-react";
import { FilterFactor, getMemoFilterKey, MemoFilter, useMemoFilterStore } from "@/store/v1"; import { FilterFactor, getMemoFilterKey, MemoFilter, useMemoFilterStore } from "@/store/v1";
import Icon from "./Icon";
const MemoFilters = () => { const MemoFilters = () => {
const memoFilterStore = useMemoFilterStore(); const memoFilterStore = useMemoFilterStore();
@ -23,7 +23,7 @@ const MemoFilters = () => {
return ( return (
<div className="w-full mb-2 flex flex-row justify-start items-start gap-2"> <div className="w-full mb-2 flex flex-row justify-start items-start gap-2">
<span className="flex flex-row items-center gap-0.5 text-gray-500 text-sm leading-6 border border-transparent"> <span className="flex flex-row items-center gap-0.5 text-gray-500 text-sm leading-6 border border-transparent">
<Icon.Filter className="w-4 h-auto opacity-60 inline" /> <FilterIcon className="w-4 h-auto opacity-60 inline" />
Filters Filters
</span> </span>
<div className="flex flex-row justify-start items-center flex-wrap gap-2 leading-6 h-6"> <div className="flex flex-row justify-start items-center flex-wrap gap-2 leading-6 h-6">
@ -36,7 +36,7 @@ const MemoFilters = () => {
<FactorIcon className="w-4 h-auto text-gray-500 dark:text-gray-400 opacity-60" factor={filter.factor} /> <FactorIcon className="w-4 h-auto text-gray-500 dark:text-gray-400 opacity-60" factor={filter.factor} />
<span className="text-gray-500 dark:text-gray-400 text-sm max-w-32 truncate">{getFilterDisplayText(filter)}</span> <span className="text-gray-500 dark:text-gray-400 text-sm max-w-32 truncate">{getFilterDisplayText(filter)}</span>
<button className="text-gray-500 dark:text-gray-300 opacity-60 hover:opacity-100"> <button className="text-gray-500 dark:text-gray-300 opacity-60 hover:opacity-100">
<Icon.X className="w-4 h-auto" /> <XIcon className="w-4 h-auto" />
</button> </button>
</div> </div>
))} ))}
@ -47,13 +47,13 @@ const MemoFilters = () => {
const FactorIcon = ({ factor, className }: { factor: FilterFactor; className?: string }) => { const FactorIcon = ({ factor, className }: { factor: FilterFactor; className?: string }) => {
const iconMap = { const iconMap = {
tagSearch: <Icon.Tag className={className} />, tagSearch: <TagIcon className={className} />,
visibility: <Icon.Eye className={className} />, visibility: <EyeIcon className={className} />,
contentSearch: <Icon.Search className={className} />, contentSearch: <SearchIcon className={className} />,
displayTime: <Icon.Calendar className={className} />, displayTime: <CalendarIcon className={className} />,
"property.hasLink": <Icon.Link className={className} />, "property.hasLink": <LinkIcon className={className} />,
"property.hasTaskList": <Icon.CheckCircle className={className} />, "property.hasTaskList": <CheckCircleIcon className={className} />,
"property.hasCode": <Icon.Code className={className} />, "property.hasCode": <CodeIcon className={className} />,
}; };
return iconMap[factor as keyof typeof iconMap] || <></>; return iconMap[factor as keyof typeof iconMap] || <></>;
}; };

View File

@ -1,11 +1,11 @@
import clsx from "clsx"; import clsx from "clsx";
import { DotIcon, LinkIcon, MilestoneIcon } from "lucide-react";
import { memo, useState } from "react"; import { memo, useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import useAsyncEffect from "@/hooks/useAsyncEffect"; import useAsyncEffect from "@/hooks/useAsyncEffect";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
import { MemoRelation } from "@/types/proto/api/v1/memo_relation_service"; import { MemoRelation } from "@/types/proto/api/v1/memo_relation_service";
import { Memo } from "@/types/proto/api/v1/memo_service"; import { Memo } from "@/types/proto/api/v1/memo_service";
import Icon from "./Icon";
interface Props { interface Props {
memo: Memo; memo: Memo;
@ -54,7 +54,7 @@ const MemoRelationListView = (props: Props) => {
)} )}
onClick={() => setSelectedTab("referencing")} onClick={() => setSelectedTab("referencing")}
> >
<Icon.Link className="w-3 h-auto shrink-0 opacity-70" /> <LinkIcon className="w-3 h-auto shrink-0 opacity-70" />
<span>Referencing</span> <span>Referencing</span>
<span className="opacity-80">({referencingMemoList.length})</span> <span className="opacity-80">({referencingMemoList.length})</span>
</button> </button>
@ -67,7 +67,7 @@ const MemoRelationListView = (props: Props) => {
)} )}
onClick={() => setSelectedTab("referenced")} onClick={() => setSelectedTab("referenced")}
> >
<Icon.Milestone className="w-3 h-auto shrink-0 opacity-70" /> <MilestoneIcon className="w-3 h-auto shrink-0 opacity-70" />
<span>Referenced by</span> <span>Referenced by</span>
<span className="opacity-80">({referencedMemoList.length})</span> <span className="opacity-80">({referencedMemoList.length})</span>
</button> </button>
@ -83,7 +83,7 @@ const MemoRelationListView = (props: Props) => {
to={`/m/${memo.uid}`} to={`/m/${memo.uid}`}
unstable_viewTransition unstable_viewTransition
> >
<Icon.Dot className="shrink-0 w-4 h-auto opacity-40" /> <DotIcon className="shrink-0 w-4 h-auto opacity-40" />
<span className="truncate">{memo.snippet}</span> <span className="truncate">{memo.snippet}</span>
</Link> </Link>
); );
@ -100,7 +100,7 @@ const MemoRelationListView = (props: Props) => {
to={`/m/${memo.uid}`} to={`/m/${memo.uid}`}
unstable_viewTransition unstable_viewTransition
> >
<Icon.Dot className="shrink-0 w-4 h-auto opacity-40" /> <DotIcon className="shrink-0 w-4 h-auto opacity-40" />
<span className="truncate">{memo.snippet}</span> <span className="truncate">{memo.snippet}</span>
</Link> </Link>
); );

View File

@ -1,5 +1,6 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { BookmarkIcon, MessageCircleMoreIcon } from "lucide-react";
import { memo, useCallback, useEffect, useRef, useState } from "react"; import { memo, useCallback, useEffect, useRef, useState } from "react";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
@ -11,7 +12,6 @@ import { WorkspaceMemoRelatedSetting } from "@/types/proto/api/v1/workspace_sett
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { convertVisibilityToString } from "@/utils/memo"; import { convertVisibilityToString } from "@/utils/memo";
import Icon from "./Icon";
import MemoActionMenu from "./MemoActionMenu"; import MemoActionMenu from "./MemoActionMenu";
import MemoContent from "./MemoContent"; import MemoContent from "./MemoContent";
import MemoEditor from "./MemoEditor"; import MemoEditor from "./MemoEditor";
@ -169,13 +169,13 @@ const MemoView: React.FC<Props> = (props: Props) => {
to={`/m/${memo.uid}#comments`} to={`/m/${memo.uid}#comments`}
unstable_viewTransition unstable_viewTransition
> >
<Icon.MessageCircleMore className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" /> <MessageCircleMoreIcon className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" />
{commentAmount > 0 && <span className="text-xs text-gray-500 dark:text-gray-400">{commentAmount}</span>} {commentAmount > 0 && <span className="text-xs text-gray-500 dark:text-gray-400">{commentAmount}</span>}
</Link> </Link>
)} )}
{props.showPinned && memo.pinned && ( {props.showPinned && memo.pinned && (
<Tooltip title={t("common.pinned")} placement="top"> <Tooltip title={t("common.pinned")} placement="top">
<Icon.Bookmark className="w-4 h-auto text-amber-500" /> <BookmarkIcon className="w-4 h-auto text-amber-500" />
</Tooltip> </Tooltip>
)} )}
{!readonly && ( {!readonly && (

View File

@ -1,5 +1,6 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { ArchiveIcon, BellIcon, Globe2Icon, HomeIcon, LogInIcon, PaperclipIcon, SettingsIcon, SmileIcon, User2Icon } from "lucide-react";
import { useEffect } from "react"; import { useEffect } from "react";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
@ -7,7 +8,6 @@ import { Routes } from "@/router";
import { useInboxStore } from "@/store/v1"; import { useInboxStore } from "@/store/v1";
import { Inbox_Status } from "@/types/proto/api/v1/inbox_service"; import { Inbox_Status } from "@/types/proto/api/v1/inbox_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "./Icon";
import UserBanner from "./UserBanner"; import UserBanner from "./UserBanner";
interface NavLinkItem { interface NavLinkItem {
@ -52,25 +52,25 @@ const Navigation = (props: Props) => {
id: "header-home", id: "header-home",
path: Routes.ROOT, path: Routes.ROOT,
title: t("common.home"), title: t("common.home"),
icon: <Icon.Home className="w-6 h-auto opacity-70 shrink-0" />, icon: <HomeIcon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const resourcesNavLink: NavLinkItem = { const resourcesNavLink: NavLinkItem = {
id: "header-resources", id: "header-resources",
path: Routes.RESOURCES, path: Routes.RESOURCES,
title: t("common.resources"), title: t("common.resources"),
icon: <Icon.Paperclip className="w-6 h-auto opacity-70 shrink-0" />, icon: <PaperclipIcon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const exploreNavLink: NavLinkItem = { const exploreNavLink: NavLinkItem = {
id: "header-explore", id: "header-explore",
path: Routes.EXPLORE, path: Routes.EXPLORE,
title: t("common.explore"), title: t("common.explore"),
icon: <Icon.Globe2 className="w-6 h-auto opacity-70 shrink-0" />, icon: <Globe2Icon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const profileNavLink: NavLinkItem = { const profileNavLink: NavLinkItem = {
id: "header-profile", id: "header-profile",
path: user ? `/u/${encodeURIComponent(user.username)}` : "", path: user ? `/u/${encodeURIComponent(user.username)}` : "",
title: t("common.profile"), title: t("common.profile"),
icon: <Icon.User2 className="w-6 h-auto opacity-70 shrink-0" />, icon: <User2Icon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const inboxNavLink: NavLinkItem = { const inboxNavLink: NavLinkItem = {
id: "header-inbox", id: "header-inbox",
@ -79,7 +79,7 @@ const Navigation = (props: Props) => {
icon: ( icon: (
<> <>
<div className="relative"> <div className="relative">
<Icon.Bell className="w-6 h-auto opacity-70 shrink-0" /> <BellIcon className="w-6 h-auto opacity-70 shrink-0" />
{hasUnreadInbox && <div className="absolute top-0 left-5 w-2 h-2 rounded-full bg-blue-500"></div>} {hasUnreadInbox && <div className="absolute top-0 left-5 w-2 h-2 rounded-full bg-blue-500"></div>}
</div> </div>
</> </>
@ -89,25 +89,25 @@ const Navigation = (props: Props) => {
id: "header-archived", id: "header-archived",
path: Routes.ARCHIVED, path: Routes.ARCHIVED,
title: t("common.archived"), title: t("common.archived"),
icon: <Icon.Archive className="w-6 h-auto opacity-70 shrink-0" />, icon: <ArchiveIcon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const settingNavLink: NavLinkItem = { const settingNavLink: NavLinkItem = {
id: "header-setting", id: "header-setting",
path: Routes.SETTING, path: Routes.SETTING,
title: t("common.settings"), title: t("common.settings"),
icon: <Icon.Settings className="w-6 h-auto opacity-70 shrink-0" />, icon: <SettingsIcon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const signInNavLink: NavLinkItem = { const signInNavLink: NavLinkItem = {
id: "header-auth", id: "header-auth",
path: Routes.AUTH, path: Routes.AUTH,
title: t("common.sign-in"), title: t("common.sign-in"),
icon: <Icon.LogIn className="w-6 h-auto opacity-70 shrink-0" />, icon: <LogInIcon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const aboutNavLink: NavLinkItem = { const aboutNavLink: NavLinkItem = {
id: "header-about", id: "header-about",
path: Routes.ABOUT, path: Routes.ABOUT,
title: t("common.about"), title: t("common.about"),
icon: <Icon.Smile className="w-6 h-auto opacity-70 shrink-0" />, icon: <SmileIcon className="w-6 h-auto opacity-70 shrink-0" />,
}; };
const navLinks: NavLinkItem[] = user const navLinks: NavLinkItem[] = user

View File

@ -1,7 +1,7 @@
import { Drawer, IconButton } from "@mui/joy"; import { Drawer, IconButton } from "@mui/joy";
import { MenuIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import Icon from "./Icon";
import Navigation from "./Navigation"; import Navigation from "./Navigation";
const NavigationDrawer = () => { const NavigationDrawer = () => {
@ -23,7 +23,7 @@ const NavigationDrawer = () => {
return ( return (
<> <>
<IconButton onClick={toggleDrawer(true)}> <IconButton onClick={toggleDrawer(true)}>
<Icon.Menu className="w-5 h-auto dark:text-gray-400" /> <MenuIcon className="w-5 h-auto dark:text-gray-400" />
</IconButton> </IconButton>
<Drawer anchor="left" size="sm" open={open} onClose={toggleDrawer(false)}> <Drawer anchor="left" size="sm" open={open} onClose={toggleDrawer(false)}>
<div className="w-full h-full overflow-auto px-4 bg-zinc-100 dark:bg-zinc-900"> <div className="w-full h-full overflow-auto px-4 bg-zinc-100 dark:bg-zinc-900">

View File

@ -1,6 +1,6 @@
import { XIcon } from "lucide-react";
import React, { useState } from "react"; import React, { useState } from "react";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
import "@/less/preview-image-dialog.less"; import "@/less/preview-image-dialog.less";
const MIN_SCALE = 0.5; const MIN_SCALE = 0.5;
@ -116,7 +116,7 @@ const PreviewImageDialog: React.FC<Props> = ({ destroy, imgUrls, initialIndex }:
<> <>
<div className="btns-container"> <div className="btns-container">
<button className="btn" onClick={handleCloseBtnClick}> <button className="btn" onClick={handleCloseBtnClick}>
<Icon.X className="icon-img" /> <XIcon className="icon-img" />
</button> </button>
</div> </div>
<div className="img-container" onClick={handleImgContainerClick}> <div className="img-container" onClick={handleImgContainerClick}>

View File

@ -1,8 +1,8 @@
import { Dropdown, Menu, MenuButton } from "@mui/joy"; import { Dropdown, Menu, MenuButton } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { SmilePlusIcon } from "lucide-react";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import useClickAway from "react-use/lib/useClickAway"; import useClickAway from "react-use/lib/useClickAway";
import Icon from "@/components/Icon";
import { memoServiceClient } from "@/grpcweb"; import { memoServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { useMemoStore } from "@/store/v1"; import { useMemoStore } from "@/store/v1";
@ -76,7 +76,7 @@ const ReactionSelector = (props: Props) => {
<span <span
className={clsx("h-7 w-7 flex justify-center items-center rounded-full border dark:border-zinc-700 hover:opacity-70", className)} className={clsx("h-7 w-7 flex justify-center items-center rounded-full border dark:border-zinc-700 hover:opacity-70", className)}
> >
<Icon.SmilePlus className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" /> <SmilePlusIcon className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" />
</span> </span>
</MenuButton> </MenuButton>
<Menu className="relative text-sm" component="div" size="sm" placement="bottom-start"> <Menu className="relative text-sm" component="div" size="sm" placement="bottom-start">

View File

@ -1,4 +1,5 @@
import { Button, IconButton, Input, List, ListItem } from "@mui/joy"; import { Button, IconButton, Input, List, ListItem } from "@mui/joy";
import { XIcon } from "lucide-react";
import React, { useState } from "react"; import React, { useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { memoServiceClient } from "@/grpcweb"; import { memoServiceClient } from "@/grpcweb";
@ -7,7 +8,6 @@ import useLoading from "@/hooks/useLoading";
import { useTagStore } from "@/store/v1"; import { useTagStore } from "@/store/v1";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
interface Props extends DialogProps { interface Props extends DialogProps {
tag: string; tag: string;
@ -55,7 +55,7 @@ const RenameTagDialog: React.FC<Props> = (props: Props) => {
<div className="dialog-header-container"> <div className="dialog-header-container">
<p className="title-text">{"Rename tag"}</p> <p className="title-text">{"Rename tag"}</p>
<IconButton size="sm" onClick={() => destroy()}> <IconButton size="sm" onClick={() => destroy()}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container max-w-xs"> <div className="dialog-content-container max-w-xs">

View File

@ -1,8 +1,18 @@
import clsx from "clsx"; import clsx from "clsx";
import {
BinaryIcon,
BookIcon,
FileArchiveIcon,
FileAudioIcon,
FileEditIcon,
FileIcon,
FileTextIcon,
FileVideo2Icon,
SheetIcon,
} from "lucide-react";
import React from "react"; import React from "react";
import { Resource } from "@/types/proto/api/v1/resource_service"; import { Resource } from "@/types/proto/api/v1/resource_service";
import { getResourceType, getResourceUrl } from "@/utils/resource"; import { getResourceType, getResourceUrl } from "@/utils/resource";
import Icon from "./Icon";
import showPreviewImageDialog from "./PreviewImageDialog"; import showPreviewImageDialog from "./PreviewImageDialog";
import SquareDiv from "./kit/SquareDiv"; import SquareDiv from "./kit/SquareDiv";
@ -40,25 +50,25 @@ const ResourceIcon = (props: Props) => {
const getResourceIcon = () => { const getResourceIcon = () => {
switch (resourceType) { switch (resourceType) {
case "video/*": case "video/*":
return <Icon.FileVideo2 strokeWidth={strokeWidth} className="w-full h-auto" />; return <FileVideo2Icon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "audio/*": case "audio/*":
return <Icon.FileAudio strokeWidth={strokeWidth} className="w-full h-auto" />; return <FileAudioIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "text/*": case "text/*":
return <Icon.FileText strokeWidth={strokeWidth} className="w-full h-auto" />; return <FileTextIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "application/epub+zip": case "application/epub+zip":
return <Icon.Book strokeWidth={strokeWidth} className="w-full h-auto" />; return <BookIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "application/pdf": case "application/pdf":
return <Icon.Book strokeWidth={strokeWidth} className="w-full h-auto" />; return <BookIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "application/msword": case "application/msword":
return <Icon.FileEdit strokeWidth={strokeWidth} className="w-full h-auto" />; return <FileEditIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "application/msexcel": case "application/msexcel":
return <Icon.SheetIcon strokeWidth={strokeWidth} className="w-full h-auto" />; return <SheetIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
case "application/zip": case "application/zip":
return <Icon.FileArchiveIcon onClick={previewResource} strokeWidth={strokeWidth} className="w-full h-auto" />; return <FileArchiveIcon onClick={previewResource} strokeWidth={strokeWidth} className="w-full h-auto" />;
case "application/x-java-archive": case "application/x-java-archive":
return <Icon.BinaryIcon strokeWidth={strokeWidth} className="w-full h-auto" />; return <BinaryIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
default: default:
return <Icon.File strokeWidth={strokeWidth} className="w-full h-auto" />; return <FileIcon strokeWidth={strokeWidth} className="w-full h-auto" />;
} }
}; };

View File

@ -1,7 +1,7 @@
import { SearchIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { useMemoFilterStore } from "@/store/v1"; import { useMemoFilterStore } from "@/store/v1";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "./Icon";
import MemoDisplaySettingMenu from "./MemoDisplaySettingMenu"; import MemoDisplaySettingMenu from "./MemoDisplaySettingMenu";
const SearchBar = () => { const SearchBar = () => {
@ -29,7 +29,7 @@ const SearchBar = () => {
return ( return (
<div className="relative w-full h-auto flex flex-row justify-start items-center"> <div className="relative w-full h-auto flex flex-row justify-start items-center">
<Icon.Search className="absolute left-3 w-4 h-auto opacity-40" /> <SearchIcon className="absolute left-3 w-4 h-auto opacity-40" />
<input <input
className="w-full text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 border dark:border-zinc-800 text-sm leading-7 rounded-lg p-1 pl-8 outline-none" className="w-full text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 border dark:border-zinc-800 text-sm leading-7 rounded-lg p-1 pl-8 outline-none"
placeholder={t("memo.search-placeholder")} placeholder={t("memo.search-placeholder")}

View File

@ -1,5 +1,6 @@
import { Button, IconButton } from "@mui/joy"; import { Button, IconButton } from "@mui/joy";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { ClipboardIcon, TrashIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { userServiceClient } from "@/grpcweb"; import { userServiceClient } from "@/grpcweb";
@ -7,7 +8,6 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import { UserAccessToken } from "@/types/proto/api/v1/user_service"; import { UserAccessToken } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import showCreateAccessTokenDialog from "../CreateAccessTokenDialog"; import showCreateAccessTokenDialog from "../CreateAccessTokenDialog";
import Icon from "../Icon";
import LearnMore from "../LearnMore"; import LearnMore from "../LearnMore";
const listAccessTokens = async (name: string) => { const listAccessTokens = async (name: string) => {
@ -102,7 +102,7 @@ const AccessTokenSection = () => {
<td className="whitespace-nowrap px-3 py-2 text-sm text-gray-900 dark:text-gray-400 flex flex-row justify-start items-center gap-x-1"> <td className="whitespace-nowrap px-3 py-2 text-sm text-gray-900 dark:text-gray-400 flex flex-row justify-start items-center gap-x-1">
<span className="font-mono">{getFormatedAccessToken(userAccessToken.accessToken)}</span> <span className="font-mono">{getFormatedAccessToken(userAccessToken.accessToken)}</span>
<IconButton color="neutral" variant="plain" size="sm" onClick={() => copyAccessToken(userAccessToken.accessToken)}> <IconButton color="neutral" variant="plain" size="sm" onClick={() => copyAccessToken(userAccessToken.accessToken)}>
<Icon.Clipboard className="w-4 h-auto text-gray-400" /> <ClipboardIcon className="w-4 h-auto text-gray-400" />
</IconButton> </IconButton>
</td> </td>
<td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-400"> <td className="whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-400">
@ -123,7 +123,7 @@ const AccessTokenSection = () => {
handleDeleteAccessToken(userAccessToken.accessToken); handleDeleteAccessToken(userAccessToken.accessToken);
}} }}
> >
<Icon.Trash className="w-4 h-auto" /> <TrashIcon className="w-4 h-auto" />
</IconButton> </IconButton>
</td> </td>
</tr> </tr>

View File

@ -1,5 +1,6 @@
import { Button, Dropdown, Input, Menu, MenuButton, MenuItem, Radio, RadioGroup } from "@mui/joy"; import { Button, Dropdown, Input, Menu, MenuButton, MenuItem, Radio, RadioGroup } from "@mui/joy";
import { sortBy } from "lodash-es"; import { sortBy } from "lodash-es";
import { MoreVerticalIcon } from "lucide-react";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { userServiceClient } from "@/grpcweb"; import { userServiceClient } from "@/grpcweb";
@ -9,7 +10,6 @@ import { RowStatus } from "@/types/proto/api/v1/common";
import { User, User_Role } from "@/types/proto/api/v1/user_service"; import { User, User_Role } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import showChangeMemberPasswordDialog from "../ChangeMemberPasswordDialog"; import showChangeMemberPasswordDialog from "../ChangeMemberPasswordDialog";
import Icon from "../Icon";
interface State { interface State {
creatingUser: User; creatingUser: User;
@ -204,7 +204,7 @@ const MemberSection = () => {
) : ( ) : (
<Dropdown> <Dropdown>
<MenuButton size="sm"> <MenuButton size="sm">
<Icon.MoreVertical className="w-4 h-auto" /> <MoreVerticalIcon className="w-4 h-auto" />
</MenuButton> </MenuButton>
<Menu placement="bottom-end" size="sm"> <Menu placement="bottom-end" size="sm">
<MenuItem onClick={() => handleChangePasswordClick(user)}> <MenuItem onClick={() => handleChangePasswordClick(user)}>

View File

@ -1,10 +1,10 @@
import { Button, Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy"; import { Button, Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy";
import { MoreVerticalIcon, PenLineIcon } from "lucide-react";
import { memoServiceClient } from "@/grpcweb"; import { memoServiceClient } from "@/grpcweb";
import { downloadFileFromUrl } from "@/helpers/utils"; import { downloadFileFromUrl } from "@/helpers/utils";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import showChangePasswordDialog from "../ChangePasswordDialog"; import showChangePasswordDialog from "../ChangePasswordDialog";
import Icon from "../Icon";
import showUpdateAccountDialog from "../UpdateAccountDialog"; import showUpdateAccountDialog from "../UpdateAccountDialog";
import UserAvatar from "../UserAvatar"; import UserAvatar from "../UserAvatar";
import AccessTokenSection from "./AccessTokenSection"; import AccessTokenSection from "./AccessTokenSection";
@ -35,13 +35,13 @@ const MyAccountSection = () => {
</div> </div>
<div className="w-full flex flex-row justify-start items-center mt-2 space-x-2"> <div className="w-full flex flex-row justify-start items-center mt-2 space-x-2">
<Button variant="outlined" color="neutral" size="sm" onClick={showUpdateAccountDialog}> <Button variant="outlined" color="neutral" size="sm" onClick={showUpdateAccountDialog}>
<Icon.PenLine className="w-4 h-4 mx-auto mr-1" /> <PenLineIcon className="w-4 h-4 mx-auto mr-1" />
{t("common.edit")} {t("common.edit")}
</Button> </Button>
<Dropdown> <Dropdown>
<MenuButton slots={{ root: "div" }}> <MenuButton slots={{ root: "div" }}>
<Button variant="outlined" color="neutral" size="sm"> <Button variant="outlined" color="neutral" size="sm">
<Icon.MoreVertical className="w-4 h-4 mx-auto" /> <MoreVerticalIcon className="w-4 h-4 mx-auto" />
</Button> </Button>
</MenuButton> </MenuButton>
<Menu className="text-sm" size="sm" placement="bottom"> <Menu className="text-sm" size="sm" placement="bottom">

View File

@ -1,4 +1,5 @@
import { Button, Divider, Dropdown, List, ListItem, Menu, MenuButton, MenuItem } from "@mui/joy"; import { Button, Divider, Dropdown, List, ListItem, Menu, MenuButton, MenuItem } from "@mui/joy";
import { MoreVerticalIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -6,7 +7,6 @@ import { identityProviderServiceClient } from "@/grpcweb";
import { IdentityProvider } from "@/types/proto/api/v1/idp_service"; import { IdentityProvider } from "@/types/proto/api/v1/idp_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import showCreateIdentityProviderDialog from "../CreateIdentityProviderDialog"; import showCreateIdentityProviderDialog from "../CreateIdentityProviderDialog";
import Icon from "../Icon";
import LearnMore from "../LearnMore"; import LearnMore from "../LearnMore";
const SSOSection = () => { const SSOSection = () => {
@ -59,7 +59,7 @@ const SSOSection = () => {
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<Dropdown> <Dropdown>
<MenuButton size="sm"> <MenuButton size="sm">
<Icon.MoreVertical className="w-4 h-auto" /> <MoreVerticalIcon className="w-4 h-auto" />
</MenuButton> </MenuButton>
<Menu placement="bottom-end" size="sm"> <Menu placement="bottom-end" size="sm">
<MenuItem onClick={() => showCreateIdentityProviderDialog(identityProvider, fetchIdentityProviderList)}> <MenuItem onClick={() => showCreateIdentityProviderDialog(identityProvider, fetchIdentityProviderList)}>

View File

@ -1,5 +1,6 @@
import { Button, Divider, Input, List, ListItem, Radio, RadioGroup, Tooltip } from "@mui/joy"; import { Button, Divider, Input, List, ListItem, Radio, RadioGroup, Tooltip } from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { HelpCircleIcon } from "lucide-react";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -11,7 +12,6 @@ import {
} from "@/types/proto/api/v1/workspace_setting_service"; } from "@/types/proto/api/v1/workspace_setting_service";
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
const StorageSection = () => { const StorageSection = () => {
const t = useTranslate(); const t = useTranslate();
@ -132,7 +132,7 @@ const StorageSection = () => {
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<span className="text-gray-700 dark:text-gray-500 mr-1">{t("setting.system-section.max-upload-size")}</span> <span className="text-gray-700 dark:text-gray-500 mr-1">{t("setting.system-section.max-upload-size")}</span>
<Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top"> <Tooltip title={t("setting.system-section.max-upload-size-hint")} placement="top">
<Icon.HelpCircle className="w-4 h-auto" /> <HelpCircleIcon className="w-4 h-auto" />
</Tooltip> </Tooltip>
</div> </div>
<Input <Input

View File

@ -1,4 +1,5 @@
import { Button, IconButton } from "@mui/joy"; import { Button, IconButton } from "@mui/joy";
import { ExternalLinkIcon, TrashIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { webhookServiceClient } from "@/grpcweb"; import { webhookServiceClient } from "@/grpcweb";
@ -6,7 +7,6 @@ import useCurrentUser from "@/hooks/useCurrentUser";
import { Webhook } from "@/types/proto/api/v1/webhook_service"; import { Webhook } from "@/types/proto/api/v1/webhook_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import showCreateWebhookDialog from "../CreateWebhookDialog"; import showCreateWebhookDialog from "../CreateWebhookDialog";
import Icon from "../Icon";
const listWebhooks = async (userId: number) => { const listWebhooks = async (userId: number) => {
const { webhooks } = await webhookServiceClient.listWebhooks({ const { webhooks } = await webhookServiceClient.listWebhooks({
@ -88,7 +88,7 @@ const WebhookSection = () => {
handleDeleteWebhook(webhook); handleDeleteWebhook(webhook);
}} }}
> >
<Icon.Trash className="w-4 h-auto" /> <TrashIcon className="w-4 h-auto" />
</IconButton> </IconButton>
</td> </td>
</tr> </tr>
@ -113,7 +113,7 @@ const WebhookSection = () => {
target="_blank" target="_blank"
> >
{t("common.learn-more")} {t("common.learn-more")}
<Icon.ExternalLink className="inline w-4 h-auto ml-1" /> <ExternalLinkIcon className="inline w-4 h-auto ml-1" />
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -1,4 +1,5 @@
import { Button, Select, Textarea, Option, Divider } from "@mui/joy"; import { Button, Select, Textarea, Option, Divider } from "@mui/joy";
import { ExternalLinkIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -7,7 +8,6 @@ import { workspaceSettingNamePrefix, useWorkspaceSettingStore } from "@/store/v1
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service"; import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service";
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "../Icon";
import showUpdateCustomizedProfileDialog from "../UpdateCustomizedProfileDialog"; import showUpdateCustomizedProfileDialog from "../UpdateCustomizedProfileDialog";
const WorkspaceSection = () => { const WorkspaceSection = () => {
@ -101,7 +101,7 @@ const WorkspaceSection = () => {
target="_blank" target="_blank"
> >
{t("common.learn-more")} {t("common.learn-more")}
<Icon.ExternalLink className="inline w-4 h-auto ml-1" /> <ExternalLinkIcon className="inline w-4 h-auto ml-1" />
</Link> </Link>
</div> </div>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">

View File

@ -1,7 +1,7 @@
import { ChevronRightIcon, HashIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import useToggle from "react-use/lib/useToggle"; import useToggle from "react-use/lib/useToggle";
import { useMemoFilterStore } from "@/store/v1"; import { useMemoFilterStore } from "@/store/v1";
import Icon from "./Icon";
interface Tag { interface Tag {
key: string; key: string;
@ -108,7 +108,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
}`} }`}
> >
<div className="shrink-0"> <div className="shrink-0">
<Icon.Hash className="w-4 h-auto shrink-0 mr-1 text-gray-400 dark:text-gray-500" /> <HashIcon className="w-4 h-auto shrink-0 mr-1 text-gray-400 dark:text-gray-500" />
</div> </div>
<span className="truncate cursor-pointer hover:opacity-80" onClick={handleTagClick}> <span className="truncate cursor-pointer hover:opacity-80" onClick={handleTagClick}>
{tag.key} {tag.key}
@ -120,7 +120,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
className={`flex flex-row justify-center items-center w-6 h-6 shrink-0 transition-all rotate-0 ${showSubTags && "rotate-90"}`} className={`flex flex-row justify-center items-center w-6 h-6 shrink-0 transition-all rotate-0 ${showSubTags && "rotate-90"}`}
onClick={handleToggleBtnClick} onClick={handleToggleBtnClick}
> >
<Icon.ChevronRight className="w-5 h-5 cursor-pointer text-gray-400 dark:text-gray-500" /> <ChevronRightIcon className="w-5 h-5 cursor-pointer text-gray-400 dark:text-gray-500" />
</span> </span>
) : null} ) : null}
</div> </div>

View File

@ -1,5 +1,6 @@
import { Button, IconButton, Input, Textarea } from "@mui/joy"; import { Button, IconButton, Input, Textarea } from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { XIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { convertFileToBase64 } from "@/helpers/utils"; import { convertFileToBase64 } from "@/helpers/utils";
@ -8,7 +9,6 @@ import { userNamePrefix, useUserStore } from "@/store/v1";
import { User as UserPb } from "@/types/proto/api/v1/user_service"; import { User as UserPb } from "@/types/proto/api/v1/user_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
import UserAvatar from "./UserAvatar"; import UserAvatar from "./UserAvatar";
type Props = DialogProps; type Props = DialogProps;
@ -143,7 +143,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
<div className="dialog-header-container !w-64"> <div className="dialog-header-container !w-64">
<p className="title-text">{t("setting.account-section.update-information")}</p> <p className="title-text">{t("setting.account-section.update-information")}</p>
<IconButton size="sm" onClick={handleCloseBtnClick}> <IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container space-y-2"> <div className="dialog-content-container space-y-2">
@ -154,7 +154,7 @@ const UpdateAccountDialog: React.FC<Props> = ({ destroy }: Props) => {
<input type="file" accept="image/*" className="absolute invisible w-full h-full inset-0" onChange={handleAvatarChanged} /> <input type="file" accept="image/*" className="absolute invisible w-full h-full inset-0" onChange={handleAvatarChanged} />
</label> </label>
{state.avatarUrl && ( {state.avatarUrl && (
<Icon.X <XIcon
className="w-4 h-auto ml-1 cursor-pointer opacity-60 hover:opacity-80" className="w-4 h-auto ml-1 cursor-pointer opacity-60 hover:opacity-80"
onClick={() => onClick={() =>
setPartialState({ setPartialState({

View File

@ -1,5 +1,6 @@
import { Button, IconButton, Input } from "@mui/joy"; import { Button, IconButton, Input } from "@mui/joy";
import Textarea from "@mui/joy/Textarea/Textarea"; import Textarea from "@mui/joy/Textarea/Textarea";
import { XIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { workspaceSettingNamePrefix, useWorkspaceSettingStore } from "@/store/v1"; import { workspaceSettingNamePrefix, useWorkspaceSettingStore } from "@/store/v1";
@ -8,7 +9,6 @@ import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import AppearanceSelect from "./AppearanceSelect"; import AppearanceSelect from "./AppearanceSelect";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon";
import LocaleSelect from "./LocaleSelect"; import LocaleSelect from "./LocaleSelect";
type Props = DialogProps; type Props = DialogProps;
@ -103,7 +103,7 @@ const UpdateCustomizedProfileDialog: React.FC<Props> = ({ destroy }: Props) => {
<div className="dialog-header-container"> <div className="dialog-header-container">
<p className="title-text">{t("setting.system-section.customize-server.title")}</p> <p className="title-text">{t("setting.system-section.customize-server.title")}</p>
<IconButton size="sm" onClick={handleCloseButtonClick}> <IconButton size="sm" onClick={handleCloseButtonClick}>
<Icon.X className="w-5 h-auto" /> <XIcon className="w-5 h-auto" />
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container min-w-[16rem]"> <div className="dialog-content-container min-w-[16rem]">

View File

@ -1,11 +1,11 @@
import { Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy"; import { Dropdown, Menu, MenuButton, MenuItem } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { LogOutIcon, SmileIcon } from "lucide-react";
import { authServiceClient } from "@/grpcweb"; import { authServiceClient } from "@/grpcweb";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import useNavigateTo from "@/hooks/useNavigateTo"; import useNavigateTo from "@/hooks/useNavigateTo";
import { Routes } from "@/router"; import { Routes } from "@/router";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import Icon from "./Icon";
import UserAvatar from "./UserAvatar"; import UserAvatar from "./UserAvatar";
interface Props { interface Props {
@ -41,11 +41,11 @@ const UserBanner = (props: Props) => {
</MenuButton> </MenuButton>
<Menu placement="bottom-start" style={{ zIndex: "9999" }}> <Menu placement="bottom-start" style={{ zIndex: "9999" }}>
<MenuItem onClick={handleSignOut}> <MenuItem onClick={handleSignOut}>
<Icon.LogOut className="w-4 h-auto opacity-60" /> <LogOutIcon className="w-4 h-auto opacity-60" />
<span className="truncate">{t("common.sign-out")}</span> <span className="truncate">{t("common.sign-out")}</span>
</MenuItem> </MenuItem>
<MenuItem onClick={() => navigateTo(Routes.ABOUT)}> <MenuItem onClick={() => navigateTo(Routes.ABOUT)}>
<Icon.Smile className="w-4 h-auto opacity-60" /> <SmileIcon className="w-4 h-auto opacity-60" />
<span className="truncate">{t("common.about")}</span> <span className="truncate">{t("common.about")}</span>
</MenuItem> </MenuItem>
</Menu> </Menu>

View File

@ -2,6 +2,7 @@ import { Divider, Tooltip } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { countBy } from "lodash-es"; import { countBy } from "lodash-es";
import { CalendarDaysIcon, CheckCircleIcon, Code2Icon, LinkIcon, ListTodoIcon, MoreVerticalIcon, RefreshCcwIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { memoServiceClient } from "@/grpcweb"; import { memoServiceClient } from "@/grpcweb";
@ -11,7 +12,6 @@ import i18n from "@/i18n";
import { useMemoFilterStore, useMemoStore } from "@/store/v1"; import { useMemoFilterStore, useMemoStore } from "@/store/v1";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import ActivityCalendar from "./ActivityCalendar"; import ActivityCalendar from "./ActivityCalendar";
import Icon from "./Icon";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover";
interface UserMemoStats { interface UserMemoStats {
@ -75,7 +75,7 @@ const UserStatisticsView = () => {
<div className="group w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800"> <div className="group w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800">
<div className="w-full mb-1 flex flex-row justify-between items-center"> <div className="w-full mb-1 flex flex-row justify-between items-center">
<div className="relative text-base font-medium leading-6 flex flex-row items-center dark:text-gray-400"> <div className="relative text-base font-medium leading-6 flex flex-row items-center dark:text-gray-400">
<Icon.CalendarDays className="w-5 h-auto mr-1 opacity-60" strokeWidth={1.5} /> <CalendarDaysIcon className="w-5 h-auto mr-1 opacity-60" strokeWidth={1.5} />
<span>{dayjs(monthString).toDate().toLocaleString(i18n.language, { year: "numeric", month: "long" })}</span> <span>{dayjs(monthString).toDate().toLocaleString(i18n.language, { year: "numeric", month: "long" })}</span>
<input <input
className="inset-0 absolute z-1 opacity-0" className="inset-0 absolute z-1 opacity-0"
@ -88,11 +88,11 @@ const UserStatisticsView = () => {
<div className="invisible group-hover:visible flex justify-end items-center"> <div className="invisible group-hover:visible flex justify-end items-center">
<Popover> <Popover>
<PopoverTrigger> <PopoverTrigger>
<Icon.MoreVertical className="w-4 h-auto shrink-0 opacity-60" /> <MoreVerticalIcon className="w-4 h-auto shrink-0 opacity-60" />
</PopoverTrigger> </PopoverTrigger>
<PopoverContent align="end" alignOffset={-12}> <PopoverContent align="end" alignOffset={-12}>
<button className="w-auto flex flex-row justify-between items-center gap-2 hover:opacity-80" onClick={rebuildMemoTags}> <button className="w-auto flex flex-row justify-between items-center gap-2 hover:opacity-80" onClick={rebuildMemoTags}>
<Icon.RefreshCcw className="text-gray-400 w-4 h-auto cursor-pointer opacity-60" /> <RefreshCcwIcon className="text-gray-400 w-4 h-auto cursor-pointer opacity-60" />
<span className="text-sm shrink-0 text-gray-500 dark:text-gray-400">Refresh</span> <span className="text-sm shrink-0 text-gray-500 dark:text-gray-400">Refresh</span>
</button> </button>
</PopoverContent> </PopoverContent>
@ -114,7 +114,7 @@ const UserStatisticsView = () => {
onClick={() => memoFilterStore.addFilter({ factor: "property.hasLink", value: "" })} onClick={() => memoFilterStore.addFilter({ factor: "property.hasLink", value: "" })}
> >
<div className="w-auto flex justify-start items-center mr-1"> <div className="w-auto flex justify-start items-center mr-1">
<Icon.Link className="w-4 h-auto mr-1" /> <LinkIcon className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.links")}</span> <span className="block text-sm">{t("memo.links")}</span>
</div> </div>
<span className="text-sm truncate">{memoStats.link}</span> <span className="text-sm truncate">{memoStats.link}</span>
@ -124,11 +124,7 @@ const UserStatisticsView = () => {
onClick={() => memoFilterStore.addFilter({ factor: "property.hasTaskList", value: "" })} onClick={() => memoFilterStore.addFilter({ factor: "property.hasTaskList", value: "" })}
> >
<div className="w-auto flex justify-start items-center mr-1"> <div className="w-auto flex justify-start items-center mr-1">
{memoStats.incompleteTasks > 0 ? ( {memoStats.incompleteTasks > 0 ? <ListTodoIcon className="w-4 h-auto mr-1" /> : <CheckCircleIcon className="w-4 h-auto mr-1" />}
<Icon.ListTodo className="w-4 h-auto mr-1" />
) : (
<Icon.CheckCircle className="w-4 h-auto mr-1" />
)}
<span className="block text-sm">{t("memo.to-do")}</span> <span className="block text-sm">{t("memo.to-do")}</span>
</div> </div>
{memoStats.incompleteTasks > 0 ? ( {memoStats.incompleteTasks > 0 ? (
@ -148,7 +144,7 @@ const UserStatisticsView = () => {
onClick={() => memoFilterStore.addFilter({ factor: "property.hasCode", value: "" })} onClick={() => memoFilterStore.addFilter({ factor: "property.hasCode", value: "" })}
> >
<div className="w-auto flex justify-start items-center mr-1"> <div className="w-auto flex justify-start items-center mr-1">
<Icon.Code2 className="w-4 h-auto mr-1" /> <Code2Icon className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.code")}</span> <span className="block text-sm">{t("memo.code")}</span>
</div> </div>
<span className="text-sm truncate">{memoStats.code}</span> <span className="text-sm truncate">{memoStats.code}</span>

View File

@ -1,6 +1,6 @@
import clsx from "clsx"; import clsx from "clsx";
import { Globe2Icon, LockIcon, UsersIcon } from "lucide-react";
import { Visibility } from "@/types/proto/api/v1/memo_service"; import { Visibility } from "@/types/proto/api/v1/memo_service";
import Icon from "./Icon";
interface Props { interface Props {
visibility: Visibility; visibility: Visibility;
@ -11,11 +11,11 @@ const VisibilityIcon = (props: Props) => {
let VIcon = null; let VIcon = null;
if (visibility === Visibility.PRIVATE) { if (visibility === Visibility.PRIVATE) {
VIcon = Icon.Lock; VIcon = LockIcon;
} else if (visibility === Visibility.PROTECTED) { } else if (visibility === Visibility.PROTECTED) {
VIcon = Icon.Users; VIcon = UsersIcon;
} else if (visibility === Visibility.PUBLIC) { } else if (visibility === Visibility.PUBLIC) {
VIcon = Icon.Globe2; VIcon = Globe2Icon;
} }
if (!VIcon) { if (!VIcon) {
return null; return null;

View File

@ -1,9 +1,9 @@
import { Button, IconButton, Tooltip } from "@mui/joy"; import { Button, IconButton, Tooltip } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import { Suspense, useEffect, useState } from "react"; import { Suspense, useEffect, useState } from "react";
import { Outlet, useLocation } from "react-router-dom"; import { Outlet, useLocation } from "react-router-dom";
import useLocalStorage from "react-use/lib/useLocalStorage"; import useLocalStorage from "react-use/lib/useLocalStorage";
import Icon from "@/components/Icon";
import Navigation from "@/components/Navigation"; import Navigation from "@/components/Navigation";
import useCurrentUser from "@/hooks/useCurrentUser"; import useCurrentUser from "@/hooks/useCurrentUser";
import useResponsiveWidth from "@/hooks/useResponsiveWidth"; import useResponsiveWidth from "@/hooks/useResponsiveWidth";
@ -55,13 +55,13 @@ const RootLayout = () => {
onClick={() => setCollapsed(!collapsed)} onClick={() => setCollapsed(!collapsed)}
> >
{!collapsed ? ( {!collapsed ? (
<Button variant="plain" color="neutral" startDecorator={<Icon.ChevronLeft className="w-5 h-auto opacity-70" />}> <Button variant="plain" color="neutral" startDecorator={<ChevronLeftIcon className="w-5 h-auto opacity-70" />}>
{t("common.collapse")} {t("common.collapse")}
</Button> </Button>
) : ( ) : (
<Tooltip title={t("common.expand")} placement="right" arrow> <Tooltip title={t("common.expand")} placement="right" arrow>
<IconButton> <IconButton>
<Icon.ChevronRight className="w-5 h-auto opacity-70" /> <ChevronRightIcon className="w-5 h-auto opacity-70" />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
)} )}

View File

@ -1,5 +1,5 @@
import { Link } from "@mui/joy"; import { Link } from "@mui/joy";
import Icon from "@/components/Icon"; import { DotIcon } from "lucide-react";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
const About = () => { const About = () => {
@ -16,15 +16,15 @@ const About = () => {
<Link underline="always" href="https://www.github.com/usememos/memos" target="_blank"> <Link underline="always" href="https://www.github.com/usememos/memos" target="_blank">
GitHub Repo GitHub Repo
</Link> </Link>
<Icon.Dot className="w-4 h-auto opacity-60" /> <DotIcon className="w-4 h-auto opacity-60" />
<Link underline="always" href="https://www.usememos.com/" target="_blank"> <Link underline="always" href="https://www.usememos.com/" target="_blank">
Official Website Official Website
</Link> </Link>
<Icon.Dot className="w-4 h-auto opacity-60" /> <DotIcon className="w-4 h-auto opacity-60" />
<Link underline="always" href="https://www.usememos.com/blog" target="_blank"> <Link underline="always" href="https://www.usememos.com/blog" target="_blank">
Blogs Blogs
</Link> </Link>
<Icon.Dot className="w-4 h-auto opacity-60" /> <DotIcon className="w-4 h-auto opacity-60" />
<Link underline="always" href="https://www.usememos.com/docs" target="_blank"> <Link underline="always" href="https://www.usememos.com/docs" target="_blank">
Documents Documents
</Link> </Link>

View File

@ -1,10 +1,10 @@
import { Button, Tooltip } from "@mui/joy"; import { Button, Tooltip } from "@mui/joy";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ArchiveIcon, ArchiveRestoreIcon, ArrowDownIcon, TrashIcon } from "lucide-react";
import { ClientError } from "nice-grpc-web"; import { ClientError } from "nice-grpc-web";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import Icon from "@/components/Icon";
import MemoContent from "@/components/MemoContent"; import MemoContent from "@/components/MemoContent";
import MemoFilters from "@/components/MemoFilters"; import MemoFilters from "@/components/MemoFilters";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
@ -97,7 +97,7 @@ const Archived = () => {
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<div className="w-full flex flex-row justify-between items-center mb-2"> <div className="w-full flex flex-row justify-between items-center mb-2">
<div className="flex flex-row justify-start items-center gap-1"> <div className="flex flex-row justify-start items-center gap-1">
<Icon.Archive className="w-5 h-auto opacity-70 shrink-0" /> <ArchiveIcon className="w-5 h-auto opacity-70 shrink-0" />
<span>{t("common.archived")}</span> <span>{t("common.archived")}</span>
</div> </div>
<div className="w-44"> <div className="w-44">
@ -119,12 +119,12 @@ const Archived = () => {
<div className="flex flex-row justify-end items-center gap-x-2"> <div className="flex flex-row justify-end items-center gap-x-2">
<Tooltip title={t("common.restore")} placement="top"> <Tooltip title={t("common.restore")} placement="top">
<button onClick={() => handleRestoreMemoClick(memo)}> <button onClick={() => handleRestoreMemoClick(memo)}>
<Icon.ArchiveRestore className="w-4 h-auto cursor-pointer text-gray-500 dark:text-gray-400" /> <ArchiveRestoreIcon className="w-4 h-auto cursor-pointer text-gray-500 dark:text-gray-400" />
</button> </button>
</Tooltip> </Tooltip>
<Tooltip title={t("common.delete")} placement="top"> <Tooltip title={t("common.delete")} placement="top">
<button onClick={() => handleDeleteMemoClick(memo)} className="text-gray-500 dark:text-gray-400"> <button onClick={() => handleDeleteMemoClick(memo)} className="text-gray-500 dark:text-gray-400">
<Icon.Trash className="w-4 h-auto cursor-pointer" /> <TrashIcon className="w-4 h-auto cursor-pointer" />
</button> </button>
</Tooltip> </Tooltip>
</div> </div>
@ -138,7 +138,7 @@ const Archived = () => {
variant="plain" variant="plain"
color="neutral" color="neutral"
loading={isRequesting} loading={isRequesting}
endDecorator={<Icon.ArrowDown className="w-4 h-auto" />} endDecorator={<ArrowDownIcon className="w-4 h-auto" />}
onClick={() => fetchMemos(nextPageToken)} onClick={() => fetchMemos(nextPageToken)}
> >
{t("memo.load-more")} {t("memo.load-more")}

View File

@ -1,8 +1,8 @@
import { last } from "lodash-es"; import { last } from "lodash-es";
import { LoaderIcon } from "lucide-react";
import { ClientError } from "nice-grpc-web"; import { ClientError } from "nice-grpc-web";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom";
import Icon from "@/components/Icon";
import { authServiceClient } from "@/grpcweb"; import { authServiceClient } from "@/grpcweb";
import { absolutifyLink } from "@/helpers/utils"; import { absolutifyLink } from "@/helpers/utils";
import useNavigateTo from "@/hooks/useNavigateTo"; import useNavigateTo from "@/hooks/useNavigateTo";
@ -70,7 +70,7 @@ const AuthCallback = () => {
return ( return (
<div className="p-4 py-24 w-full h-full flex justify-center items-center"> <div className="p-4 py-24 w-full h-full flex justify-center items-center">
{state.loading ? ( {state.loading ? (
<Icon.Loader className="animate-spin dark:text-gray-200" /> <LoaderIcon className="animate-spin dark:text-gray-200" />
) : ( ) : (
<div className="max-w-lg font-mono whitespace-pre-wrap opacity-80">{state.errorMessage}</div> <div className="max-w-lg font-mono whitespace-pre-wrap opacity-80">{state.errorMessage}</div>
)} )}

View File

@ -1,10 +1,10 @@
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ArrowDownIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import { ExploreSidebar, ExploreSidebarDrawer } from "@/components/ExploreSidebar"; import { ExploreSidebar, ExploreSidebarDrawer } from "@/components/ExploreSidebar";
import Icon from "@/components/Icon";
import MemoFilters from "@/components/MemoFilters"; import MemoFilters from "@/components/MemoFilters";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
@ -84,7 +84,7 @@ const Explore = () => {
variant="plain" variant="plain"
color="neutral" color="neutral"
loading={isRequesting} loading={isRequesting}
endDecorator={<Icon.ArrowDown className="w-4 h-auto" />} endDecorator={<ArrowDownIcon className="w-4 h-auto" />}
onClick={() => fetchMemos(nextPageToken)} onClick={() => fetchMemos(nextPageToken)}
> >
{t("memo.load-more")} {t("memo.load-more")}

View File

@ -1,10 +1,10 @@
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ArrowDownIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import { HomeSidebar, HomeSidebarDrawer } from "@/components/HomeSidebar"; import { HomeSidebar, HomeSidebarDrawer } from "@/components/HomeSidebar";
import Icon from "@/components/Icon";
import MemoEditor from "@/components/MemoEditor"; import MemoEditor from "@/components/MemoEditor";
import MemoFilters from "@/components/MemoFilters"; import MemoFilters from "@/components/MemoFilters";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
@ -100,7 +100,7 @@ const Home = () => {
variant="plain" variant="plain"
color="neutral" color="neutral"
loading={isRequesting} loading={isRequesting}
endDecorator={<Icon.ArrowDown className="w-4 h-auto" />} endDecorator={<ArrowDownIcon className="w-4 h-auto" />}
onClick={() => fetchMemos(nextPageToken)} onClick={() => fetchMemos(nextPageToken)}
> >
{t("memo.load-more")} {t("memo.load-more")}

View File

@ -1,6 +1,6 @@
import { BellIcon } from "lucide-react";
import { useEffect } from "react"; import { useEffect } from "react";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import Icon from "@/components/Icon";
import MemoCommentMessage from "@/components/Inbox/MemoCommentMessage"; import MemoCommentMessage from "@/components/Inbox/MemoCommentMessage";
import VersionUpdateMessage from "@/components/Inbox/VersionUpdateMessage"; import VersionUpdateMessage from "@/components/Inbox/VersionUpdateMessage";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
@ -29,7 +29,7 @@ const Inboxes = () => {
<div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-800 text-black dark:text-gray-300"> <div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-800 text-black dark:text-gray-300">
<div className="relative w-full flex flex-row justify-between items-center"> <div className="relative w-full flex flex-row justify-between items-center">
<p className="py-1 flex flex-row justify-start items-center select-none opacity-80"> <p className="py-1 flex flex-row justify-start items-center select-none opacity-80">
<Icon.Bell className="w-6 h-auto mr-1 opacity-80" /> <BellIcon className="w-6 h-auto mr-1 opacity-80" />
<span className="text-lg">{t("common.inbox")}</span> <span className="text-lg">{t("common.inbox")}</span>
</p> </p>
</div> </div>

View File

@ -1,10 +1,10 @@
import Icon from "@/components/Icon"; import { LoaderIcon } from "lucide-react";
function Loading() { function Loading() {
return ( return (
<div className="fixed w-full h-full flex flex-row justify-center items-center"> <div className="fixed w-full h-full flex flex-row justify-center items-center">
<div className="w-80 max-w-full h-full py-4 flex flex-col justify-center items-center"> <div className="w-80 max-w-full h-full py-4 flex flex-col justify-center items-center">
<Icon.Loader className="animate-spin dark:text-gray-200" /> <LoaderIcon className="animate-spin dark:text-gray-200" />
</div> </div>
</div> </div>
); );

View File

@ -1,10 +1,10 @@
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import clsx from "clsx"; import clsx from "clsx";
import { ArrowUpLeftFromCircleIcon, MessageCircleIcon } from "lucide-react";
import { ClientError } from "nice-grpc-web"; import { ClientError } from "nice-grpc-web";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import Icon from "@/components/Icon";
import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar"; import { MemoDetailSidebar, MemoDetailSidebarDrawer } from "@/components/MemoDetailSidebar";
import MemoEditor from "@/components/MemoEditor"; import MemoEditor from "@/components/MemoEditor";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
@ -98,7 +98,7 @@ const MemoDetail = () => {
to={`/m/${parentMemo.uid}`} to={`/m/${parentMemo.uid}`}
unstable_viewTransition unstable_viewTransition
> >
<Icon.ArrowUpLeftFromCircle className="w-4 h-auto shrink-0 opacity-60 mr-2" /> <ArrowUpLeftFromCircleIcon className="w-4 h-auto shrink-0 opacity-60 mr-2" />
<span className="truncate">{parentMemo.content}</span> <span className="truncate">{parentMemo.content}</span>
</Link> </Link>
</div> </div>
@ -123,7 +123,7 @@ const MemoDetail = () => {
<Button <Button
variant="plain" variant="plain"
color="neutral" color="neutral"
endDecorator={<Icon.MessageCircle className="w-5 h-auto text-gray-500" />} endDecorator={<MessageCircleIcon className="w-5 h-auto text-gray-500" />}
onClick={handleShowCommentEditor} onClick={handleShowCommentEditor}
> >
<span className="font-normal text-gray-500">{t("memo.comment.write-a-comment")}</span> <span className="font-normal text-gray-500">{t("memo.comment.write-a-comment")}</span>
@ -134,7 +134,7 @@ const MemoDetail = () => {
<> <>
<div className="w-full flex flex-row justify-between items-center px-3 mb-2"> <div className="w-full flex flex-row justify-between items-center px-3 mb-2">
<div className="flex flex-row justify-start items-center"> <div className="flex flex-row justify-start items-center">
<Icon.MessageCircle className="w-5 h-auto text-gray-400 mr-1" /> <MessageCircleIcon className="w-5 h-auto text-gray-400 mr-1" />
<span className="text-gray-400 text-sm">{t("memo.comment.self")}</span> <span className="text-gray-400 text-sm">{t("memo.comment.self")}</span>
<span className="text-gray-400 text-sm ml-1">({comments.length})</span> <span className="text-gray-400 text-sm ml-1">({comments.length})</span>
</div> </div>

View File

@ -1,9 +1,9 @@
import { Divider, IconButton, Input, Tooltip } from "@mui/joy"; import { Divider, IconButton, Input, Tooltip } from "@mui/joy";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { includes } from "lodash-es"; import { includes } from "lodash-es";
import { PaperclipIcon, SearchIcon, TrashIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import Icon from "@/components/Icon";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
import ResourceIcon from "@/components/ResourceIcon"; import ResourceIcon from "@/components/ResourceIcon";
import { resourceServiceClient } from "@/grpcweb"; import { resourceServiceClient } from "@/grpcweb";
@ -68,14 +68,14 @@ const Resources = () => {
<div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-800 text-black dark:text-gray-300"> <div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-800 text-black dark:text-gray-300">
<div className="relative w-full flex flex-row justify-between items-center"> <div className="relative w-full flex flex-row justify-between items-center">
<p className="py-1 flex flex-row justify-start items-center select-none opacity-80"> <p className="py-1 flex flex-row justify-start items-center select-none opacity-80">
<Icon.Paperclip className="w-6 h-auto mr-1 opacity-80" /> <PaperclipIcon className="w-6 h-auto mr-1 opacity-80" />
<span className="text-lg">{t("common.resources")}</span> <span className="text-lg">{t("common.resources")}</span>
</p> </p>
<div> <div>
<Input <Input
className="max-w-[8rem]" className="max-w-[8rem]"
placeholder={t("common.search")} placeholder={t("common.search")}
startDecorator={<Icon.Search className="w-4 h-auto" />} startDecorator={<SearchIcon className="w-4 h-auto" />}
value={state.searchQuery} value={state.searchQuery}
onChange={(e) => setState({ ...state, searchQuery: e.target.value })} onChange={(e) => setState({ ...state, searchQuery: e.target.value })}
/> />
@ -133,7 +133,7 @@ const Resources = () => {
<span className="text-gray-500 dark:text-gray-500 opacity-80">({unusedResources.length})</span> <span className="text-gray-500 dark:text-gray-500 opacity-80">({unusedResources.length})</span>
<Tooltip title="Delete all" placement="top"> <Tooltip title="Delete all" placement="top">
<IconButton size="sm" onClick={handleDeleteUnusedResources}> <IconButton size="sm" onClick={handleDeleteUnusedResources}>
<Icon.Trash className="w-4 h-auto opacity-60" /> <TrashIcon className="w-4 h-auto opacity-60" />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
</div> </div>

View File

@ -1,7 +1,6 @@
import { Option, Select } from "@mui/joy"; import { Option, Select } from "@mui/joy";
import { LucideIcon } from "lucide-react"; import { CogIcon, DatabaseIcon, KeyIcon, LibraryIcon, LucideIcon, Settings2Icon, UserIcon, UsersIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import Icon from "@/components/Icon";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
import MemberSection from "@/components/Settings/MemberSection"; import MemberSection from "@/components/Settings/MemberSection";
import MemoRelatedSettings from "@/components/Settings/MemoRelatedSettings"; import MemoRelatedSettings from "@/components/Settings/MemoRelatedSettings";
@ -27,13 +26,13 @@ interface State {
const BASIC_SECTIONS: SettingSection[] = ["my-account", "preference"]; const BASIC_SECTIONS: SettingSection[] = ["my-account", "preference"];
const ADMIN_SECTIONS: SettingSection[] = ["member", "system", "memo-related", "storage", "sso"]; const ADMIN_SECTIONS: SettingSection[] = ["member", "system", "memo-related", "storage", "sso"];
const SECTION_ICON_MAP: Record<SettingSection, LucideIcon> = { const SECTION_ICON_MAP: Record<SettingSection, LucideIcon> = {
"my-account": Icon.User, "my-account": UserIcon,
preference: Icon.Cog, preference: CogIcon,
member: Icon.Users, member: UsersIcon,
system: Icon.Settings2, system: Settings2Icon,
"memo-related": Icon.Library, "memo-related": LibraryIcon,
storage: Icon.Database, storage: DatabaseIcon,
sso: Icon.Key, sso: KeyIcon,
}; };
const Setting = () => { const Setting = () => {

View File

@ -1,11 +1,11 @@
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ArrowDownIcon, ExternalLinkIcon } from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Empty from "@/components/Empty"; import Empty from "@/components/Empty";
import Icon from "@/components/Icon";
import MemoFilters from "@/components/MemoFilters"; import MemoFilters from "@/components/MemoFilters";
import MemoView from "@/components/MemoView"; import MemoView from "@/components/MemoView";
import MobileHeader from "@/components/MobileHeader"; import MobileHeader from "@/components/MobileHeader";
@ -113,7 +113,7 @@ const UserProfile = () => {
<Button <Button
color="neutral" color="neutral"
variant="outlined" variant="outlined"
endDecorator={<Icon.ExternalLink className="w-4 h-auto opacity-60" />} endDecorator={<ExternalLinkIcon className="w-4 h-auto opacity-60" />}
onClick={handleCopyProfileLink} onClick={handleCopyProfileLink}
> >
{t("common.share")} {t("common.share")}
@ -140,7 +140,7 @@ const UserProfile = () => {
variant="plain" variant="plain"
color="neutral" color="neutral"
loading={isRequesting} loading={isRequesting}
endDecorator={<Icon.ArrowDown className="w-4 h-auto" />} endDecorator={<ArrowDownIcon className="w-4 h-auto" />}
onClick={() => fetchMemos(nextPageToken)} onClick={() => fetchMemos(nextPageToken)}
> >
{t("memo.load-more")} {t("memo.load-more")}