mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
fork refine
This commit is contained in:
623
packages/inferencer/src/components/shared-code-viewer/index.tsx
Normal file
623
packages/inferencer/src/components/shared-code-viewer/index.tsx
Normal file
@@ -0,0 +1,623 @@
|
||||
import React, { SVGProps } from "react";
|
||||
import Highlight, { defaultProps } from "prism-react-renderer";
|
||||
import theme from "prism-react-renderer/themes/vsDark";
|
||||
|
||||
import { CreateInferencerConfig } from "../../types";
|
||||
import { prettierFormat } from "../../utilities";
|
||||
|
||||
export const SharedCodeViewer: CreateInferencerConfig["codeViewerComponent"] =
|
||||
({ code: rawCode, loading }) => {
|
||||
const code = React.useMemo(() => {
|
||||
return prettierFormat(rawCode ?? "");
|
||||
}, [rawCode]);
|
||||
|
||||
const [settled, setSettled] = React.useState(false);
|
||||
const [isModalVisible, setIsModalVisible] = React.useState(false);
|
||||
const [isVisible, setIsVisible] = React.useState(false);
|
||||
const [isColumn, setIsColumn] = React.useState(false);
|
||||
const [isModalButtonHover, setIsModalButtonHover] =
|
||||
React.useState(false);
|
||||
|
||||
// Settled Check
|
||||
React.useEffect(() => {
|
||||
if (!loading) {
|
||||
const timeout = setTimeout(() => {
|
||||
setSettled(true);
|
||||
}, 300);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}, [loading]);
|
||||
|
||||
// Visibility Check
|
||||
React.useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
const mediaQuery = window.matchMedia("(max-width: 449px)");
|
||||
if (mediaQuery.matches) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
if (mediaQuery.matches) {
|
||||
setIsVisible(false);
|
||||
} else {
|
||||
setIsVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}, []);
|
||||
|
||||
// Flex Direction Check
|
||||
React.useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
const mediaQuery = window.matchMedia("(max-width: 1280px)");
|
||||
if (mediaQuery.matches) {
|
||||
setIsColumn(true);
|
||||
} else {
|
||||
setIsColumn(false);
|
||||
}
|
||||
|
||||
const handleResize = () => {
|
||||
if (mediaQuery.matches) {
|
||||
setIsColumn(true);
|
||||
} else {
|
||||
setIsColumn(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}, []);
|
||||
|
||||
if (code && !loading) {
|
||||
return (
|
||||
<>
|
||||
{isVisible && (
|
||||
<div
|
||||
className="refine-inferencer--code-viewer"
|
||||
style={{
|
||||
position: "sticky",
|
||||
bottom: "24px",
|
||||
paddingTop: "24px",
|
||||
left: 0,
|
||||
right: 0,
|
||||
width: "100%",
|
||||
zIndex: 10,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
transition: "all 0.2s ease",
|
||||
opacity: settled && !isModalVisible ? 1 : 0,
|
||||
transform:
|
||||
settled && !isModalVisible
|
||||
? "translateY(0)"
|
||||
: "translateY(100px)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "calc(100% - calc(64px * 2))",
|
||||
maxWidth: "1080px",
|
||||
padding: "20px",
|
||||
backgroundColor: "#1A1A1A",
|
||||
boxShadow:
|
||||
"0px 4px 16px -4px rgba(0, 0, 0, 0.5), 0px 8px 32px -8px rgba(0, 0, 0, 0.35)",
|
||||
borderRadius: "16px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isColumn ? "column" : "row",
|
||||
gap: "12px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
gap: "8px",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<InfoIcon />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
lineHeight: "20px",
|
||||
color: "#ffffff",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
Most of the page code is
|
||||
auto-generated by the{" "}
|
||||
<span
|
||||
style={{
|
||||
textDecoration: "underline",
|
||||
}}
|
||||
>
|
||||
Inferencer
|
||||
</span>{" "}
|
||||
feature, based on your backend data
|
||||
structure.
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
While this is an excellent way to
|
||||
experiment with refine,{" "}
|
||||
<span style={{ fontWeight: 600 }}>
|
||||
it's not intended to be
|
||||
used on production.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
onPointerEnter={() =>
|
||||
setIsModalButtonHover(true)
|
||||
}
|
||||
onPointerLeave={() =>
|
||||
setIsModalButtonHover(false)
|
||||
}
|
||||
onClick={() => setIsModalVisible(true)}
|
||||
style={{
|
||||
appearance: "none",
|
||||
border: "none",
|
||||
padding: "10px 16px",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#0080FF",
|
||||
color: "#ffffff",
|
||||
fontSize: "14px",
|
||||
lineHeight: "20px",
|
||||
fontWeight: 600,
|
||||
gap: "8px",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s ease",
|
||||
transform: isModalButtonHover
|
||||
? "scale(1.025)"
|
||||
: undefined,
|
||||
filter: isModalButtonHover
|
||||
? "brightness(1.1)"
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
<OpenIcon
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<span>
|
||||
Show the auto-generated code
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<CodeModal
|
||||
visible={isModalVisible}
|
||||
onClose={() => setIsModalVisible(false)}
|
||||
code={code}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const CodeModal = ({
|
||||
visible,
|
||||
onClose,
|
||||
code = "",
|
||||
}: {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
code?: string;
|
||||
}) => {
|
||||
const modalRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isCopied, setIsCopied] = React.useState(false);
|
||||
const [isCopyHover, setIsCopyHover] = React.useState(false);
|
||||
const [isCloseHover, setIsCloseHover] = React.useState(false);
|
||||
const [isLearnMoreHover, setIsLearnMoreHover] = React.useState(false);
|
||||
|
||||
// On Outside Click
|
||||
React.useEffect(() => {
|
||||
if (typeof document !== "undefined") {
|
||||
const onOutsideClick = (event: PointerEvent) => {
|
||||
if (
|
||||
modalRef.current &&
|
||||
!modalRef.current.contains(event.target as Node)
|
||||
) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("pointerdown", onOutsideClick);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("pointerdown", onOutsideClick);
|
||||
};
|
||||
}
|
||||
|
||||
return () => undefined;
|
||||
}, [onClose]);
|
||||
|
||||
// onCopy Handler
|
||||
const onCopy = () => {
|
||||
if (typeof navigator !== "undefined") {
|
||||
navigator.clipboard.writeText(code);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const title = (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 700,
|
||||
fontSize: "20px",
|
||||
lineHeight: "32px",
|
||||
color: "#0D0D0D",
|
||||
}}
|
||||
>
|
||||
Auto-generated code by Inferencer
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
onPointerEnter={() => setIsCloseHover(true)}
|
||||
onPointerLeave={() => setIsCloseHover(false)}
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
appearance: "none",
|
||||
border: "none",
|
||||
background: "none",
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
outline: "none",
|
||||
borderRadius: "50px",
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.15s ease",
|
||||
transform: isCloseHover ? "scale(1.05)" : undefined,
|
||||
filter: isCloseHover ? "brightness(0.8)" : undefined,
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const highlight = (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
borderRadius: "8px",
|
||||
flex: "1",
|
||||
overflow: "scroll",
|
||||
background: "#1E1E1E",
|
||||
}}
|
||||
>
|
||||
<Highlight
|
||||
{...defaultProps}
|
||||
theme={theme}
|
||||
code={code}
|
||||
language="tsx"
|
||||
>
|
||||
{({
|
||||
className,
|
||||
style,
|
||||
tokens,
|
||||
getLineProps,
|
||||
getTokenProps,
|
||||
}) => (
|
||||
<pre
|
||||
className={className}
|
||||
style={{
|
||||
...style,
|
||||
padding: "14px 14px 14px 14px",
|
||||
margin: "0",
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
{tokens.map((line, i) => (
|
||||
<div
|
||||
key={i}
|
||||
{...getLineProps({
|
||||
line,
|
||||
key: i,
|
||||
})}
|
||||
>
|
||||
{line.map((token, key) => (
|
||||
<span
|
||||
key={key}
|
||||
{...getTokenProps({
|
||||
token,
|
||||
key,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
</div>
|
||||
);
|
||||
|
||||
const buttons = (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "flex-end",
|
||||
gap: "16px",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onPointerEnter={() => setIsCopyHover(true)}
|
||||
onPointerLeave={() => setIsCopyHover(false)}
|
||||
onClick={onCopy}
|
||||
style={{
|
||||
appearance: "none",
|
||||
height: "40px",
|
||||
border: "none",
|
||||
padding: "10px 16px",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#0080FF",
|
||||
color: "#ffffff",
|
||||
fontSize: "14px",
|
||||
lineHeight: "20px",
|
||||
fontWeight: 600,
|
||||
gap: "8px",
|
||||
cursor: "pointer",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
transition: "filter 0.2s ease",
|
||||
transform: isCopyHover ? "scale(1.025)" : "scale(1)",
|
||||
filter: isCopyHover ? "brightness(1.1)" : undefined,
|
||||
}}
|
||||
>
|
||||
<ClipboardIcon
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
marginTop: "-2px",
|
||||
marginBottom: "-2px",
|
||||
}}
|
||||
/>
|
||||
<span>Copy Generated Code</span>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
top: 0,
|
||||
left: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#0080FF",
|
||||
transition: "all 0.2s ease",
|
||||
transform: isCopied
|
||||
? "translateY(0)"
|
||||
: "translateY(40px)",
|
||||
}}
|
||||
>
|
||||
<ClipboardIcon
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
marginTop: "-2px",
|
||||
marginBottom: "-2px",
|
||||
}}
|
||||
/>
|
||||
<span>Copied!</span>
|
||||
</div>
|
||||
</button>
|
||||
<a
|
||||
onPointerEnter={() => setIsLearnMoreHover(true)}
|
||||
onPointerLeave={() => setIsLearnMoreHover(false)}
|
||||
href="https://refine.dev/docs/packages/documentation/inferencer"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
style={{
|
||||
appearance: "none",
|
||||
textDecoration: "none",
|
||||
border: "none",
|
||||
padding: "10px 16px",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "rgba(0, 128, 255, 0.1)",
|
||||
color: "#0080FF",
|
||||
fontSize: "14px",
|
||||
lineHeight: "20px",
|
||||
fontWeight: 600,
|
||||
gap: "8px",
|
||||
cursor: "pointer",
|
||||
transition: "all 0.2s ease",
|
||||
transform: isLearnMoreHover ? "scale(1.025)" : undefined,
|
||||
filter: isLearnMoreHover ? "brightness(1.1)" : undefined,
|
||||
}}
|
||||
>
|
||||
<OpenIcon
|
||||
style={{
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<span>Learn more about inferencer</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="refine-inferencer--code-viewer-modal"
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
zIndex: 9999,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
transition: "all 0.2s ease",
|
||||
opacity: visible ? 1 : 0,
|
||||
pointerEvents: visible ? "all" : "none",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={modalRef}
|
||||
style={{
|
||||
transform: visible
|
||||
? "scale(1) translateY(0px)"
|
||||
: "scale(0) translateY(-200px)",
|
||||
transition: "all 0.25s cubic-bezier(.35,1.29,.81,1.08)",
|
||||
transitionDelay: "0.25",
|
||||
width: "calc(100% - calc(32px * 2))",
|
||||
height: "calc(100% - calc(32px * 2))",
|
||||
backgroundColor: "#fff",
|
||||
maxWidth: "640px",
|
||||
maxHeight: "720px",
|
||||
borderRadius: "8px",
|
||||
padding: "16px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
gap: "16px",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
{highlight}
|
||||
{buttons}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const InfoIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={20}
|
||||
height={20}
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#0080FF"
|
||||
fillRule="evenodd"
|
||||
d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10Zm0-15a1.25 1.25 0 1 0 0 2.5A1.25 1.25 0 0 0 10 5Zm0 10c.69 0 1.25-.56 1.25-1.25V10a1.25 1.25 0 1 0-2.5 0v3.75c0 .69.56 1.25 1.25 1.25Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const OpenIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={16}
|
||||
height={16}
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 2a1 1 0 0 1 0 2H4v8h8v-1a1 1 0 1 1 2 0v1a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h1Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M9 2a1 1 0 0 0 0 2h1.586L6.293 8.293a1 1 0 0 0 1.414 1.414L12 5.414V7a1 1 0 1 0 2 0V3a1 1 0 0 0-1-1H9Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CloseIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={32}
|
||||
height={32}
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="#A6A6A6"
|
||||
fillRule="evenodd"
|
||||
d="M16 32C7.163 32 0 24.837 0 16S7.163 0 16 0s16 7.163 16 16-7.163 16-16 16Zm-2.586-21.414a2 2 0 1 0-2.828 2.828L13.172 16l-2.586 2.586a2 2 0 1 0 2.828 2.828L16 18.828l2.586 2.586a2 2 0 1 0 2.828-2.828L18.828 16l2.586-2.586a2 2 0 1 0-2.828-2.828L16 13.172l-2.586-2.586Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ClipboardIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={20}
|
||||
height={20}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M8 5a3 3 0 0 0 3 3h2a3 3 0 0 0 3-3h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h1Zm0 7a1 1 0 0 1 1-1h6a1 1 0 1 1 0 2H9a1 1 0 0 1-1-1Zm1 3a1 1 0 1 0 0 2h4a1 1 0 1 0 0-2H9Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path fill="currentColor" d="M13 4a1 1 0 1 1 0 2h-2a1 1 0 1 1 0-2h2Z" />
|
||||
</svg>
|
||||
);
|
||||
Reference in New Issue
Block a user