From 757775edaa51368f34ef81017066d1ec8b0658e3 Mon Sep 17 00:00:00 2001 From: Kiran Ashok Date: Fri, 5 Apr 2024 16:08:32 +0530 Subject: [PATCH] Bugfixes :: Codehinter (#9306) * fix : If the query panel is small in size the suggestions are not clearly visible. * fix : Not able to popout the runjs query editor field if it has larger data. * fix : scrolling inside the codehinter should only scroll within codehinter (query panel) * fix : If runjs returns string it is parsing as an array on codehinter * fix : align placeholders of inputfields to vertically center of the field. * fix : Code hinter says something went wrong for ds and when focusing and clicking out without typing * fixes * update : resolve multi reference null check * remove braces from fx preview box * suggestion for fx in with curly brace * fix : We should be able to save empty fields, currently it is not possible. * resolve fx codehinter in preview * remove all fxActive checks and fix codehinter suggestion position * CLEANUP * fix :: to fix parameter suggestion UI/UX/ + on mouse click data is not saving , eventmanager, add paramater --- frontend/src/Editor/CodeEditor/CodeHinter.jsx | 3 +- frontend/src/Editor/CodeEditor/PreviewBox.jsx | 23 +++----------- .../CodeEditor/SingleLineCodeEditor.jsx | 31 +++++++------------ .../CodeEditor/autocompleteExtensionConfig.js | 9 +++--- frontend/src/Editor/CodeEditor/styles.scss | 27 ++++++++++++++-- frontend/src/Editor/CodeEditor/utils.js | 13 ++++---- .../src/Editor/Inspector/EventManager.jsx | 4 +-- .../Components/ParameterDetails.jsx | 8 ++--- frontend/src/_helpers/appUtils.js | 8 +++++ frontend/src/_stores/resolverStore.js | 4 +-- frontend/src/_stores/utils.js | 7 ++++- 11 files changed, 73 insertions(+), 64 deletions(-) diff --git a/frontend/src/Editor/CodeEditor/CodeHinter.jsx b/frontend/src/Editor/CodeEditor/CodeHinter.jsx index 70453a4895..39d615384c 100644 --- a/frontend/src/Editor/CodeEditor/CodeHinter.jsx +++ b/frontend/src/Editor/CodeEditor/CodeHinter.jsx @@ -88,8 +88,7 @@ const Portal = ({ children, ...restProps }) => { const PopupIcon = ({ callback, icon, tip, position, isMultiEditor = false }) => { const size = 16; const topRef = isNumber(position?.height) ? Math.floor(position?.height) - 30 : 32; - let top = isMultiEditor ? 370 : topRef > 32 ? topRef : 0; - + let top = isMultiEditor ? 270 : topRef > 32 ? topRef : 0; return (
{ +export const PreviewBox = ({ currentValue, validationSchema, setErrorStateActive, componentId, setErrorMessage }) => { const { variablesExposedForPreview } = useContext(EditorContext); const customVariables = variablesExposedForPreview?.[componentId] ?? {}; @@ -27,10 +20,8 @@ export const PreviewBox = ({ const [resolvedValue, setResolvedValue] = useState(''); const [error, setError] = useState(null); const [coersionData, setCoersionData] = useState(null); - const getPreviewContent = (content, type) => { if (!content) return currentValue; - try { switch (type) { case 'Object': @@ -75,12 +66,7 @@ export const PreviewBox = ({ }, [error]); useEffect(() => { - const [valid, _error, newValue, resolvedValue] = resolveReferences( - currentValue, - validationSchema, - customVariables, - fxActive - ); + const [valid, _error, newValue, resolvedValue] = resolveReferences(currentValue, validationSchema, customVariables); if (!validationSchema || isEmpty(validationSchema)) { return setResolvedValue(newValue); @@ -345,7 +331,6 @@ const PreviewCodeBlock = ({ code, isExpectValue = false }) => {
); } - return (
 {
           padding: '0',
         }}
       >
-        {prettyPrintedJson}
+        {prettyPrintedJson?.startsWith('{{')
+          ? prettyPrintedJson?.replace(/{{/g, '').replace(/}}/g, '')
+          : prettyPrintedJson}
       
); diff --git a/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx b/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx index 2c3c9403ef..0b620851be 100644 --- a/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx +++ b/frontend/src/Editor/CodeEditor/SingleLineCodeEditor.jsx @@ -34,15 +34,9 @@ const SingleLineCodeEditor = ({ suggestions, componentName, fieldMeta = {}, fxAc useEffect(() => { if (typeof initialValue !== 'string') return; - - if (fxActive && initialValue?.startsWith('{{')) { - const _value = initialValue?.replace(/{{/g, '').replace(/}}/g, ''); - return setCurrentValue(_value); - } - setCurrentValue(initialValue); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [componentName, initialValue, fxActive]); + }, [componentName, initialValue]); useEffect(() => { const handleClickOutside = (event) => { @@ -80,7 +74,7 @@ const SingleLineCodeEditor = ({ suggestions, componentName, fieldMeta = {}, fxAc setErrorStateActive={setErrorStateActive} ignoreValidation={restProps?.ignoreValidation || isEmpty(validation)} componentId={restProps?.componentId ?? null} - fxActive={fxActive} + // fxActive={fxActive} isWorkspaceVariable={isWorkspaceVariable} errorStateActive={errorStateActive} previewPlacement={restProps?.cyLabel === 'canvas-bg-colour' ? 'top' : 'left-start'} @@ -99,7 +93,6 @@ const SingleLineCodeEditor = ({ suggestions, componentName, fieldMeta = {}, fxAc cyLabel={restProps.cyLabel} portalProps={portalProps} componentName={componentName} - fxActive={fxActive} {...restProps} /> @@ -124,7 +117,6 @@ const EditorInput = ({ renderPreview, portalProps, ignoreValidation, - fxActive, lang, isFocused, componentId, @@ -138,15 +130,15 @@ const EditorInput = ({ if (totalReferences > 1) { const currentWord = queryInput.split('{{').pop().split('}}')[0]; - queryInput = fxActive ? currentWord : `{{${currentWord}}}`; + queryInput = currentWord; } - let completions = getAutocompletion(queryInput, validationType, hints, fxActive, totalReferences); + let completions = getAutocompletion(queryInput, validationType, hints, totalReferences); return { from: word.from, options: completions, - validFor: !fxActive ? /^\{\{.*\}\}$/ : '', + validFor: /^\{\{.*\}\}$/, }; } @@ -168,18 +160,17 @@ const EditorInput = ({ setCurrentValue(val); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const handleOnBlur = React.useCallback(() => { setFirstTimeFocus(false); if (ignoreValidation) { return onBlurUpdate(currentValue); } - - if (!error) { - const _value = fxActive ? `{{${currentValue}}}` : currentValue; - - onBlurUpdate(_value); - } + setTimeout(() => { + if (!error || currentValue == '') { + const _value = currentValue; + onBlurUpdate(_value); + } + }, 0); // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentValue, error]); diff --git a/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js b/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js index d06c49a1bf..8d034b9139 100644 --- a/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js +++ b/frontend/src/Editor/CodeEditor/autocompleteExtensionConfig.js @@ -1,8 +1,7 @@ -export const getAutocompletion = (input, fieldType, hints, fxActive = false, totalReferences = 1) => { - if (!fxActive && (!input.startsWith('{{') || !input.endsWith('}}'))) return []; - - const actualInput = !fxActive ? input.replace(/{{|}}/g, '') : input; +export const getAutocompletion = (input, fieldType, hints, totalReferences = 1) => { + if (!input.startsWith('{{') || !input.endsWith('}}')) return []; + const actualInput = input.replace(/{{|}}/g, ''); let JSLangHints = []; if (fieldType) { @@ -50,7 +49,7 @@ export const getAutocompletion = (input, fieldType, hints, fxActive = false, tot if (autoSuggestionList.length === 0 && !cm.hint.includes(actualInput)) return true; }); - const suggestions = generateHints([...jsHints, ...autoSuggestionList], fxActive, totalReferences); + const suggestions = generateHints([...jsHints, ...autoSuggestionList], totalReferences); return orderSuggestions(suggestions, fieldType).map((cm, index) => ({ ...cm, boost: 100 - index })); }; diff --git a/frontend/src/Editor/CodeEditor/styles.scss b/frontend/src/Editor/CodeEditor/styles.scss index 3acc3a490c..f032ee08b0 100644 --- a/frontend/src/Editor/CodeEditor/styles.scss +++ b/frontend/src/Editor/CodeEditor/styles.scss @@ -24,6 +24,10 @@ } } +.cm-widgetBuffer { + display: none !important; +} + .cm-base-autocomplete { @@ -153,18 +157,35 @@ } } +.query-manager-sort-filter-popup { + .cm-base-autocomplete { + position: fixed !important; + top: 130px !important; + } +} +.canvas-codehinter-container { + .cm-base-autocomplete { + position: fixed !important; + top: 500px !important; + left: 38px !important; + } +} .widget-code-editor { height: 100%; .cm-content { - max-width: 220px !important; + max-width: 100% !important; white-space: pre-wrap; word-wrap: break-word; } } +.cm-placeholder { + width: 220px; +} + .code-hinter-wrapper { .cm-editor { min-height: 32px; @@ -224,6 +245,7 @@ .cm-tooltip-autocomplete { @extend .cm-base-autocomplete; + top: 0px !important; } } @@ -277,6 +299,7 @@ overflow-y: auto !important; overflow-x: hidden !important; padding: 2px !important; + overscroll-behavior: contain; } .cm-focused { @@ -315,8 +338,6 @@ .code-hinter-preview-card-body::-webkit-scrollbar { width: 4px !important; - /* for vertical scrollbars */ - ; } .code-hinter-preview-card-body::-webkit-scrollbar-track { diff --git a/frontend/src/Editor/CodeEditor/utils.js b/frontend/src/Editor/CodeEditor/utils.js index 7f06064f21..22dc1eef40 100644 --- a/frontend/src/Editor/CodeEditor/utils.js +++ b/frontend/src/Editor/CodeEditor/utils.js @@ -211,9 +211,8 @@ const resolveMultiDynamicReferences = (code, lookupTable) => { return resolvedValue; }; -export const resolveReferences = (query, validationSchema, customResolvers = {}, fxActive = false) => { +export const resolveReferences = (query, validationSchema, customResolvers = {}) => { if (!query || typeof query !== 'string') return [false, null, null]; - let resolvedValue = query; let error = null; @@ -226,20 +225,20 @@ export const resolveReferences = (query, validationSchema, customResolvers = {}, return [true, error, resolvedValue]; } - if (validationSchema && !fxActive && !query?.includes('{{') && !query?.includes('}}')) { + if (validationSchema && !query?.includes('{{') && !query?.includes('}}')) { const [valid, errors, newValue] = validateComponentProperty(query, validationSchema); return [valid, errors, newValue, resolvedValue]; } - const hasMultiDynamicVariables = getDynamicVariables(query); + const hasMultiDynamicVariables = getDynamicVariables(query)?.length > 1; const { lookupTable } = useResolveStore.getState(); - if (isEmpty(validationSchema) && hasMultiDynamicVariables) { + if (hasMultiDynamicVariables) { resolvedValue = resolveMultiDynamicReferences(query, lookupTable); } else { - let value = !fxActive ? query?.replace(/{{|}}/g, '').trim() : query; + let value = query?.replace(/{{|}}/g, '').trim(); - if (fxActive && (value.startsWith('#') || value.includes('table-'))) { + if (value.startsWith('#') || value.includes('table-')) { value = JSON.stringify(value); } const { toResolveReference, jsExpression, jsExpMatch } = inferJSExpAndReferences(value, lookupTable.hints); diff --git a/frontend/src/Editor/Inspector/EventManager.jsx b/frontend/src/Editor/Inspector/EventManager.jsx index ae2bd7ac77..b723f17180 100644 --- a/frontend/src/Editor/Inspector/EventManager.jsx +++ b/frontend/src/Editor/Inspector/EventManager.jsx @@ -108,10 +108,10 @@ export const EventManager = ({ return { name: action.name, value: action.id }; }); - let checkIfClicksAreInsideOf = document.querySelector('#cm-complete-0'); + let checkIfClicksAreInsideOf = document.querySelector('.cm-completionListIncompleteBottom'); // Listen for click events on body if (checkIfClicksAreInsideOf) { - document.body.addEventListener('click', function (event) { + document.body.addEventListener('mousedown', function (event) { if (checkIfClicksAreInsideOf.contains(event.target)) { event.stopPropagation(); } diff --git a/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx b/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx index 528cc480f9..a92919eaca 100644 --- a/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx +++ b/frontend/src/Editor/QueryManager/Components/ParameterDetails.jsx @@ -21,19 +21,19 @@ const ParameterDetails = ({ darkMode, onSubmit, isEdit, name, defaultValue, onRe if ( showModal && event.target.closest('#parameter-form-popover') === null && - event.target.closest('#cm-complete-0') === null + event.target.closest('.cm-completionListIncompleteBottom') === null ) { closeMenu(); } }; if (showModal) { - document.addEventListener('mouseup', handleClickOutside); + document.addEventListener('click', handleClickOutside); } else { - document.removeEventListener('mouseup', handleClickOutside); + document.removeEventListener('click', handleClickOutside); } return () => { - document.removeEventListener('mouseup', handleClickOutside); + document.removeEventListener('click', handleClickOutside); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [showModal]); diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index f6c185c1e3..beebecae5a 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -604,6 +604,9 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { const value = resolveReferences(event.value, getCurrentState(), undefined, customVariables); const customAppVariables = { ...getCurrentState().variables }; customAppVariables[key] = value; + useResolveStore.getState().actions.addAppSuggestions({ + variables: customAppVariables, + }); return useCurrentStateStore.getState().actions.setCurrentState({ variables: customAppVariables, }); @@ -619,6 +622,7 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables); const customAppVariables = { ...getCurrentState().variables }; delete customAppVariables[key]; + // useResolveStore.getState().actions.removeAppSuggestions(key); return useCurrentStateStore.getState().actions.setCurrentState({ variables: customAppVariables, }); @@ -631,6 +635,9 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { ...getCurrentState().page.variables, [key]: value, }; + useResolveStore.getState().actions.addAppSuggestions({ + variables: customPageVariables, + }); return useCurrentStateStore.getState().actions.setCurrentState({ page: { ...getCurrentState().page, @@ -650,6 +657,7 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { case 'unset-page-variable': { const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables); const customPageVariables = _.omit(getCurrentState().page.variables, key); + // useResolveStore.getState().actions.removeAppSuggestions(key); return useCurrentStateStore.getState().actions.setCurrentState({ page: { ...getCurrentState().page, diff --git a/frontend/src/_stores/resolverStore.js b/frontend/src/_stores/resolverStore.js index 48c6b94fcd..4fc70a1d5b 100644 --- a/frontend/src/_stores/resolverStore.js +++ b/frontend/src/_stores/resolverStore.js @@ -136,13 +136,13 @@ export const useResolveStore = create( }, removeAppSuggestions: (suggestionsArray) => { - if (suggestionsArray.length === 0) return new Promise((resolve) => resolve({ status: '' })); + if (suggestionsArray?.length === 0) return new Promise((resolve) => resolve({ status: '' })); const lookupHintsMap = new Map([...get().lookupTable.hints]); const lookupResolvedRefs = new Map([...get().lookupTable.resolvedRefs]); const currentSuggestions = get().suggestions.appHints; - suggestionsArray.forEach((suggestion) => { + suggestionsArray?.forEach((suggestion) => { const index = currentSuggestions.findIndex((s) => s.hint === suggestion); if (index === -1) return; diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js index e7d2df97e4..07899b3341 100644 --- a/frontend/src/_stores/utils.js +++ b/frontend/src/_stores/utils.js @@ -505,7 +505,12 @@ export function findAllEntityReferences(node, allRefs) { if (typeof node === 'object') { for (let key in node) { const value = node[key]; - if (typeof value === 'string' && value.includes('{{') && value.includes('}}')) { + if ( + typeof value === 'string' && + value.includes('{{') && + value.includes('}}') && + (value.startsWith('{{components') || value.startsWith('queries')) + ) { const referenceExists = value; if (referenceExists) {