feat(Builder): Limit Output preview size (#7554)
* feat(Builder): Limit Output preview size * fix indentpull/7564/head^2
parent
77034f2df0
commit
03ea51b266
|
@ -1,8 +1,9 @@
|
||||||
import React, { useState, useEffect, FC, memo } from 'react';
|
import React, { useState, useEffect, FC, memo, useRef } from 'react';
|
||||||
import { NodeProps } from 'reactflow';
|
import { NodeProps } from 'reactflow';
|
||||||
import 'reactflow/dist/style.css';
|
import 'reactflow/dist/style.css';
|
||||||
import './customnode.css';
|
import './customnode.css';
|
||||||
import ModalComponent from './ModalComponent';
|
import InputModalComponent from './InputModalComponent';
|
||||||
|
import OutputModalComponent from './OutputModalComponent';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import { Input } from './ui/input';
|
import { Input } from './ui/input';
|
||||||
import { BlockSchema } from '@/lib/types';
|
import { BlockSchema } from '@/lib/types';
|
||||||
|
@ -33,6 +34,8 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
|
||||||
const [activeKey, setActiveKey] = useState<string | null>(null);
|
const [activeKey, setActiveKey] = useState<string | null>(null);
|
||||||
const [modalValue, setModalValue] = useState<string>('');
|
const [modalValue, setModalValue] = useState<string>('');
|
||||||
const [errors, setErrors] = useState<{ [key: string]: string | null }>({});
|
const [errors, setErrors] = useState<{ [key: string]: string | null }>({});
|
||||||
|
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
|
||||||
|
const outputDataRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data.output_data || data.status) {
|
if (data.output_data || data.status) {
|
||||||
|
@ -400,6 +403,16 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
|
||||||
return Object.values(newErrors).every((error) => error === null);
|
return Object.values(newErrors).every((error) => error === null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOutputClick = () => {
|
||||||
|
setIsOutputModalOpen(true);
|
||||||
|
setModalValue(typeof data.output_data === 'object' ? JSON.stringify(data.output_data, null, 2) : data.output_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isTextTruncated = (element: HTMLElement | null): boolean => {
|
||||||
|
if (!element) return false;
|
||||||
|
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`custom-node dark-theme ${data.status === 'RUNNING' ? 'running' : data.status === 'COMPLETED' ? 'completed' : data.status === 'FAILED' ? 'failed' : ''}`}>
|
<div className={`custom-node dark-theme ${data.status === 'RUNNING' ? 'running' : data.status === 'COMPLETED' ? 'completed' : data.status === 'FAILED' ? 'failed' : ''}`}>
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
|
@ -423,16 +436,24 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isOutputOpen && (
|
{isOutputOpen && (
|
||||||
<div className="node-output">
|
<div className="node-output" onClick={handleOutputClick}>
|
||||||
<p>
|
<p>
|
||||||
<strong>Status:</strong>{' '}
|
<strong>Status:</strong>{' '}
|
||||||
{typeof data.status === 'object' ? JSON.stringify(data.status) : data.status || 'N/A'}
|
{typeof data.status === 'object' ? JSON.stringify(data.status) : data.status || 'N/A'}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Output Data:</strong>{' '}
|
<strong>Output Data:</strong>{' '}
|
||||||
{typeof data.output_data === 'object'
|
{(() => {
|
||||||
|
const outputText = typeof data.output_data === 'object'
|
||||||
? JSON.stringify(data.output_data)
|
? JSON.stringify(data.output_data)
|
||||||
: data.output_data || 'N/A'}
|
: data.output_data;
|
||||||
|
|
||||||
|
if (!outputText) return 'No output data';
|
||||||
|
|
||||||
|
return outputText.length > 100
|
||||||
|
? `${outputText.slice(0, 100)}... Press To Read More`
|
||||||
|
: outputText;
|
||||||
|
})()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -446,13 +467,18 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ModalComponent
|
<InputModalComponent
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleModalSave}
|
onSave={handleModalSave}
|
||||||
value={modalValue}
|
value={modalValue}
|
||||||
key={activeKey}
|
key={activeKey}
|
||||||
/>
|
/>
|
||||||
|
<OutputModalComponent
|
||||||
|
isOpen={isOutputModalOpen}
|
||||||
|
onClose={() => setIsOutputModalOpen(false)}
|
||||||
|
value={modalValue}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@ interface ModalProps {
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => {
|
const InputModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => {
|
||||||
const [tempValue, setTempValue] = React.useState(value);
|
const [tempValue, setTempValue] = React.useState(value);
|
||||||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
@ -50,4 +50,4 @@ const ModalComponent: FC<ModalProps> = ({ isOpen, onClose, onSave, value }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ModalComponent;
|
export default InputModalComponent;
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { FC, useEffect } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
import { Button } from './ui/button';
|
||||||
|
import { Textarea } from './ui/textarea';
|
||||||
|
|
||||||
|
interface OutputModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutputModalComponent: FC<OutputModalProps> = ({ isOpen, onClose, value }) => {
|
||||||
|
const [tempValue, setTempValue] = React.useState(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
setTempValue(value);
|
||||||
|
}
|
||||||
|
}, [isOpen, value]);
|
||||||
|
|
||||||
|
if (!isOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className="fixed inset-0 bg-white bg-opacity-60 flex justify-center items-center z-50">
|
||||||
|
<div className="bg-white p-5 rounded-lg w-[1000px] max-w-[100%]">
|
||||||
|
<center><h1 style={{ color: 'black' }}>Full Output</h1></center>
|
||||||
|
<Textarea
|
||||||
|
className="w-full h-[400px] p-2.5 rounded border border-[#dfdfdf] text-black bg-[#dfdfdf]"
|
||||||
|
value={tempValue}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
<div className="flex justify-end gap-2.5 mt-2.5">
|
||||||
|
<Button onClick={onClose}>Close</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OutputModalComponent;
|
Loading…
Reference in New Issue