mirror of https://github.com/usememos/memos
chore: add data empty placeholder (#1913)
parent
7e391bd53d
commit
0292f472e0
Binary file not shown.
After Width: | Height: | Size: 127 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
const Empty = () => {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto">
|
||||||
|
<img className="w-24 h-auto dark:opacity-40" src="/assets/empty.png" alt="" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Empty;
|
@ -1,283 +0,0 @@
|
|||||||
import { ReactNode } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Button, IconButton, Tooltip } from "@mui/joy";
|
|
||||||
import { generateDialog } from "../Dialog";
|
|
||||||
import Icon from "../Icon";
|
|
||||||
|
|
||||||
const openUrl = (url?: string) => {
|
|
||||||
window.open(url, "_blank");
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Options for {@link HelpButton} */
|
|
||||||
interface HelpProps {
|
|
||||||
/**
|
|
||||||
* Plain text to show in the dialog.
|
|
||||||
*
|
|
||||||
* If the text contains "\n", it will be split to multiple paragraphs.
|
|
||||||
*/
|
|
||||||
text?: string;
|
|
||||||
/**
|
|
||||||
* The title of the dialog.
|
|
||||||
*
|
|
||||||
* If not provided, the title will be set according to the `icon` prop.
|
|
||||||
*/
|
|
||||||
title?: string;
|
|
||||||
/**
|
|
||||||
* External documentation URL.
|
|
||||||
*
|
|
||||||
* If provided, this will be shown as a link button in the bottom of the dialog.
|
|
||||||
*
|
|
||||||
* If provided alone, the button will just open the URL in a new tab.
|
|
||||||
*
|
|
||||||
* @param {string} url - External URL to the documentation.
|
|
||||||
*/
|
|
||||||
url?: string;
|
|
||||||
/**
|
|
||||||
* The tooltip of the button.
|
|
||||||
*/
|
|
||||||
hint?: string | "none";
|
|
||||||
/**
|
|
||||||
* The placement of the hovering hint.
|
|
||||||
* @defaultValue "top"
|
|
||||||
*/
|
|
||||||
hintPlacement?: "top" | "bottom" | "left" | "right";
|
|
||||||
/**
|
|
||||||
* The icon to show in the button.
|
|
||||||
*
|
|
||||||
* Also used to infer `title` and `hint`, if they are not provided.
|
|
||||||
*
|
|
||||||
* @defaultValue Icon.HelpCircle
|
|
||||||
* @see {@link Icon.LucideIcon}
|
|
||||||
*/
|
|
||||||
icon?: Icon.LucideIcon | "link" | "info" | "help" | "alert" | "warn";
|
|
||||||
/**
|
|
||||||
* The className for the button.
|
|
||||||
* @defaultValue `!-mt-2` (aligns the button vertically with nearby text)
|
|
||||||
*/
|
|
||||||
className?: string;
|
|
||||||
/**
|
|
||||||
* The color of the button.
|
|
||||||
* @defaultValue "neutral"
|
|
||||||
*/
|
|
||||||
color?: "primary" | "neutral" | "danger" | "info" | "success" | "warning";
|
|
||||||
/**
|
|
||||||
* The variant of the button.
|
|
||||||
* @defaultValue "plain"
|
|
||||||
*/
|
|
||||||
variant?: "plain" | "outlined" | "soft" | "solid";
|
|
||||||
/**
|
|
||||||
* The size of the button.
|
|
||||||
* @defaultValue "md"
|
|
||||||
*/
|
|
||||||
size?: "sm" | "md" | "lg";
|
|
||||||
/**
|
|
||||||
* `ReactNode` HTML content to show in the dialog.
|
|
||||||
*
|
|
||||||
* If provided, will be shown before `text`.
|
|
||||||
*
|
|
||||||
* You'll probably want to use `text` instead.
|
|
||||||
*/
|
|
||||||
children?: ReactNode | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HelpDialogProps extends HelpProps, DialogProps {}
|
|
||||||
|
|
||||||
const HelpfulDialog: React.FC<HelpDialogProps> = (props: HelpDialogProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { children, destroy, icon } = props;
|
|
||||||
const LucideIcon = icon as Icon.LucideIcon;
|
|
||||||
const handleCloseBtnClick = () => {
|
|
||||||
destroy();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="dialog-header-container">
|
|
||||||
<LucideIcon size="24" />
|
|
||||||
<p className="title-text text-left">{props.title}</p>
|
|
||||||
<button className="btn close-btn" onClick={handleCloseBtnClick}>
|
|
||||||
<Icon.X />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="dialog-content-container max-w-sm">
|
|
||||||
{children}
|
|
||||||
{props.text
|
|
||||||
? props.text.split(/\n|\\n/).map((text) => {
|
|
||||||
return (
|
|
||||||
<p key={text} className="mt-2 break-words text-justify">
|
|
||||||
{text}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: null}
|
|
||||||
<div className="mt-2 w-full flex flex-row justify-end space-x-2">
|
|
||||||
{props.url ? (
|
|
||||||
<Button className="btn-normal" variant="outlined" color={props.color} onClick={() => openUrl(props.url)}>
|
|
||||||
{t("common.learn-more")}
|
|
||||||
<Icon.ExternalLink className="ml-1 w-4 h-4 opacity-80" />
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
<Button className="btn-normal" variant="outlined" color={props.color} onClick={handleCloseBtnClick}>
|
|
||||||
{t("common.close")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function showHelpDialog(props: HelpProps) {
|
|
||||||
generateDialog(
|
|
||||||
{
|
|
||||||
className: "help-dialog",
|
|
||||||
dialogName: "help-dialog",
|
|
||||||
clickSpaceDestroy: true,
|
|
||||||
},
|
|
||||||
HelpfulDialog,
|
|
||||||
props
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a helpful `IconButton` that behaves differently depending on the props.
|
|
||||||
*
|
|
||||||
* The main purpose of this component is to avoid UI clutter.
|
|
||||||
*
|
|
||||||
* Use the property `icon` to set the icon and infer the title and hint automatically.
|
|
||||||
*
|
|
||||||
* Use cases:
|
|
||||||
* - Button with just a hover hint
|
|
||||||
* - Button with a hover hint and link
|
|
||||||
* - Button with a hover hint that opens a dialog with text and a link.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* <Helpful hint="Hint" />
|
|
||||||
* <Helpful hint="This is a hint with a link" url="https://usememos.com/" />
|
|
||||||
* <Helpful icon="warn" text={t("i18n.key.long-dialog-text")} url="https://usememos.com/" />
|
|
||||||
* <Helpful />
|
|
||||||
*
|
|
||||||
* <div className="flex flex-row">
|
|
||||||
* <span className="ml-2">Sample alignment</span>
|
|
||||||
* <Helpful hint="Button with hint" />
|
|
||||||
* </div>
|
|
||||||
|
|
||||||
* @param props.title - The title of the dialog. Defaults to "Learn more" i18n key.
|
|
||||||
* @param props.text - Plain text to show in the dialog. Line breaks are supported.
|
|
||||||
* @param props.url - External memos documentation URL.
|
|
||||||
* @param props.hint - The hint when hovering the button.
|
|
||||||
* @param props.hintPlacement - The placement of the hovering hint. Defaults to "top".
|
|
||||||
* @param props.icon - The icon to show in the button.
|
|
||||||
* @param props.className - The class name for the button.
|
|
||||||
* @param {HelpProps} props - See {@link HelpDialogProps} for all exposed props.
|
|
||||||
*/
|
|
||||||
const HelpButton = (props: HelpProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const color = props.color ?? "neutral";
|
|
||||||
const variant = props.variant ?? "plain";
|
|
||||||
const className = props.className ?? "";
|
|
||||||
const hintPlacement = props.hintPlacement ?? "top";
|
|
||||||
const iconButtonSize = "sm";
|
|
||||||
|
|
||||||
const dialogAvailable = props.text || props.children;
|
|
||||||
const clickActionAvailable = props.url || dialogAvailable;
|
|
||||||
const onlyUrlAvailable = props.url && !dialogAvailable;
|
|
||||||
|
|
||||||
let LucideIcon = (() => {
|
|
||||||
switch (props.icon) {
|
|
||||||
case "info":
|
|
||||||
return Icon.Info;
|
|
||||||
case "help":
|
|
||||||
return Icon.HelpCircle;
|
|
||||||
case "warn":
|
|
||||||
case "alert":
|
|
||||||
return Icon.AlertTriangle;
|
|
||||||
case "link":
|
|
||||||
return Icon.ExternalLink;
|
|
||||||
default:
|
|
||||||
return Icon.HelpCircle;
|
|
||||||
}
|
|
||||||
})() as Icon.LucideIcon;
|
|
||||||
|
|
||||||
const hint = (() => {
|
|
||||||
switch (props.hint) {
|
|
||||||
case undefined:
|
|
||||||
return t(
|
|
||||||
(() => {
|
|
||||||
if (!dialogAvailable) {
|
|
||||||
LucideIcon = Icon.ExternalLink;
|
|
||||||
}
|
|
||||||
switch (LucideIcon) {
|
|
||||||
case Icon.Info:
|
|
||||||
return "common.dialog.info";
|
|
||||||
case Icon.AlertTriangle:
|
|
||||||
return "common.dialog.warning";
|
|
||||||
case Icon.ExternalLink:
|
|
||||||
return "common.learn-more";
|
|
||||||
case Icon.HelpCircle:
|
|
||||||
default:
|
|
||||||
return "common.dialog.help";
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
);
|
|
||||||
case "":
|
|
||||||
case "none":
|
|
||||||
case "false":
|
|
||||||
case "disabled":
|
|
||||||
return undefined;
|
|
||||||
default:
|
|
||||||
return props.hint;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const sizePx = (() => {
|
|
||||||
switch (props.size) {
|
|
||||||
case "sm":
|
|
||||||
return 14;
|
|
||||||
case "lg":
|
|
||||||
return 18;
|
|
||||||
case "md":
|
|
||||||
default:
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (!dialogAvailable && !clickActionAvailable && !props.hint) {
|
|
||||||
return (
|
|
||||||
<IconButton className={className} color={color} variant={variant} size={iconButtonSize}>
|
|
||||||
<LucideIcon size={sizePx} />
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrapInTooltip = (element: JSX.Element) => {
|
|
||||||
if (!hint) {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Tooltip placement={hintPlacement} title={hint} color={color} variant={variant} size={props.size}>
|
|
||||||
{element}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (clickActionAvailable) {
|
|
||||||
props = { ...props, title: props.title ?? hint, hint: hint, color: color, variant: variant, icon: LucideIcon };
|
|
||||||
const clickAction = () => {
|
|
||||||
dialogAvailable ? showHelpDialog(props) : openUrl(props.url);
|
|
||||||
};
|
|
||||||
LucideIcon = dialogAvailable || onlyUrlAvailable ? LucideIcon : Icon.ExternalLink;
|
|
||||||
return wrapInTooltip(
|
|
||||||
<IconButton className={className} color={color} variant={variant} size={iconButtonSize} onClick={clickAction}>
|
|
||||||
<LucideIcon size={sizePx} />
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return wrapInTooltip(
|
|
||||||
<IconButton className={className} color={color} variant={variant} size={iconButtonSize}>
|
|
||||||
<LucideIcon size={sizePx} />
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HelpButton;
|
|
Loading…
Reference in New Issue