Fixed jumpyness of Code Editor. Main missing piece: Actions

This commit is contained in:
Frikky
2023-08-16 02:22:39 +02:00
parent f9e9f0feb7
commit c0adb9e198
5 changed files with 268 additions and 153 deletions
+2 -2
View File
@@ -14,8 +14,8 @@
"@mui/styles": "^5.14.0",
"@mui/x-data-grid": "^5.17.11",
"@mui/x-date-pickers": "^6.11.1",
"@uiw/codemirror-themes": "^4.21.7",
"@uiw/react-codemirror": "^4.21.7",
"@uiw/codemirror-themes": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9",
"@use-it/interval": "^1.0.0",
"algoliasearch": "^4.13.1",
"calculate-size": "^1.1.1",
+64 -78
View File
@@ -92,7 +92,7 @@ import {
//import CodeMirror from "@uiw/react-codemirror";
//import "codemirror/keymap/sublime";
//import "codemirror/theme/gruvbox-dark.css";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
//import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
const useStyles = makeStyles({
notchedOutline: {
@@ -158,28 +158,30 @@ const ParsedAction = (props) => {
authenticationType,
appAuthentication,
getAppAuthentication,
actionDelayChange,
getParents,
isCloud,
lastSaved,
setLastSaved,
setShowVideo,
toolsAppId,
aiSubmit,
//expansionModalOpen,
//setExpansionModalOpen,
actionDelayChange,
getParents,
isCloud,
lastSaved,
setLastSaved,
setShowVideo,
toolsAppId,
aiSubmit,
expansionModalOpen,
setExpansionModalOpen,
setEditorData,
setcodedata,
} = props;
const classes = useStyles();
const alert = useAlert()
const [expansionModalOpen, setExpansionModalOpen] = React.useState(false);
const [hideBody, setHideBody] = React.useState(true);
const [activateHidingBodyButton, setActivateHidingBodyButton] = React.useState(false);
const [codedata, setcodedata] = React.useState("");
const [fieldCount, setFieldCount] = React.useState(0);
const [hiddenDescription, setHiddenDescription] = React.useState(true);
const [fieldCount, setFieldCount] = React.useState(0);
const [hiddenDescription, setHiddenDescription] = React.useState(true);
useEffect(() => {
@@ -1518,29 +1520,6 @@ const ParsedAction = (props) => {
const clickedFieldId = "rightside_field_" + count;
const shufflecode = fieldCount !== count ? null :
(
<ShuffleCodeEditor
isCloud={isCloud}
toolsAppId={toolsAppId}
fieldCount = {fieldCount}
setFieldCount = {setFieldCount}
actionlist = {actionlist}
changeActionParameterCodeMirror = {changeActionParameterCodeMirror}
codedata={codedata}
setcodedata={setcodedata}
expansionModalOpen={expansionModalOpen}
setExpansionModalOpen={setExpansionModalOpen}
globalUrl={globalUrl}
workflowExecutions={workflowExecutions}
getParents={getParents}
selectedAction={selectedAction}
parameterName={data.name}
aiSubmit={aiSubmit}
/>
)
//<TextareaAutosize
// <CodeMirror
//fullWidth
@@ -1590,38 +1569,47 @@ const ParsedAction = (props) => {
disableUnderline: true,
endAdornment: hideExtraTypes ? null : (
<InputAdornment position="end">
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}>
<Tooltip title="Expand window" placement="top">
<AspectRatioIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0 ,}}
onClick={(event) => {
event.preventDefault()
setFieldCount(count)
setcodedata(data.value)
setExpansionModalOpen(true)
}}
/>
</Tooltip>
<Tooltip title="Autocomplete text" placement="top">
<AddCircleOutlineIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }}
onClick={(event) => {
event.preventDefault()
<ButtonGroup orientation={multiline ? "vertical" : "horizontal"}>
<Tooltip title="Expand window" placement="top">
<AspectRatioIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0 ,}}
onClick={(event) => {
event.preventDefault()
setFieldCount(count)
setExpansionModalOpen(true)
// Get cursor position
// This makes it so we can put it in the right location?
setMenuPosition({
top: event.pageY + 10,
left: event.pageX + 10,
});
setShowDropdownNumber(count);
setShowDropdown(true);
setShowAutocomplete(true);
}}
/>
</Tooltip>
</ButtonGroup>
</InputAdornment>
//setcodedata(data.value)
setEditorData({
"name": data.name,
"value": data.value,
"field_number": count,
"actionlist": actionlist,
"field_id": clickedFieldId,
})
}}
/>
</Tooltip>
<Tooltip title="Autocomplete text" placement="top">
<AddCircleOutlineIcon
style={{ cursor: "pointer", margin: multiline ? 5 : 0, }}
onClick={(event) => {
event.preventDefault()
// Get cursor position
// This makes it so we can put it in the right location?
setMenuPosition({
top: event.pageY + 10,
left: event.pageX + 10,
});
setShowDropdownNumber(count);
setShowDropdown(true);
setShowAutocomplete(true);
}}
/>
</Tooltip>
</ButtonGroup>
</InputAdornment>
),
}}
multiline={data.name.startsWith("${") && data.name.endsWith("}") ? true : multiline}
@@ -1651,11 +1639,11 @@ const ParsedAction = (props) => {
*/
//console.log("Clicked field: ", clickedFieldId)
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
scrollConfig.selected = clickedFieldId
setScrollConfig(scrollConfig)
//console.log("Change field id!")
}
if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) {
scrollConfig.selected = clickedFieldId
setScrollConfig(scrollConfig)
//console.log("Change field id!")
}
}}
id={clickedFieldId}
rows={data.name.startsWith("${") && data.name.endsWith("}") ? 2 : rows}
@@ -2567,7 +2555,7 @@ const ParsedAction = (props) => {
*/}
</div>
{datafield}
{shufflecode}
{/*shufflecode*/}
{showDropdown &&
showDropdownNumber === count &&
data.variant === "STATIC_VALUE" &&
@@ -3542,8 +3530,6 @@ const ParsedAction = (props) => {
return null;
}
console.log("OPTION: ", option)
const newname = (
option.name.charAt(0).toUpperCase() + option.name.substring(1)
).replaceAll("_", " ");
+72 -68
View File
@@ -54,9 +54,10 @@ import {indentWithTab} from "@codemirror/commands"
import { padding, textAlign } from '@mui/system';
import data from '../frameworkStyle.jsx';
import { useNavigate, Link, useParams } from "react-router-dom";
import { tags as t } from '@lezer/highlight';
import { createTheme } from '@uiw/codemirror-themes';
import { tags } from '@lezer/highlight';
const liquidFilters = [
{"name": "Size", "value": "size", "example": ""},
@@ -81,44 +82,39 @@ const pythonFilters = [
{"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``},
]
//const shuffleTheme = createTheme({
// theme: 'dark',
// settings: {
// background: '#282828',
// foreground: '#282828',
// caret: '#5d00ff',
// selection: '#036dd626',
// selectionMatch: '#036dd626',
// lineHighlight: '#8a91991a',
// gutterBackground: '#282828',
// gutterForeground: '#8a919966',
// fontSize: 18,
// borderRadius: theme.palette.borderRadius,
// border: `2px solid ${theme.palette.inputColor}`,
// },
// styles: [
// { tag: tags.comment, color: '#787b8099' },
// { tag: tags.variableName, color: '#0080ff' },
// { tag: [tags.string, tags.special(tags.brace)], color: '#5c6166' },
// { tag: tags.number, color: '#5c6166' },
// { tag: tags.bool, color: '#5c6166' },
// { tag: tags.null, color: '#5c6166' },
// { tag: tags.keyword, color: '#5c6166' },
// { tag: tags.operator, color: '#5c6166' },
// { tag: tags.className, color: '#5c6166' },
// { tag: tags.definition(tags.typeName), color: '#5c6166' },
// { tag: tags.typeName, color: '#5c6166' },
// { tag: tags.angleBracket, color: '#5c6166' },
// { tag: tags.tagName, color: '#5c6166' },
// { tag: tags.attributeName, color: '#5c6166' },
// ],
//});
const shuffleTheme = createTheme({
theme: 'dark',
settings: {
background: "rgba(40,40,40, 1)",
foreground: '#75baff',
caret: '#5d00ff',
selection: '#036dd626',
selectionMatch: '#036dd626',
lineHighlight: '#8a91991a',
gutterForeground: '#8a919966',
},
styles: [
{ tag: t.comment, color: '#787b8099' },
{ tag: t.variableName, color: '#0080ff' },
{ tag: [t.string, t.special(t.brace)], color: '#5c6166' },
{ tag: t.number, color: '#5c6166' },
{ tag: t.bool, color: '#5c6166' },
{ tag: t.null, color: '#5c6166' },
{ tag: t.keyword, color: '#5c6166' },
{ tag: t.operator, color: '#5c6166' },
{ tag: t.className, color: '#5c6166' },
{ tag: t.definition(t.typeName), color: '#5c6166' },
{ tag: t.typeName, color: '#5c6166' },
{ tag: t.angleBracket, color: '#5c6166' },
{ tag: t.tagName, color: '#5c6166' },
{ tag: t.attributeName, color: '#5c6166' },
],
});
const CodeEditor = (props) => {
const {
globalUrl,
fieldCount,
setFieldCount,
actionlist,
changeActionParameterCodeMirror,
expansionModalOpen,
@@ -132,6 +128,8 @@ const CodeEditor = (props) => {
selectedAction ,
workflowExecutions,
getParents,
fieldname,
} = props
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
@@ -140,7 +138,7 @@ const CodeEditor = (props) => {
const [validation, setValidation] = React.useState(false);
const [expOutput, setExpOutput] = React.useState(" ");
const [linewrap, setlinewrap] = React.useState(true);
const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark");
//const [codeTheme, setcodeTheme] = React.useState("gruvbox-dark");
const [editorPopupOpen, setEditorPopupOpen] = React.useState(false);
const [currentCharacter, setCurrentCharacter] = React.useState(-1);
@@ -155,8 +153,8 @@ const CodeEditor = (props) => {
const [mainVariables, setMainVariables] = React.useState([]);
const [availableVariables, setAvailableVariables] = React.useState([]);
const [menuPosition, setMenuPosition] = useState(null);
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
const [menuPosition, setMenuPosition] = useState(null);
const [showAutocomplete, setShowAutocomplete] = React.useState(false);
const [isAiLoading, setIsAiLoading] = React.useState(false);
@@ -919,7 +917,6 @@ const CodeEditor = (props) => {
maxHeight: isMobile ? "100%" : 720,
border: theme.palette.defaultBorder,
padding: isMobile ? "25px 10px 25px 10px" : 25,
backgroundColor: theme.palette.surfaceColor,
},
}}
>
@@ -1020,6 +1017,7 @@ const CodeEditor = (props) => {
aria-controls={liquidOpen ? 'basic-menu' : undefined}
aria-expanded={liquidOpen ? 'true' : undefined}
variant="outlined"
color="secondary"
style={{
textTransform: "none",
width: 100,
@@ -1055,6 +1053,7 @@ const CodeEditor = (props) => {
aria-controls={mathOpen ? 'basic-menu' : undefined}
aria-expanded={mathOpen ? 'true' : undefined}
variant="outlined"
color="secondary"
style={{
textTransform: "none",
width: 100,
@@ -1090,6 +1089,7 @@ const CodeEditor = (props) => {
aria-controls={pythonOpen ? 'basic-menu' : undefined}
aria-expanded={pythonOpen ? 'true' : undefined}
variant="outlined"
color="secondary"
style={{
textTransform: "none",
width: 100,
@@ -1125,6 +1125,7 @@ const CodeEditor = (props) => {
aria-controls={!!menuPosition ? 'basic-menu' : undefined}
aria-expanded={!!menuPosition ? 'true' : undefined}
variant="outlined"
color="secondary"
style={{
textTransform: "none",
width: 130,
@@ -1381,7 +1382,7 @@ const CodeEditor = (props) => {
position: "relative",
}}>
<CodeMirror
value = {localcodedata}
value={localcodedata}
height={isFileEditor ? 450 : 525}
width={isFileEditor ? 650 : 600}
style={{
@@ -1389,6 +1390,8 @@ const CodeEditor = (props) => {
wordBreak: "break-word",
marginTop: 0,
paddingTop: 0,
backgroundColor: "rgba(40,40,40,1)",
minHeight: 470,
}}
onCursorActivity = {(value) => {
// console.log(value.getCursor())
@@ -1398,27 +1401,22 @@ const CodeEditor = (props) => {
findIndex(value.getCursor().line, value.getCursor().ch)
highlight_variables(value)
}}
onChange={(value) => {
//console.log("Value: '", value.getValue(), "'")
onChange={(value, viewUpdate) => {
console.log("Value: ", value, viewUpdate)
setlocalcodedata(value)
expectedOutput(value)
setlocalcodedata(value.getValue())
expectedOutput(value.getValue())
if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){
setEditorPopupOpen(true)
}
// console.log(findIndex(value.getValue()))
// highlight_variables(value)
//if(value.display.input.prevInput.startsWith('$') || value.display.input.prevInput.endsWith('$')){
// setEditorPopupOpen(true)
//}
}}
extensions={[indentWithTab]}
extensions={[]}//indentWithTab]}
theme={shuffleTheme}
options={{
styleSelectedText: true,
theme: codeTheme,
keyMap: 'sublime',
mode: validation === true ? "json" : "python",
lineWrapping: linewrap,
}}
/>
</span>
@@ -1664,34 +1662,29 @@ const CodeEditor = (props) => {
<div style={{display: 'flex',}}>
<button
<Button
style={{
color: "white",
background: "#383b49",
border: "none",
height: 35,
flex: 1,
marginLeft: 5,
marginTop: 5,
cursor: "pointer"
}}
variant="outlined"
color="secondary"
onClick={() => {
setExpansionModalOpen(false);
}}
>
Cancel
</button>
<button
</Button>
<Button
variant="contained"
color="primary"
style={{
color: "white",
background: "#f85a3e",
border: "none",
height: 35,
flex: 1,
marginLeft: 10,
marginTop: 5,
cursor: "pointer"
}}
onClick={(event) => {
// Take localcodedata through the Shuffle JSON parser just in case
@@ -1709,15 +1702,26 @@ const CodeEditor = (props) => {
runUpdateText(fixedcodedata);
setcodedata(fixedcodedata);
setExpansionModalOpen(false)
} else {
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
} else if (changeActionParameterCodeMirror !== undefined) {
//changeActionParameterCodeMirror(event, fieldCount, fixedcodedata)
changeActionParameterCodeMirror(event, fieldCount, fixedcodedata, actionlist)
setExpansionModalOpen(false)
setcodedata(fixedcodedata)
}
// Check if fieldname is set, and try to find and inject the text
if (fieldname !== undefined && fieldname !== null && fieldname.length > 0) {
const foundfield = document.getElementById(fieldname)
if (foundfield !== undefined && foundfield !== null) {
foundfield.value = fixedcodedata
}
}
setExpansionModalOpen(false)
}}
>
Submit
</button>
</Button>
</div>
</Dialog>)
}
+1
View File
@@ -4,6 +4,7 @@ import { createTheme, adaptV4Theme } from "@mui/material/styles";
//const theme = createTheme({
const theme = createTheme(adaptV4Theme({
palette: {
theme: "dark",
main: "#F86743",
primary: {
main: "#F86743",
+129 -5
View File
@@ -16,6 +16,7 @@ import { useAlert } from "react-alert";
import { isMobile } from "react-device-detect"
import aa from 'search-insights'
import Drift from "react-driftjs";
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
import { InstantSearch, Configure, connectSearchBox, connectHits, Index } from 'react-instantsearch-dom';
import algoliasearch from 'algoliasearch/lite';
@@ -516,7 +517,7 @@ const AngularWorkflow = (defaultprops) => {
const [selectionOpen, setSelectionOpen] = React.useState(false);
// eslint-disable-next-line no-unused-vars
const [_, setUpdate] = useState(""); // Used for rendring, don't remove
const [_, setUpdate] = useState(""); // Used to force rendring, don't remove
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0);
@@ -532,7 +533,16 @@ const AngularWorkflow = (defaultprops) => {
"attachedTo": "",
});
// For code editor
const [codeEditorModalOpen, setCodeEditorModalOpen] = React.useState(false);
const [codedata, setcodedata] = React.useState("");
const [editorData, setEditorData] = React.useState({
"name": "",
"value": "",
"field_number": -1,
"actionlist": [],
"field_id": "",
})
// This should all be set once, not on every iteration
// Use states and don't update lol
@@ -12798,8 +12808,11 @@ const AngularWorkflow = (defaultprops) => {
requiresAuthentication={requiresAuthentication}
setLastSaved={setLastSaved}
lastSaved={lastSaved}
aiSubmit={aiSubmit}
expansionModalOpen={codeEditorModalOpen}
setExpansionModalOpen={setCodeEditorModalOpen}
setEditorData={setEditorData}
/>
} else if (Object.getOwnPropertyNames(selectedComment).length > 0) {
@@ -14993,8 +15006,8 @@ const AngularWorkflow = (defaultprops) => {
{/* Looks for triggers" */}
{/* Only fixed the ones that require scrolling on a small screen */}
{/* Most prominent: Actions. But these are a lot more complex */}
{rightSideBarOpen ?
{/* Most important: Actions. But these are a lot more complex */}
{rightSideBarOpen && (selectedTrigger.trigger_type === "SCHEDULE" || selectedTrigger.trigger_type === "WEBHOOK") ?
<div id="rightside_actions" style={rightsidebarStyle}>
{Object.getOwnPropertyNames(selectedTrigger).length > 0 ?
selectedTrigger.trigger_type === "SCHEDULE" ?
@@ -16599,6 +16612,91 @@ const AngularWorkflow = (defaultprops) => {
</div>
</div>
const changeActionParameterCodeMirror = (event, count, data, actionlist) => {
if (data.startsWith("${") && data.endsWith("}")) {
// PARAM FIX - Gonna use the ID field, even though it's a hack
const paramcheck = selectedAction.parameters.find(param => param.name === "body")
if (paramcheck !== undefined) {
// Escapes all double quotes
const toReplace = event.target.value.trim().replaceAll("\\\"", "\"").replaceAll("\"", "\\\"");
console.log("REPLACE WITH: ", toReplace)
if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) {
paramcheck["value_replace"] = [{
"key": data.name,
"value": toReplace,
}]
console.log("IN IF: ", paramcheck)
} else {
const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name)
if (subparamindex === -1) {
paramcheck["value_replace"].push({
"key": data.name,
"value": toReplace,
})
} else {
paramcheck["value_replace"][subparamindex]["value"] = toReplace
}
console.log("IN ELSE: ", paramcheck)
}
if (paramcheck["value_replace"] === undefined) {
selectedAction.parameters[count]["value_replace"] = paramcheck
} else {
//selectedActionParameters[count]["value_replace"] = paramcheck["value_replace"]
selectedAction.parameters[count]["value_replace"] = paramcheck["value_replace"]
}
setSelectedAction(selectedAction)
//setUpdate(Math.random())
return
}
}
if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) {
console.log("GET THE LAST ARGUMENT FOR NODE!")
var curstring = ""
var record = false
for (let [key,keyval] in Object.entries(selectedAction.parameters[count].value)) {
const item = selectedAction.parameters[count].value[key]
if (record) {
curstring += item
}
if (item === "$") {
record = true
curstring = ""
}
}
//console.log("CURSTRING: ", curstring)
if (curstring.length > 0 && actionlist !== null) {
// Search back in the action list
curstring = curstring.split(" ").join("_").toLowerCase()
var actionItem = actionlist.find(data => data.autocomplete.split(" ").join("_").toLowerCase() === curstring)
if (actionItem !== undefined) {
console.log("Found item: ", actionItem)
var jsonvalid = true
try {
const tmp = String(JSON.parse(actionItem.example))
if (!actionItem.example.includes("{") && !actionItem.example.includes("[")) {
jsonvalid = false
}
} catch (e) {
jsonvalid = false
}
}
}
}
selectedAction.parameters[count].autocompleted = false
selectedAction.parameters[count].value = data
setSelectedAction(selectedAction)
//setUpdate(Math.random())
}
const loadedCheck =
isLoaded && workflowDone ? (
@@ -16614,6 +16712,31 @@ const AngularWorkflow = (defaultprops) => {
{workflowRevisions}
<SuggestionBoxUi />
{codeEditorModalOpen ?
<ShuffleCodeEditor
expansionModalOpen={codeEditorModalOpen}
setExpansionModalOpen={setCodeEditorModalOpen}
isCloud={isCloud}
globalUrl={globalUrl}
workflowExecutions={workflowExecutions}
getParents={getParents}
selectedAction={selectedAction}
aiSubmit={aiSubmit}
toolsAppId={toolsApp.id}
codedata={editorData.value}
setcodedata={setcodedata}
// Not working out of the box
parameterName={editorData.name}
fieldCount={editorData.field_number}
actionlist={editorData.actionlist}
fieldname={editorData.field_id}
changeActionParameterCodeMirror={changeActionParameterCodeMirror}
/>
: null}
{editWorkflowModalOpen === true ?
<EditWorkflow
workflow={workflow}
@@ -16740,6 +16863,7 @@ const AngularWorkflow = (defaultprops) => {
<Prompt when={!lastSaved} message={unloadText} />
*/}
{loadedCheck}
</div>
);
};