Added popup code editor functionality [1]

This commit is contained in:
gohil-jay
2021-10-12 16:56:46 +05:30
parent 09d02c7f18
commit 2cce2bffd8
4 changed files with 19640 additions and 100 deletions
+19512
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -1,15 +1,14 @@
{ {
"name": "shuffler", "name": "shuffler",
"homepage": "https://shuffler.io", "homepage": "https://shuffler.io",
"version": "0.9.22", "version": "0.8.92",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^4.5.2", "@material-ui/core": "^4.5.2",
"@material-ui/data-grid": "^4.0.0-alpha.22", "@material-ui/data-grid": "^4.0.0-alpha.22",
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.5.1",
"@material-ui/lab": "^4.0.0-alpha.58", "@material-ui/lab": "^4.0.0-alpha.58",
"@material-ui/styles": "^4.5.2", "@material-ui/styles": "^4.5.2",
"@material-ui/utils": "^4.11.2",
"@uiw/react-codemirror": "^3.2.1", "@uiw/react-codemirror": "^3.2.1",
"@use-it/interval": "^1.0.0", "@use-it/interval": "^1.0.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
+37 -97
View File
@@ -5,18 +5,18 @@ import { GetParsedPaths } from "../views/Apps.jsx";
import { GetIconInfo } from "../views/Workflows.jsx"; import { GetIconInfo } from "../views/Workflows.jsx";
import { sortByKey } from "../views/AngularWorkflow.jsx"; import { sortByKey } from "../views/AngularWorkflow.jsx";
import { useTheme } from '@material-ui/core/styles'; import { useTheme } from '@material-ui/core/styles';
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
import NestedMenuItem from "material-ui-nested-menu-item"; import NestedMenuItem from "material-ui-nested-menu-item";
//import NestedMenuItem from "./NestedMenu.jsx"; //import NestedMenuItem from "./NestedMenu.jsx";
import {Popper, TextField, TextareaAutosize, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade} from '@material-ui/core'; import {Popper, TextField, TextareaAutosize, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade} from '@material-ui/core';
import {HelpOutline as HelpOutlineIcon, Description as DescriptionIcon, GetApp as GetAppIcon, Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; import {GetApp as GetAppIcon, Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons';
import Autocomplete from '@material-ui/lab/Autocomplete'; import Autocomplete from '@material-ui/lab/Autocomplete';
import CodeMirror from '@uiw/react-codemirror'; import CodeMirror from '@uiw/react-codemirror';
import 'codemirror/keymap/sublime'; import 'codemirror/keymap/sublime';
import 'codemirror/theme/gruvbox-dark.css'; import 'codemirror/theme/gruvbox-dark.css';
const useStyles = makeStyles({ const useStyles = makeStyles({
notchedOutline: { notchedOutline: {
borderColor: "#f85a3e !important" borderColor: "#f85a3e !important"
@@ -51,6 +51,8 @@ const useStyles = makeStyles({
const ParsedAction = (props) => { const ParsedAction = (props) => {
const {workflow, setWorkflow, setAction, setSelectedAction, setUpdate, appActionArguments, selectedApp, workflowExecutions, setSelectedResult, selectedAction, setSelectedApp, setSelectedTrigger, setSelectedEdge, setCurrentView, cy, setAuthenticationModalOpen,setVariablesModalOpen, setCodeModalOpen, selectedNameChange, rightsidebarStyle, showEnvironment, selectedActionEnvironment, environments, setNewSelectedAction, appApiViewStyle, globalUrl, setSelectedActionEnvironment, requiresAuthentication, hideExtraTypes, scrollConfig, setScrollConfig, authenticationType, appAuthentication, getAppAuthentication } = props const {workflow, setWorkflow, setAction, setSelectedAction, setUpdate, appActionArguments, selectedApp, workflowExecutions, setSelectedResult, selectedAction, setSelectedApp, setSelectedTrigger, setSelectedEdge, setCurrentView, cy, setAuthenticationModalOpen,setVariablesModalOpen, setCodeModalOpen, selectedNameChange, rightsidebarStyle, showEnvironment, selectedActionEnvironment, environments, setNewSelectedAction, appApiViewStyle, globalUrl, setSelectedActionEnvironment, requiresAuthentication, hideExtraTypes, scrollConfig, setScrollConfig, authenticationType, appAuthentication, getAppAuthentication } = props
const [codedata, setcodedata] = React.useState("print('Hello')");
// const [codelang, setcodelang] = React.useState('python');
const theme = useTheme(); const theme = useTheme();
const classes = useStyles() const classes = useStyles()
@@ -481,7 +483,6 @@ const ParsedAction = (props) => {
} }
} }
//console.log("CHANGING ACTION COUNT !")
selectedActionParameters[count].value = event.target.value selectedActionParameters[count].value = event.target.value
selectedAction.parameters[count].value = event.target.value selectedAction.parameters[count].value = event.target.value
setSelectedAction(selectedAction) setSelectedAction(selectedAction)
@@ -851,30 +852,13 @@ const ParsedAction = (props) => {
marginLeft: 5, marginLeft: 5,
maxWidth: "95%", maxWidth: "95%",
fontSize: "1em", fontSize: "1em",
}, }
endAdornment: (
hideExtraTypes ? null :
<InputAdornment position="end">
<Tooltip title="Autocomplete the text" placement="top">
<AddCircleOutlineIcon style={{cursor: "pointer"}} onClick={(event) => {
setMenuPosition({
top: event.pageY+10,
left: event.pageX+10,
})
setShowDropdownNumber(count)
setShowDropdown(true)
setShowAutocomplete(true)
}}/>
</Tooltip>
</InputAdornment>
)
}} }}
fullWidth fullWidth
multiline={multiline} multiline={multiline}
onClick={() => { onClick={() => {
console.log("Clicked field: ", clickedFieldId) console.log("Clicked field: ", clickedFieldId)
setExpansionModalOpen(false) setExpansionModalOpen(true)
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
scrollConfig.selected = clickedFieldId scrollConfig.selected = clickedFieldId
setScrollConfig(scrollConfig) setScrollConfig(scrollConfig)
@@ -884,20 +868,14 @@ const ParsedAction = (props) => {
id={clickedFieldId} id={clickedFieldId}
rows={rows} rows={rows}
color="primary" color="primary"
defaultValue={data.value} value={codedata}
//value={data.value} //defaultValue={data.value}
//options={{
// theme: 'gruvbox-dark',
// keyMap: 'sublime',
// mode: 'python',
//}}
//height={multiline ? 50 : 150} //height={multiline ? 50 : 150}
type={placeholder.includes("***") || (data.configuration && (data.name.toLowerCase().includes("api") || data.name.toLowerCase().includes("key") || data.name.toLowerCase().includes("pass"))) ? "password" : "text"} type={placeholder.includes("***") || (data.configuration && (data.name.toLowerCase().includes("api") || data.name.toLowerCase().includes("key") || data.name.toLowerCase().includes("pass"))) ? "password" : "text"}
placeholder={placeholder} placeholder={placeholder}
onChange={(event) => { onChange={(event) => {
//changeActionParameterCodemirror(event, count, data)
changeActionParameter(event, count, data) changeActionParameter(event, count, data)
}} }}
@@ -927,7 +905,7 @@ const ParsedAction = (props) => {
/> />
//console.log("FIELD VALUE: ", data.value) //console.log("FIELD VALUE: ", data.value)
//const regexp = new RegExp("\W+\.", "g") //const regexp = new RegExp("\W+\.", "g")
//let match //let match
//while ((match = regexp.exec(data.value)) !== null) { //while ((match = regexp.exec(data.value)) !== null) {
// console.log(`Found ${match[0]} start=${match.index} end=${regexp.lastIndex}.`); // console.log(`Found ${match[0]} start=${match.index} end=${regexp.lastIndex}.`);
@@ -958,7 +936,7 @@ const ParsedAction = (props) => {
endAdornment: ( endAdornment: (
hideExtraTypes ? null : hideExtraTypes ? null :
<InputAdornment position="end"> <InputAdornment position="end">
<Tooltip title="Autocomplete the text" placement="top"> <Tooltip title="Autocomplete text" placement="top">
<AddCircleOutlineIcon style={{cursor: "pointer"}} onClick={(event) => { <AddCircleOutlineIcon style={{cursor: "pointer"}} onClick={(event) => {
setMenuPosition({ setMenuPosition({
top: event.pageY+10, top: event.pageY+10,
@@ -1425,27 +1403,6 @@ const ParsedAction = (props) => {
return null return null
} }
const expansionModal =
<Dialog modal
open={expansionModalOpen}
onClose={() => {
setExpansionModalOpen(false)
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: 600,
padding: 50,
},
}}
>
<DialogTitle><span style={{color: "white"}}>Workflow Variable</span></DialogTitle>
<DialogContent>
Hello
</DialogContent>
</Dialog>
//const CustomPopper = function (props) { //const CustomPopper = function (props) {
// const classes = useStyles() // const classes = useStyles()
// return <Popper {...props} className={classes.root} placement="bottom" /> // return <Popper {...props} className={classes.root} placement="bottom" />
@@ -1454,13 +1411,20 @@ const ParsedAction = (props) => {
const baselabel = selectedAction.label const baselabel = selectedAction.label
return ( return (
<div style={appApiViewStyle} id="parsed_action_view"> <div style={appApiViewStyle} id="parsed_action_view">
{expansionModal} {<ShuffleCodeEditor
codedata={codedata}
setcodedata={setcodedata}
setExpansionModalOpen={setExpansionModalOpen}
expansionModalOpen={expansionModalOpen}
// codelang={codelang}
// setcodelang={setcodelang}
/>}
{hideExtraTypes === true ? null : {hideExtraTypes === true ? null :
<span> <span>
<div style={{display: "flex", minHeight: 40, marginBottom: 30}}> <div style={{display: "flex", minHeight: 40, marginBottom: 30}}>
<div style={{flex: 1}}> <div style={{flex: 1}}>
<h3 style={{marginBottom: 5}}>{(selectedAction.app_name.charAt(0).toUpperCase()+selectedAction.app_name.substring(1)).replaceAll("_", " ")}</h3> <h3 style={{marginBottom: 5}}>{(selectedAction.app_name.charAt(0).toUpperCase()+selectedAction.app_name.substring(1)).replaceAll("_", " ")}</h3>
<div style={{display: "flex", marginTop: 10, }}> <div style={{display: "flex",}}>
<IconButton style={{marginTop: "auto", marginBottom: "auto", height: 30, paddingLeft: 0, paddingRight: 0}} onClick={() => { <IconButton style={{marginTop: "auto", marginBottom: "auto", height: 30, paddingLeft: 0, paddingRight: 0}} onClick={() => {
console.log("FIND EXAMPLE RESULTS FOR ", selectedAction) console.log("FIND EXAMPLE RESULTS FOR ", selectedAction)
if (workflowExecutions.length > 0) { if (workflowExecutions.length > 0) {
@@ -1489,24 +1453,19 @@ const ParsedAction = (props) => {
<ArrowLeftIcon style={{color: "white"}}/> <ArrowLeftIcon style={{color: "white"}}/>
</Tooltip> </Tooltip>
</IconButton> </IconButton>
<IconButton style={{marginTop: "auto", marginBottom: "auto", height: 30, paddingLeft: 25, paddingRight: 0}} onClick={() => { <span style={{}}>
setAuthenticationModalOpen(true) <Typography style={{marginTop: 5,}}><a rel="norefferer" href="https://shuffler.io/docs/workflows#nodes" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>What are actions?</a></Typography>
}}> {selectedAction.errors !== undefined && selectedAction.errors !== null && selectedAction.errors.length > 0 ?
<Tooltip color="primary" title="Read app docs" placement="top"> <div>
<DescriptionIcon style={{color: "white"}} /> Errors: {selectedAction.errors.join("\n")}
</Tooltip> </div>
</IconButton> : null
<IconButton style={{marginTop: "auto", marginBottom: "auto", height: 30, paddingLeft: 25, paddingRight: 0}} onClick={() => {}}> }
<a rel="norefferer" href="https://shuffler.io/docs/workflows#nodes" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}> </span>
<Tooltip color="primary" title="What are actions?" placement="top">
<HelpOutlineIcon style={{color: "white"}}/>
</Tooltip>
</a>
</IconButton>
</div> </div>
</div> </div>
<div style={{display: "flex", flexDirection: "column",}}> <div style={{display: "flex", flexDirection: "column",}}>
{/*selectedAction.id === workflow.start ? null : {selectedAction.id === workflow.start ? null :
<Tooltip color="primary" title={"Make this node the start action"} placement="top"> <Tooltip color="primary" title={"Make this node the start action"} placement="top">
<Button style={{zIndex: 5000, marginTop: 10,}} color="primary" variant="outlined" onClick={(e) => { <Button style={{zIndex: 5000, marginTop: 10,}} color="primary" variant="outlined" onClick={(e) => {
defineStartnode(e) defineStartnode(e)
@@ -1514,7 +1473,7 @@ const ParsedAction = (props) => {
<KeyboardArrowRightIcon /> <KeyboardArrowRightIcon />
</Button> </Button>
</Tooltip> </Tooltip>
*/} }
{selectedApp.versions !== null && selectedApp.versions !== undefined && selectedApp.versions.length > 1 ? {selectedApp.versions !== null && selectedApp.versions !== undefined && selectedApp.versions.length > 1 ?
<Select <Select
defaultValue={selectedAction.app_version} defaultValue={selectedAction.app_version}
@@ -1556,7 +1515,6 @@ const ParsedAction = (props) => {
fullWidth fullWidth
color="primary" color="primary"
placeholder={selectedAction.label} placeholder={selectedAction.label}
defaultValue={selectedAction.label}
onChange={selectedNameChange} onChange={selectedNameChange}
onBlur={(e) => { onBlur={(e) => {
const name = e.target.value const name = e.target.value
@@ -1603,7 +1561,7 @@ const ParsedAction = (props) => {
<div style={{display: "flex"}}> <div style={{display: "flex"}}>
<Select <Select
labelId="select-app-auth" labelId="select-app-auth"
value={Object.getOwnPropertyNames(selectedAction.selectedAuthentication).length === 0 ? "No selection" : selectedAction.selectedAuthentication} value={selectedAction.selectedAuthentication}
SelectDisplayProps={{ SelectDisplayProps={{
style: { style: {
marginLeft: 10, marginLeft: 10,
@@ -1611,32 +1569,14 @@ const ParsedAction = (props) => {
}} }}
fullWidth fullWidth
onChange={(e) => { onChange={(e) => {
if (e.target.value === "No selection") { //console.log("CHOSE AN AUTHENTICATION OPTION: ", e.target.value)
selectedAction.selectedAuthentication = {} selectedAction.selectedAuthentication = e.target.value
selectedAction.authentication_id = "" selectedAction.authentication_id = e.target.value.id
setSelectedAction(selectedAction)
for (var key in selectedAction.parameters) { setUpdate(Math.random())
console.log(selectedAction.parameters[key])
if (selectedAction.parameters[key].configuration) {
selectedAction.parameters[key].value = ""
}
}
setSelectedAction(selectedAction)
setUpdate(Math.random())
} else {
//console.log("CHOSE AN AUTHENTICATION OPTION: ", e.target.value)
selectedAction.selectedAuthentication = e.target.value
selectedAction.authentication_id = e.target.value.id
setSelectedAction(selectedAction)
setUpdate(Math.random())
}
}} }}
style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, maxWidth: rightsidebarStyle.maxWidth-80, borderRadius: theme.palette.borderRadius,}} style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, maxWidth: rightsidebarStyle.maxWidth-80, borderRadius: theme.palette.borderRadius,}}
> >
<MenuItem style={{backgroundColor: theme.palette.inputColor, color: "white"}} value="No selection">
<em>No selection</em>
</MenuItem>
{selectedAction.authentication.map(data => { {selectedAction.authentication.map(data => {
//console.log("AUTH DATA: ", data) //console.log("AUTH DATA: ", data)
return( return(
@@ -1731,7 +1671,7 @@ const ParsedAction = (props) => {
<MenuItem style={{backgroundColor: theme.palette.inputColor, color: "white"}} value="No selection"> <MenuItem style={{backgroundColor: theme.palette.inputColor, color: "white"}} value="No selection">
<em>No selection</em> <em>No selection</em>
</MenuItem> </MenuItem>
<Divider style={{backgroundColor: theme.palette.inputColor }} /> <Divider />
{workflow.execution_variables.map(data => ( {workflow.execution_variables.map(data => (
<MenuItem style={{backgroundColor: theme.palette.inputColor, color: "white"}} value={data.name}> <MenuItem style={{backgroundColor: theme.palette.inputColor, color: "white"}} value={data.name}>
{data.name} {data.name}
@@ -0,0 +1,89 @@
import React, {useState, useEffect, useLayoutEffect} from 'react';
import {Dialog, Modal, DialogTitle, DialogContent} from '@material-ui/core';
import { useTheme } from '@material-ui/core/styles';
import CodeMirror from '@uiw/react-codemirror';
import 'codemirror/keymap/sublime';
import 'codemirror/theme/gruvbox-dark.css';
const Textfield = (props) => {
const {expansionModalOpen, setExpansionModalOpen} = props
const {codedata, setcodedata} = props
const [localcodedata, setlocalcodedata] = React.useState("print('Hello')");
// const {codelang, setcodelang} = props
const theme = useTheme();
return (
<Dialog modal
open={expansionModalOpen}
onClose={() => {
setExpansionModalOpen(false)
}}
PaperProps={{
style: {
backgroundColor: theme.palette.surfaceColor,
color: "white",
minWidth: 600,
padding: 50,
},
}}
>
<div
style={{
display: 'flex',
justifyContent: 'right',
}}
>
<button
style={{
color: "white",
background: "#27292d",
border: "2px solid white",
borderRadius: '50%',
fontSize: 15,
width: 40,
height: 40,
cursor: "pointer"
}}
onClick={() => {
setExpansionModalOpen(false)
}}
>
</button>
</div>
<DialogTitle><span style={{color: "white"}}>Code Editor</span></DialogTitle>
<CodeMirror
value = {codedata}
height="200px"
onChange={(value) => {
// setcodedata(value.getValue())
setlocalcodedata(value.getValue())
}}
options={{
theme: 'gruvbox-dark',
keyMap: 'sublime',
mode: 'python',
// mode: {codelang},
}}
/>
<button
style={{
color: "white",
background: "#f85a3e",
border: "none",
height: 35,
cursor: "pointer"
}}
onClick={() => {
setcodedata(localcodedata)
setExpansionModalOpen(false)
}}
>
DONE
</button>
</Dialog>)
}
export default Textfield;