import dayjs from "dayjs"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useLockFn } from "ahooks"; import { alpha, Box, Chip, styled, Typography, MenuItem, Menu, } from "@mui/material"; import { CmdType } from "../../services/types"; import { viewProfile } from "../../services/cmds"; import enhance from "../../services/enhance"; import ProfileEdit from "./profile-edit"; import FileEditor from "./file-editor"; import Notice from "../base/base-notice"; const Wrapper = styled(Box)(({ theme }) => ({ width: "100%", display: "block", cursor: "pointer", textAlign: "left", borderRadius: theme.shape.borderRadius, boxShadow: theme.shadows[2], padding: "8px 16px", boxSizing: "border-box", })); interface Props { selected: boolean; itemData: CmdType.ProfileItem; enableNum: number; onEnable: () => void; onDisable: () => void; onMoveTop: () => void; onMoveEnd: () => void; onDelete: () => void; } // profile enhanced item const ProfileMore = (props: Props) => { const { selected, itemData, enableNum, onEnable, onDisable, onMoveTop, onMoveEnd, onDelete, } = props; const { uid, type } = itemData; const { t } = useTranslation(); const [anchorEl, setAnchorEl] = useState(null); const [position, setPosition] = useState({ left: 0, top: 0 }); const [editOpen, setEditOpen] = useState(false); const [fileOpen, setFileOpen] = useState(false); const [status, setStatus] = useState(enhance.status(uid)); // unlisten when unmount useEffect(() => enhance.listen(uid, setStatus), [uid]); // error during enhanced mode const hasError = selected && status?.status === "error"; const onEditInfo = () => { setAnchorEl(null); setEditOpen(true); }; const onEditFile = () => { setAnchorEl(null); setFileOpen(true); }; const onOpenFile = useLockFn(async () => { setAnchorEl(null); try { await viewProfile(itemData.uid); } catch (err: any) { Notice.error(err?.message || err.toString()); } }); const fnWrapper = (fn: () => void) => () => { setAnchorEl(null); return fn(); }; const showMove = enableNum > 1 && !hasError; const enableMenu = [ { label: "Disable", handler: fnWrapper(onDisable) }, { label: "Edit Info", handler: onEditInfo }, { label: "Edit File", handler: onEditFile }, { label: "Open File", handler: onOpenFile }, { label: "To Top", show: showMove, handler: fnWrapper(onMoveTop) }, { label: "To End", show: showMove, handler: fnWrapper(onMoveEnd) }, { label: "Delete", handler: fnWrapper(onDelete) }, ]; const disableMenu = [ { label: "Enable", handler: fnWrapper(onEnable) }, { label: "Edit Info", handler: onEditInfo }, { label: "Edit File", handler: onEditFile }, { label: "Open File", handler: onOpenFile }, { label: "Delete", handler: fnWrapper(onDelete) }, ]; const boxStyle = { height: 26, display: "flex", alignItems: "center", justifyContent: "space-between", lineHeight: 1, }; return ( <> { // todo // 区分 selected 和 error 和 mode 下各种颜色的排列组合 const { mode, primary, text, grey, error } = palette; const key = `${mode}-${selected}`; const bgkey = hasError ? `${mode}-err` : key; const bgcolor = { "light-true": alpha(primary.main, 0.15), "light-false": palette.background.paper, "dark-true": alpha(primary.main, 0.35), "dark-false": alpha(grey[700], 0.35), "light-err": alpha(error.main, 0.12), "dark-err": alpha(error.main, 0.3), }[bgkey]!; const color = { "light-true": text.secondary, "light-false": text.secondary, "dark-true": alpha(text.secondary, 0.6), "dark-false": alpha(text.secondary, 0.6), }[key]!; const h2color = { "light-true": primary.main, "light-false": text.primary, "dark-true": primary.light, "dark-false": text.primary, }[key]!; return { bgcolor, color, "& h2": { color: h2color } }; }} // onClick={() => onSelect(false)} onContextMenu={(event) => { const { clientX, clientY } = event; setPosition({ top: clientY, left: clientX }); setAnchorEl(event.currentTarget); event.preventDefault(); }} > {itemData.name} {hasError ? ( {status.message} ) : ( {itemData.desc} )} {parseExpire(itemData.updated)} setAnchorEl(null)} anchorPosition={position} anchorReference="anchorPosition" transitionDuration={225} onContextMenu={(e) => { setAnchorEl(null); e.preventDefault(); }} > {(selected ? enableMenu : disableMenu) .filter((item: any) => item.show !== false) .map((item) => ( {t(item.label)} ))} {editOpen && ( setEditOpen(false)} /> )} {fileOpen && ( setFileOpen(false)} /> )} ); }; function parseExpire(expire?: number) { if (!expire) return "-"; return dayjs(expire * 1000).format("YYYY-MM-DD"); } export default ProfileMore;