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