profile-more.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { useState } from "react";
  2. import { useTranslation } from "react-i18next";
  3. import { useLockFn } from "ahooks";
  4. import {
  5. Box,
  6. Badge,
  7. Chip,
  8. Typography,
  9. MenuItem,
  10. Menu,
  11. IconButton,
  12. } from "@mui/material";
  13. import { FeaturedPlayListRounded } from "@mui/icons-material";
  14. import { viewProfile } from "@/services/cmds";
  15. import { Notice } from "@/components/base";
  16. import { EditorViewer } from "@/components/profile/editor-viewer";
  17. import { ProfileBox } from "./profile-box";
  18. import { LogViewer } from "./log-viewer";
  19. interface Props {
  20. logInfo?: [string, string][];
  21. id: "Merge" | "Script";
  22. onChange?: (prev?: string, curr?: string) => void;
  23. }
  24. // profile enhanced item
  25. export const ProfileMore = (props: Props) => {
  26. const { id, logInfo = [], onChange } = props;
  27. const { t, i18n } = useTranslation();
  28. const [anchorEl, setAnchorEl] = useState<any>(null);
  29. const [position, setPosition] = useState({ left: 0, top: 0 });
  30. const [fileOpen, setFileOpen] = useState(false);
  31. const [logOpen, setLogOpen] = useState(false);
  32. const onEditFile = () => {
  33. setAnchorEl(null);
  34. setFileOpen(true);
  35. };
  36. const onOpenFile = useLockFn(async () => {
  37. setAnchorEl(null);
  38. try {
  39. await viewProfile(id);
  40. } catch (err: any) {
  41. Notice.error(err?.message || err.toString());
  42. }
  43. });
  44. const fnWrapper = (fn: () => void) => () => {
  45. setAnchorEl(null);
  46. return fn();
  47. };
  48. const hasError = !!logInfo.find((e) => e[0] === "exception");
  49. const itemMenu = [
  50. { label: "Edit File", handler: onEditFile },
  51. { label: "Open File", handler: onOpenFile },
  52. ];
  53. const boxStyle = {
  54. height: 26,
  55. display: "flex",
  56. alignItems: "center",
  57. justifyContent: "space-between",
  58. lineHeight: 1,
  59. };
  60. return (
  61. <>
  62. <ProfileBox
  63. onDoubleClick={onEditFile}
  64. onContextMenu={(event) => {
  65. const { clientX, clientY } = event;
  66. setPosition({ top: clientY, left: clientX });
  67. setAnchorEl(event.currentTarget);
  68. event.preventDefault();
  69. }}
  70. >
  71. <Box
  72. display="flex"
  73. justifyContent="space-between"
  74. alignItems="center"
  75. mb={0.5}
  76. >
  77. <Typography
  78. width="calc(100% - 52px)"
  79. variant="h6"
  80. component="h2"
  81. noWrap
  82. title={t(`Global ${id}`)}
  83. >
  84. {t(`Global ${id}`)}
  85. </Typography>
  86. <Chip
  87. label={id}
  88. color="primary"
  89. size="small"
  90. variant="outlined"
  91. sx={{ height: 20, textTransform: "capitalize" }}
  92. />
  93. </Box>
  94. <Box sx={boxStyle}>
  95. {id === "Script" &&
  96. (hasError ? (
  97. <Badge color="error" variant="dot" overlap="circular">
  98. <IconButton
  99. size="small"
  100. edge="start"
  101. color="error"
  102. title={t("Script Console")}
  103. onClick={() => setLogOpen(true)}
  104. >
  105. <FeaturedPlayListRounded fontSize="inherit" />
  106. </IconButton>
  107. </Badge>
  108. ) : (
  109. <IconButton
  110. size="small"
  111. edge="start"
  112. color="inherit"
  113. title={t("Script Console")}
  114. onClick={() => setLogOpen(true)}
  115. >
  116. <FeaturedPlayListRounded fontSize="inherit" />
  117. </IconButton>
  118. ))}
  119. </Box>
  120. </ProfileBox>
  121. <Menu
  122. open={!!anchorEl}
  123. anchorEl={anchorEl}
  124. onClose={() => setAnchorEl(null)}
  125. anchorPosition={position}
  126. anchorReference="anchorPosition"
  127. transitionDuration={225}
  128. MenuListProps={{ sx: { py: 0.5 } }}
  129. onContextMenu={(e) => {
  130. setAnchorEl(null);
  131. e.preventDefault();
  132. }}
  133. >
  134. {itemMenu
  135. .filter((item: any) => item.show !== false)
  136. .map((item) => (
  137. <MenuItem
  138. key={item.label}
  139. onClick={item.handler}
  140. sx={[
  141. { minWidth: 120 },
  142. (theme) => {
  143. return {
  144. color:
  145. item.label === "Delete"
  146. ? theme.palette.error.main
  147. : undefined,
  148. };
  149. },
  150. ]}
  151. dense
  152. >
  153. {t(item.label)}
  154. </MenuItem>
  155. ))}
  156. </Menu>
  157. <EditorViewer
  158. mode="profile"
  159. property={id}
  160. open={fileOpen}
  161. language={id === "Merge" ? "yaml" : "javascript"}
  162. schema={id === "Merge" ? "merge" : undefined}
  163. onChange={onChange}
  164. onClose={() => setFileOpen(false)}
  165. />
  166. <LogViewer
  167. open={logOpen}
  168. logInfo={logInfo}
  169. onClose={() => setLogOpen(false)}
  170. />
  171. </>
  172. );
  173. };