2024-10-22 08:01:21 +00:00
|
|
|
import React from 'react';
|
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
|
import { useResolveStore } from '@/_stores/resolverStore';
|
|
|
|
|
import { shallow } from 'zustand/shallow';
|
|
|
|
|
import './styles.scss';
|
|
|
|
|
import SingleLineCodeEditor from './SingleLineCodeEditor';
|
|
|
|
|
import MultiLineCodeEditor from './MultiLineCodeEditor';
|
|
|
|
|
import usePortal from '@/_hooks/use-portal';
|
|
|
|
|
import Tooltip from 'react-bootstrap/Tooltip';
|
|
|
|
|
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|
|
|
|
import { isNumber } from 'lodash';
|
|
|
|
|
import { Alert } from '@/_ui/Alert/Alert';
|
2024-11-29 10:43:24 +00:00
|
|
|
import TJDBCodeEditor from './TJDBHinter';
|
2024-10-22 08:01:21 +00:00
|
|
|
|
|
|
|
|
const CODE_EDITOR_TYPE = {
|
|
|
|
|
fxEditor: SingleLineCodeEditor.EditorBridge,
|
|
|
|
|
basic: SingleLineCodeEditor,
|
|
|
|
|
multiline: MultiLineCodeEditor,
|
|
|
|
|
extendedSingleLine: SingleLineCodeEditor,
|
2024-11-29 10:43:24 +00:00
|
|
|
tjdbHinter: TJDBCodeEditor,
|
2024-10-22 08:01:21 +00:00
|
|
|
};
|
|
|
|
|
|
2025-06-27 14:38:15 +00:00
|
|
|
const CodeHinter = ({
|
|
|
|
|
type = 'basic',
|
|
|
|
|
initialValue,
|
|
|
|
|
componentName,
|
|
|
|
|
disabled,
|
|
|
|
|
renderCopilot,
|
|
|
|
|
setCodeEditorView,
|
2025-07-11 06:45:39 +00:00
|
|
|
helpText,
|
2025-06-27 14:38:15 +00:00
|
|
|
...restProps
|
|
|
|
|
}) => {
|
2024-10-22 08:01:21 +00:00
|
|
|
const darkMode = localStorage.getItem('darkMode') === 'true';
|
|
|
|
|
|
|
|
|
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
const handleTogglePopupExapand = () => {
|
|
|
|
|
const changeOpen = (newOpen) => {
|
|
|
|
|
setIsOpen(newOpen);
|
|
|
|
|
if (typeof restProps?.popOverCallback === 'function') restProps?.popOverCallback(newOpen);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!isOpen) {
|
|
|
|
|
changeOpen(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
const element = document.getElementsByClassName('portal-container');
|
|
|
|
|
if (element) {
|
|
|
|
|
const checkPortalExits = element[0]?.classList.contains(componentName);
|
|
|
|
|
|
|
|
|
|
if (checkPortalExits === false) {
|
|
|
|
|
const parent = element[0].parentNode;
|
|
|
|
|
parent.removeChild(element[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
changeOpen(false);
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
}).then(() => {
|
|
|
|
|
changeOpen(true);
|
|
|
|
|
forceUpdate();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
|
|
|
|
|
|
|
|
|
|
const RenderCodeEditor = CODE_EDITOR_TYPE[type];
|
|
|
|
|
|
|
|
|
|
return (
|
2025-07-11 06:45:39 +00:00
|
|
|
<>
|
|
|
|
|
<RenderCodeEditor
|
|
|
|
|
renderCopilot={renderCopilot}
|
|
|
|
|
type={type}
|
|
|
|
|
initialValue={initialValue}
|
|
|
|
|
darkMode={darkMode}
|
|
|
|
|
portalProps={{
|
|
|
|
|
isOpen,
|
|
|
|
|
setIsOpen,
|
|
|
|
|
handleTogglePopupExapand,
|
|
|
|
|
forceUpdate,
|
|
|
|
|
}}
|
|
|
|
|
componentName={componentName}
|
|
|
|
|
disabled={disabled}
|
|
|
|
|
setCodeEditorView={setCodeEditorView}
|
|
|
|
|
{...restProps}
|
|
|
|
|
/>
|
|
|
|
|
{helpText && <span className="codehinter-helper-text">{helpText}</span>}
|
|
|
|
|
</>
|
2024-10-22 08:01:21 +00:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Portal = ({ children, ...restProps }) => {
|
|
|
|
|
const renderPortal = usePortal({ children, ...restProps });
|
|
|
|
|
|
|
|
|
|
return <React.Fragment>{renderPortal}</React.Fragment>;
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-25 06:52:50 +00:00
|
|
|
const PopupIcon = ({ callback, icon, tip, position, isMultiEditor = false, isQueryManager = false }) => {
|
2024-10-22 08:01:21 +00:00
|
|
|
const size = 16;
|
|
|
|
|
const topRef = isNumber(position?.height) ? Math.floor(position?.height) - 30 : 32;
|
2025-10-03 05:21:18 +00:00
|
|
|
let top = topRef > 32 ? topRef : 0;
|
2025-02-25 06:52:50 +00:00
|
|
|
// for query manager we allow the height of query manager to be dynamic, so we need to render the popup icon at the bottom of code editor
|
|
|
|
|
const renderAtBottom = isQueryManager && (isMultiEditor || topRef > 32);
|
|
|
|
|
|
2024-10-22 08:01:21 +00:00
|
|
|
return (
|
2025-02-25 06:52:50 +00:00
|
|
|
<div
|
|
|
|
|
className="d-flex justify-content-end w-100 position-absolute codehinter-popup-icon"
|
|
|
|
|
style={renderAtBottom ? { bottom: '30px' } : { top: top }}
|
|
|
|
|
>
|
2024-10-22 08:01:21 +00:00
|
|
|
<OverlayTrigger
|
|
|
|
|
trigger={['hover', 'focus']}
|
|
|
|
|
placement="top"
|
|
|
|
|
delay={{ show: 800, hide: 100 }}
|
|
|
|
|
overlay={<Tooltip id="button-tooltip">{tip}</Tooltip>}
|
|
|
|
|
>
|
|
|
|
|
<img
|
2024-11-29 10:43:24 +00:00
|
|
|
style={{ zIndex: 10000 }}
|
2024-10-22 08:01:21 +00:00
|
|
|
className="svg-icon m-2 popup-btn"
|
|
|
|
|
src={`assets/images/icons/${icon}.svg`}
|
|
|
|
|
width={size}
|
|
|
|
|
height={size}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
callback();
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</OverlayTrigger>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const DepericatedAlertForWorkspaceVariable = ({ text }) => {
|
|
|
|
|
return (
|
|
|
|
|
<Alert
|
|
|
|
|
svg="tj-info-warning"
|
|
|
|
|
cls="codehinter workspace-variables-alert-banner p-1 mb-0 mt-2"
|
|
|
|
|
data-cy={``}
|
|
|
|
|
imgHeight={18}
|
|
|
|
|
imgWidth={18}
|
|
|
|
|
>
|
|
|
|
|
<div className="d-flex align-items-center">
|
|
|
|
|
<div class="">{text}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Alert>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CodeHinter.Portal = Portal;
|
|
|
|
|
CodeHinter.PopupIcon = PopupIcon;
|
|
|
|
|
CodeHinter.DepericatedAlert = DepericatedAlertForWorkspaceVariable;
|
|
|
|
|
|
|
|
|
|
CodeHinter.propTypes = {
|
|
|
|
|
type: PropTypes.string.isRequired,
|
|
|
|
|
disabled: PropTypes.bool,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default CodeHinter;
|