|
import { createPortal } from 'react-dom' |
|
import { useNavigate } from 'react-router-dom' |
|
import { useState, useEffect } from 'react' |
|
import { useDispatch } from 'react-redux' |
|
import PropTypes from 'prop-types' |
|
|
|
import { Tabs, Tab, Dialog, DialogContent, DialogTitle, Box } from '@mui/material' |
|
import { CopyBlock, atomOneDark } from 'react-code-blocks' |
|
|
|
|
|
import { Dropdown } from 'ui-component/dropdown/Dropdown' |
|
|
|
|
|
import { baseURL } from 'store/constant' |
|
import { SET_CHATFLOW } from 'store/actions' |
|
|
|
|
|
import pythonSVG from 'assets/images/python.svg' |
|
import javascriptSVG from 'assets/images/javascript.svg' |
|
import cURLSVG from 'assets/images/cURL.svg' |
|
import EmbedSVG from 'assets/images/embed.svg' |
|
|
|
|
|
import apiKeyApi from 'api/apikey' |
|
import chatflowsApi from 'api/chatflows' |
|
import configApi from 'api/config' |
|
|
|
|
|
import useApi from 'hooks/useApi' |
|
import { CheckboxInput } from 'ui-component/checkbox/Checkbox' |
|
import { TableViewOnly } from 'ui-component/table/Table' |
|
|
|
function TabPanel(props) { |
|
const { children, value, index, ...other } = props |
|
return ( |
|
<div |
|
role='tabpanel' |
|
hidden={value !== index} |
|
id={`attachment-tabpanel-${index}`} |
|
aria-labelledby={`attachment-tab-${index}`} |
|
{...other} |
|
> |
|
{value === index && <Box sx={{ p: 1 }}>{children}</Box>} |
|
</div> |
|
) |
|
} |
|
|
|
TabPanel.propTypes = { |
|
children: PropTypes.node, |
|
index: PropTypes.number.isRequired, |
|
value: PropTypes.number.isRequired |
|
} |
|
|
|
function a11yProps(index) { |
|
return { |
|
id: `attachment-tab-${index}`, |
|
'aria-controls': `attachment-tabpanel-${index}` |
|
} |
|
} |
|
|
|
const unshiftFiles = (configData) => { |
|
const filesConfig = configData.find((config) => config.name === 'files') |
|
if (filesConfig) { |
|
configData = configData.filter((config) => config.name !== 'files') |
|
configData.unshift(filesConfig) |
|
} |
|
return configData |
|
} |
|
|
|
const getConfigExamplesForJS = (configData, bodyType) => { |
|
let finalStr = '' |
|
configData = unshiftFiles(configData) |
|
const loop = Math.min(configData.length, 4) |
|
for (let i = 0; i < loop; i += 1) { |
|
const config = configData[i] |
|
let exampleVal = `"example"` |
|
if (config.type === 'string') exampleVal = `"example"` |
|
else if (config.type === 'boolean') exampleVal = `true` |
|
else if (config.type === 'number') exampleVal = `1` |
|
else if (config.name === 'files') exampleVal = `input.files[0]` |
|
finalStr += bodyType === 'json' ? `\n "${config.name}": ${exampleVal},` : `formData.append("${config.name}", ${exampleVal})\n` |
|
if (i === loop - 1 && bodyType !== 'json') `formData.append("question", "Hey, how are you?")\n` |
|
} |
|
return finalStr |
|
} |
|
|
|
const getConfigExamplesForPython = (configData, bodyType) => { |
|
let finalStr = '' |
|
configData = unshiftFiles(configData) |
|
const loop = Math.min(configData.length, 4) |
|
for (let i = 0; i < loop; i += 1) { |
|
const config = configData[i] |
|
let exampleVal = `"example"` |
|
if (config.type === 'string') exampleVal = `"example"` |
|
else if (config.type === 'boolean') exampleVal = `true` |
|
else if (config.type === 'number') exampleVal = `1` |
|
else if (config.name === 'files') exampleVal = `('example${config.type}', open('example${config.type}', 'rb'))` |
|
finalStr += bodyType === 'json' ? `\n "${config.name}": ${exampleVal},` : `\n "${config.name}": ${exampleVal},` |
|
if (i === loop - 1 && bodyType !== 'json') finalStr += `\n "question": "Hey, how are you?"\n` |
|
} |
|
return finalStr |
|
} |
|
|
|
const getConfigExamplesForCurl = (configData, bodyType) => { |
|
let finalStr = '' |
|
configData = unshiftFiles(configData) |
|
const loop = Math.min(configData.length, 4) |
|
for (let i = 0; i < loop; i += 1) { |
|
const config = configData[i] |
|
let exampleVal = `example` |
|
if (config.type === 'string') exampleVal = bodyType === 'json' ? `"example"` : `example` |
|
else if (config.type === 'boolean') exampleVal = `true` |
|
else if (config.type === 'number') exampleVal = `1` |
|
else if (config.name === 'files') exampleVal = `@/home/user1/Desktop/example${config.type}` |
|
finalStr += bodyType === 'json' ? `"${config.name}": ${exampleVal}` : `\n -F "${config.name}=${exampleVal}"` |
|
if (i === loop - 1) finalStr += bodyType === 'json' ? ` }` : ` \\\n -F "question=Hey, how are you?"` |
|
else finalStr += bodyType === 'json' ? `, ` : ` \\` |
|
} |
|
return finalStr |
|
} |
|
|
|
const embedCode = (chatflowid) => { |
|
return `<script type="module"> |
|
import Chatbot from "https://cdn.jsdelivr.net/npm/flowise-embed/dist/web.js" |
|
Chatbot.init({ |
|
chatflowid: "${chatflowid}", |
|
apiHost: "${baseURL}", |
|
}) |
|
</script>` |
|
} |
|
|
|
const embedCodeCustomization = (chatflowid) => { |
|
return `<script type="module"> |
|
import Chatbot from "https://cdn.jsdelivr.net/npm/flowise-embed/dist/web.js" |
|
Chatbot.init({ |
|
chatflowid: "${chatflowid}", |
|
apiHost: "${baseURL}", |
|
theme: { |
|
button: { |
|
backgroundColor: "#3B81F6", |
|
right: 20, |
|
bottom: 20, |
|
size: "medium", |
|
iconColor: "white", |
|
customIconSrc: "https://raw.githubusercontent.com/walkxcode/dashboard-icons/main/svg/google-messages.svg", |
|
}, |
|
chatWindow: { |
|
welcomeMessage: "Hello! This is custom welcome message", |
|
backgroundColor: "#ffffff", |
|
height: 700, |
|
width: 400, |
|
poweredByTextColor: "#303235", |
|
botMessage: { |
|
backgroundColor: "#f7f8ff", |
|
textColor: "#303235", |
|
showAvatar: true, |
|
avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png", |
|
}, |
|
userMessage: { |
|
backgroundColor: "#3B81F6", |
|
textColor: "#ffffff", |
|
showAvatar: true, |
|
avatarSrc: "https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/usericon.png", |
|
}, |
|
textInput: { |
|
placeholder: "Type your question", |
|
backgroundColor: "#ffffff", |
|
textColor: "#303235", |
|
sendButtonColor: "#3B81F6", |
|
} |
|
} |
|
} |
|
}) |
|
</script>` |
|
} |
|
|
|
const APICodeDialog = ({ show, dialogProps, onCancel }) => { |
|
const portalElement = document.getElementById('portal') |
|
const navigate = useNavigate() |
|
const dispatch = useDispatch() |
|
const codes = ['Embed', 'Python', 'JavaScript', 'cURL'] |
|
const [value, setValue] = useState(0) |
|
const [keyOptions, setKeyOptions] = useState([]) |
|
const [apiKeys, setAPIKeys] = useState([]) |
|
const [chatflowApiKeyId, setChatflowApiKeyId] = useState('') |
|
const [selectedApiKey, setSelectedApiKey] = useState({}) |
|
const [checkboxVal, setCheckbox] = useState(false) |
|
const [embedChatCheckboxVal, setEmbedChatCheckbox] = useState(false) |
|
|
|
const getAllAPIKeysApi = useApi(apiKeyApi.getAllAPIKeys) |
|
const updateChatflowApi = useApi(chatflowsApi.updateChatflow) |
|
const getConfigApi = useApi(configApi.getConfig) |
|
|
|
const onCheckBoxChanged = (newVal) => { |
|
setCheckbox(newVal) |
|
if (newVal) { |
|
getConfigApi.request(dialogProps.chatflowid) |
|
} |
|
} |
|
|
|
const onCheckBoxEmbedChatChanged = (newVal) => { |
|
setEmbedChatCheckbox(newVal) |
|
} |
|
|
|
const onApiKeySelected = (keyValue) => { |
|
if (keyValue === 'addnewkey') { |
|
navigate('/apikey') |
|
return |
|
} |
|
setChatflowApiKeyId(keyValue) |
|
setSelectedApiKey(apiKeys.find((key) => key.id === keyValue)) |
|
const updateBody = { |
|
apikeyid: keyValue |
|
} |
|
updateChatflowApi.request(dialogProps.chatflowid, updateBody) |
|
} |
|
|
|
useEffect(() => { |
|
if (updateChatflowApi.data) { |
|
dispatch({ type: SET_CHATFLOW, chatflow: updateChatflowApi.data }) |
|
} |
|
}, [updateChatflowApi.data, dispatch]) |
|
|
|
const handleChange = (event, newValue) => { |
|
setValue(newValue) |
|
} |
|
|
|
const getCode = (codeLang) => { |
|
if (codeLang === 'Python') { |
|
return `import requests |
|
|
|
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}" |
|
|
|
def query(payload): |
|
response = requests.post(API_URL, json=payload) |
|
return response.json() |
|
|
|
output = query({ |
|
"question": "Hey, how are you?", |
|
}) |
|
` |
|
} else if (codeLang === 'JavaScript') { |
|
return `async function query(data) { |
|
const response = await fetch( |
|
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", |
|
{ |
|
method: "POST", |
|
body: data |
|
} |
|
); |
|
const result = await response.json(); |
|
return result; |
|
} |
|
|
|
query({"question": "Hey, how are you?"}).then((response) => { |
|
console.log(response); |
|
}); |
|
` |
|
} else if (codeLang === 'cURL') { |
|
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ |
|
-X POST \\ |
|
-d '{"question": "Hey, how are you?"}'` |
|
} else if (codeLang === 'Embed') { |
|
return embedCode(dialogProps.chatflowid) |
|
} |
|
return '' |
|
} |
|
|
|
const getCodeWithAuthorization = (codeLang) => { |
|
if (codeLang === 'Python') { |
|
return `import requests |
|
|
|
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}" |
|
headers = {"Authorization": "Bearer ${selectedApiKey?.apiKey}"} |
|
|
|
def query(payload): |
|
response = requests.post(API_URL, headers=headers, json=payload) |
|
return response.json() |
|
|
|
output = query({ |
|
"question": "Hey, how are you?", |
|
}) |
|
` |
|
} else if (codeLang === 'JavaScript') { |
|
return `async function query(data) { |
|
const response = await fetch( |
|
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", |
|
{ |
|
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" }, |
|
method: "POST", |
|
body: data |
|
} |
|
); |
|
const result = await response.json(); |
|
return result; |
|
} |
|
|
|
query({"question": "Hey, how are you?"}).then((response) => { |
|
console.log(response); |
|
}); |
|
` |
|
} else if (codeLang === 'cURL') { |
|
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ |
|
-X POST \\ |
|
-d '{"question": "Hey, how are you?"}' \\ |
|
-H "Authorization: Bearer ${selectedApiKey?.apiKey}"` |
|
} else if (codeLang === 'Embed') { |
|
return embedCode(dialogProps.chatflowid) |
|
} |
|
return '' |
|
} |
|
|
|
const getLang = (codeLang) => { |
|
if (codeLang === 'Python') { |
|
return 'python' |
|
} else if (codeLang === 'JavaScript' || codeLang === 'Embed') { |
|
return 'javascript' |
|
} else if (codeLang === 'cURL') { |
|
return 'bash' |
|
} |
|
return 'python' |
|
} |
|
|
|
const getSVG = (codeLang) => { |
|
if (codeLang === 'Python') { |
|
return pythonSVG |
|
} else if (codeLang === 'JavaScript') { |
|
return javascriptSVG |
|
} else if (codeLang === 'Embed') { |
|
return EmbedSVG |
|
} else if (codeLang === 'cURL') { |
|
return cURLSVG |
|
} |
|
return pythonSVG |
|
} |
|
|
|
|
|
|
|
const getConfigCodeWithFormData = (codeLang, configData) => { |
|
if (codeLang === 'Python') { |
|
return `import requests |
|
|
|
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}" |
|
|
|
# use form data to upload files |
|
form_data = {${getConfigExamplesForPython(configData, 'formData')}} |
|
|
|
def query(form_data): |
|
response = requests.post(API_URL, files=form_data) |
|
return response.json() |
|
|
|
output = query(form_data) |
|
` |
|
} else if (codeLang === 'JavaScript') { |
|
return `// use FormData to upload files |
|
let formData = new FormData(); |
|
${getConfigExamplesForJS(configData, 'formData')} |
|
async function query(formData) { |
|
const response = await fetch( |
|
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", |
|
{ |
|
method: "POST", |
|
body: formData |
|
} |
|
); |
|
const result = await response.json(); |
|
return result; |
|
} |
|
|
|
query(formData).then((response) => { |
|
console.log(response); |
|
}); |
|
` |
|
} else if (codeLang === 'cURL') { |
|
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ |
|
-X POST \\${getConfigExamplesForCurl(configData, 'formData')}` |
|
} |
|
return '' |
|
} |
|
|
|
|
|
|
|
const getConfigCodeWithFormDataWithAuth = (codeLang, configData) => { |
|
if (codeLang === 'Python') { |
|
return `import requests |
|
|
|
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}" |
|
headers = {"Authorization": "Bearer ${selectedApiKey?.apiKey}"} |
|
|
|
# use form data to upload files |
|
form_data = {${getConfigExamplesForPython(configData, 'formData')}} |
|
|
|
def query(form_data): |
|
response = requests.post(API_URL, headers=headers, files=form_data) |
|
return response.json() |
|
|
|
output = query(form_data) |
|
` |
|
} else if (codeLang === 'JavaScript') { |
|
return `// use FormData to upload files |
|
let formData = new FormData(); |
|
${getConfigExamplesForJS(configData, 'formData')} |
|
async function query(formData) { |
|
const response = await fetch( |
|
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", |
|
{ |
|
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" }, |
|
method: "POST", |
|
body: formData |
|
} |
|
); |
|
const result = await response.json(); |
|
return result; |
|
} |
|
|
|
query(formData).then((response) => { |
|
console.log(response); |
|
}); |
|
` |
|
} else if (codeLang === 'cURL') { |
|
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ |
|
-X POST \\${getConfigExamplesForCurl(configData, 'formData')} \\ |
|
-H "Authorization: Bearer ${selectedApiKey?.apiKey}"` |
|
} |
|
return '' |
|
} |
|
|
|
|
|
|
|
const getConfigCode = (codeLang, configData) => { |
|
if (codeLang === 'Python') { |
|
return `import requests |
|
|
|
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}" |
|
|
|
def query(payload): |
|
response = requests.post(API_URL, json=payload) |
|
return response.json() |
|
|
|
output = query({ |
|
"question": "Hey, how are you?", |
|
"overrideConfig": {${getConfigExamplesForPython(configData, 'json')} |
|
} |
|
}) |
|
` |
|
} else if (codeLang === 'JavaScript') { |
|
return `async function query(data) { |
|
const response = await fetch( |
|
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", |
|
{ |
|
method: "POST", |
|
body: data |
|
} |
|
); |
|
const result = await response.json(); |
|
return result; |
|
} |
|
|
|
query({ |
|
"question": "Hey, how are you?", |
|
"overrideConfig": {${getConfigExamplesForJS(configData, 'json')} |
|
} |
|
}).then((response) => { |
|
console.log(response); |
|
}); |
|
` |
|
} else if (codeLang === 'cURL') { |
|
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ |
|
-X POST \\ |
|
-d '{"question": "Hey, how are you?", "overrideConfig": {${getConfigExamplesForCurl(configData, 'json')}}'` |
|
} |
|
return '' |
|
} |
|
|
|
|
|
|
|
const getConfigCodeWithAuthorization = (codeLang, configData) => { |
|
if (codeLang === 'Python') { |
|
return `import requests |
|
|
|
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}" |
|
headers = {"Authorization": "Bearer ${selectedApiKey?.apiKey}"} |
|
|
|
def query(payload): |
|
response = requests.post(API_URL, headers=headers, json=payload) |
|
return response.json() |
|
|
|
output = query({ |
|
"question": "Hey, how are you?", |
|
"overrideConfig": {${getConfigExamplesForPython(configData, 'json')} |
|
} |
|
}) |
|
` |
|
} else if (codeLang === 'JavaScript') { |
|
return `async function query(data) { |
|
const response = await fetch( |
|
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", |
|
{ |
|
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" }, |
|
method: "POST", |
|
body: data |
|
} |
|
); |
|
const result = await response.json(); |
|
return result; |
|
} |
|
|
|
query({ |
|
"question": "Hey, how are you?", |
|
"overrideConfig": {${getConfigExamplesForJS(configData, 'json')} |
|
} |
|
}).then((response) => { |
|
console.log(response); |
|
}); |
|
` |
|
} else if (codeLang === 'cURL') { |
|
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ |
|
-X POST \\ |
|
-d '{"question": "Hey, how are you?", "overrideConfig": {${getConfigExamplesForCurl(configData, 'json')}}' \\ |
|
-H "Authorization: Bearer ${selectedApiKey?.apiKey}"` |
|
} |
|
return '' |
|
} |
|
|
|
useEffect(() => { |
|
if (getAllAPIKeysApi.data) { |
|
const options = [ |
|
{ |
|
label: 'No Authorization', |
|
name: '' |
|
} |
|
] |
|
for (const key of getAllAPIKeysApi.data) { |
|
options.push({ |
|
label: key.keyName, |
|
name: key.id |
|
}) |
|
} |
|
options.push({ |
|
label: '- Add New Key -', |
|
name: 'addnewkey' |
|
}) |
|
setKeyOptions(options) |
|
setAPIKeys(getAllAPIKeysApi.data) |
|
|
|
if (dialogProps.chatflowApiKeyId) { |
|
setChatflowApiKeyId(dialogProps.chatflowApiKeyId) |
|
setSelectedApiKey(getAllAPIKeysApi.data.find((key) => key.id === dialogProps.chatflowApiKeyId)) |
|
} |
|
} |
|
}, [dialogProps, getAllAPIKeysApi.data]) |
|
|
|
useEffect(() => { |
|
if (show) { |
|
getAllAPIKeysApi.request() |
|
} |
|
|
|
|
|
}, [show]) |
|
|
|
const component = show ? ( |
|
<Dialog |
|
open={show} |
|
fullWidth |
|
maxWidth='md' |
|
onClose={onCancel} |
|
aria-labelledby='alert-dialog-title' |
|
aria-describedby='alert-dialog-description' |
|
> |
|
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'> |
|
{dialogProps.title} |
|
</DialogTitle> |
|
<DialogContent> |
|
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}> |
|
<div style={{ flex: 80 }}> |
|
<Tabs value={value} onChange={handleChange} aria-label='tabs'> |
|
{codes.map((codeLang, index) => ( |
|
<Tab |
|
icon={ |
|
<img style={{ objectFit: 'cover', height: 15, width: 'auto' }} src={getSVG(codeLang)} alt='code' /> |
|
} |
|
iconPosition='start' |
|
key={index} |
|
label={codeLang} |
|
{...a11yProps(index)} |
|
></Tab> |
|
))} |
|
</Tabs> |
|
</div> |
|
{value !== 0 && ( |
|
<div style={{ flex: 20 }}> |
|
<Dropdown |
|
name='SelectKey' |
|
disableClearable={true} |
|
options={keyOptions} |
|
onSelect={(newValue) => onApiKeySelected(newValue)} |
|
value={dialogProps.chatflowApiKeyId ?? chatflowApiKeyId ?? 'Choose an API key'} |
|
/> |
|
</div> |
|
)} |
|
</div> |
|
<div style={{ marginTop: 10 }}></div> |
|
{codes.map((codeLang, index) => ( |
|
<TabPanel key={index} value={value} index={index}> |
|
{value === 0 && ( |
|
<> |
|
<span> |
|
Paste this anywhere in the <code>{`<body>`}</code> tag of your html file. |
|
<p> |
|
You can also specify a |
|
<a |
|
rel='noreferrer' |
|
target='_blank' |
|
href='https://www.npmjs.com/package/flowise-embed?activeTab=versions' |
|
> |
|
version |
|
</a> |
|
: <code>{`https://cdn.jsdelivr.net/npm/flowise-embed@<version>/dist/web.js`}</code> |
|
</p> |
|
</span> |
|
<div style={{ height: 10 }}></div> |
|
</> |
|
)} |
|
<CopyBlock |
|
theme={atomOneDark} |
|
text={chatflowApiKeyId ? getCodeWithAuthorization(codeLang) : getCode(codeLang)} |
|
language={getLang(codeLang)} |
|
showLineNumbers={false} |
|
wrapLines |
|
/> |
|
{value !== 0 && <CheckboxInput label='Show Input Config' value={checkboxVal} onChange={onCheckBoxChanged} />} |
|
{value !== 0 && checkboxVal && getConfigApi.data && getConfigApi.data.length > 0 && ( |
|
<> |
|
<TableViewOnly rows={getConfigApi.data} columns={Object.keys(getConfigApi.data[0])} /> |
|
<CopyBlock |
|
theme={atomOneDark} |
|
text={ |
|
chatflowApiKeyId |
|
? dialogProps.isFormDataRequired |
|
? getConfigCodeWithFormDataWithAuth(codeLang, getConfigApi.data) |
|
: getConfigCodeWithAuthorization(codeLang, getConfigApi.data) |
|
: dialogProps.isFormDataRequired |
|
? getConfigCodeWithFormData(codeLang, getConfigApi.data) |
|
: getConfigCode(codeLang, getConfigApi.data) |
|
} |
|
language={getLang(codeLang)} |
|
showLineNumbers={false} |
|
wrapLines |
|
/> |
|
</> |
|
)} |
|
{value === 0 && ( |
|
<CheckboxInput |
|
label='Show Embed Chat Config' |
|
value={embedChatCheckboxVal} |
|
onChange={onCheckBoxEmbedChatChanged} |
|
/> |
|
)} |
|
{value === 0 && embedChatCheckboxVal && ( |
|
<CopyBlock |
|
theme={atomOneDark} |
|
text={embedCodeCustomization(dialogProps.chatflowid)} |
|
language={getLang('Embed')} |
|
showLineNumbers={false} |
|
wrapLines |
|
/> |
|
)} |
|
</TabPanel> |
|
))} |
|
</DialogContent> |
|
</Dialog> |
|
) : null |
|
|
|
return createPortal(component, portalElement) |
|
} |
|
|
|
APICodeDialog.propTypes = { |
|
show: PropTypes.bool, |
|
dialogProps: PropTypes.object, |
|
onCancel: PropTypes.func |
|
} |
|
|
|
export default APICodeDialog |
|
|