diff --git a/frontend/src/AppBuilder/Header/EditorHeader.jsx b/frontend/src/AppBuilder/Header/EditorHeader.jsx index 275a2f84c0..d5ac4b767d 100644 --- a/frontend/src/AppBuilder/Header/EditorHeader.jsx +++ b/frontend/src/AppBuilder/Header/EditorHeader.jsx @@ -6,11 +6,11 @@ import LogoNavDropdown from '@/_components/LogoNavDropdown'; import HeaderActions from './HeaderActions'; import { AppVersionsManager } from './AppVersionsManager'; import RealtimeAvatars from '@/Editor/RealtimeAvatars'; -// import UpdatePresence from '@/Editor/Header/UpdatePresence'; import SolidIcon from '@/_ui/Icon/SolidIcons'; import useStore from '@/AppBuilder/_stores/store'; import RightTopHeaderButtons from './RightTopHeaderButtons/RightTopHeaderButtons'; import BuildSuggestions from './BuildSuggestions'; +import UpdatePresenceMultiPlayer from './UpdatePresenceMultiPlayer'; export const EditorHeader = ({ darkMode }) => { const { isSaving, saveError, isVersionReleased } = useStore( @@ -98,18 +98,14 @@ export const EditorHeader = ({ darkMode }) => { )} - {/* {shouldEnableMultiplayer && } */} + {shouldEnableMultiplayer && }
{/*
*/}
-
- {/* {editingVersion && ( */} - {/* */} - {/* )} */} - {/*
*/} +
diff --git a/frontend/src/AppBuilder/Header/RightTopHeaderButtons/ManageAppUsers copy.jsx b/frontend/src/AppBuilder/Header/RightTopHeaderButtons/ManageAppUsers copy.jsx deleted file mode 100644 index 89f5e90352..0000000000 --- a/frontend/src/AppBuilder/Header/RightTopHeaderButtons/ManageAppUsers copy.jsx +++ /dev/null @@ -1,416 +0,0 @@ -import React from 'react'; -import { appService, appsService, authenticationService } from '@/_services'; -import Modal from 'react-bootstrap/Modal'; -import { toast } from 'react-hot-toast'; -import { CopyToClipboard } from 'react-copy-to-clipboard'; -import _, { debounce } from 'lodash'; -import { validateName } from '@/_helpers/utils'; -import { withTranslation } from 'react-i18next'; -import { Link } from 'react-router-dom'; -import { getPrivateRoute, replaceEditorURL, getHostURL } from '@/_helpers/routes'; -import { ToolTip } from '@/_components/ToolTip'; -import SolidIcon from '@/_ui/Icon/SolidIcons'; -import cx from 'classnames'; -import { TOOLTIP_MESSAGES } from '@/_helpers/constants'; -import { useAppDataStore } from '@/_stores/appDataStore'; -import { retrieveWhiteLabelText } from '@white-label/whiteLabelling'; -import useStore from '@/AppBuilder/_stores/store'; - -class ManageAppUsersComponent extends React.Component { - constructor(props) { - super(props); - this.isUserAdmin = authenticationService.currentSessionValue?.admin; - this.whiteLabelText = retrieveWhiteLabelText(); - - this.state = { - showModal: false, - appId: null, - isSlugVerificationInProgress: false, - addingUser: false, - newUser: {}, - newSlug: { - value: null, - error: '', - }, - isSlugUpdated: false, - }; - } - - /* - Only will fail for existed apps before the app/workspace url revamp which has - special chars or spaces in their app slugs - */ - validateThePreExistingSlugs = () => { - const existedSlugErrors = validateName(this.props.slug, 'App slug', true, false, false, false); - this.setState({ - newSlug: { - value: this.props.slug, - error: existedSlugErrors.errorMsg, - }, - }); - }; - - componentDidMount() { - const appId = this.props.appId; - this.setState({ appId }); - } - - hideModal = () => { - this.setState({ - showModal: false, - newSlug: { - value: this.props.slug, - error: '', - }, - isSlugVerificationInProgress: false, - isSlugUpdated: false, - }); - }; - - addUser = () => { - this.setState({ - addingUser: true, - }); - - const { organizationUserId, role } = this.state.newUser; - - appService - .createAppUser(this.state.appId, organizationUserId, role) - .then(() => { - this.setState({ addingUser: false, newUser: {} }); - toast.success('Added user successfully'); - }) - .catch(({ error }) => { - this.setState({ addingUser: false }); - toast.error(error); - }); - }; - - toggleAppVisibility = () => { - const newState = !this.props.isPublic; - this.setState({ - ischangingVisibility: true, - }); - useStore.getState().setIsPublic(newState); - - // eslint-disable-next-line no-unused-vars - appsService - .setVisibility(this.state.appId, newState) - .then(() => { - this.setState({ - ischangingVisibility: false, - }); - - if (newState) { - toast('Application is now public.'); - } else { - toast('Application visibility set to private'); - } - }) - .catch((error) => { - this.setState({ - ischangingVisibility: false, - }); - toast.error(error); - }); - }; - - delayedSlugChange = debounce((e) => { - this.handleInputChange(e.target.value, 'slug'); - }, 500); - - handleInputChange = (value, field) => { - this.setState({ - newSlug: { - value: this.state.newSlug?.value, - error: '', - isSlugUpdated: false, - }, - }); - - const error = validateName(value, `App ${field}`, true, false, !(field === 'slug'), !(field === 'slug')); - - if (!_.isEmpty(value) && value !== this.props.slug && _.isEmpty(error.errorMsg)) { - this.setState({ - isSlugVerificationInProgress: true, - }); - appsService - .setSlug(this.state.appId, value) - .then(() => { - this.setState({ - newSlug: { - value: value, - error: '', - }, - isSlugVerificationInProgress: false, - isSlugUpdated: true, - }); - - replaceEditorURL(value, this.props.pageHandle); - useStore.getState().setSlug(value); - }) - .catch(({ error }) => { - this.setState({ - newSlug: { - value, - error, - }, - isSlugVerificationInProgress: false, - isSlugUpdated: false, - }); - }); - } else { - this.setState({ - newSlug: { - value, - error: error?.errorMsg, - }, - isSlugVerificationInProgress: false, - isSlugUpdated: false, - }); - } - }; - - render() { - const { appId, isSlugVerificationInProgress, newSlug, isSlugUpdated } = this.state; - - const appLink = `${getHostURL()}/applications/`; - const shareableLink = appLink + (this.props.slug || appId); - const slugButtonClass = !_.isEmpty(newSlug.error) ? 'is-invalid' : 'is-valid'; - const embeddableLink = ``; - - const shouldShowShareModal = this.props.isVersionReleased - ? this.props.multiEnvironmentEnabled - ? this.props.currentEnvironment?.isDefault - ? true - : false - : this.props.currentEnvironment?.priority === 1 - : false; - - const envTooltipFlag = - (!this.props.isVersionReleased && this.props.currentEnvironment?.isDefault) || - (!this.props.multiEnvironmentEnabled && this.props.currentEnvironment?.priority === 1); - - return ( - -
- { - this.validateThePreExistingSlugs(); - shouldShowShareModal && this.setState({ showModal: true }); - }} - > - - - - - {this.props.t('editor.share', 'Share')} - - - - - - { - - } - - - - {this.isUserAdmin && ( - - Manage users - - )} - - -
-
- ); - } -} - -export const ManageAppUsers = withTranslation()(ManageAppUsersComponent); diff --git a/frontend/src/AppBuilder/Header/UpdatePresenceMultiPlayer.jsx b/frontend/src/AppBuilder/Header/UpdatePresenceMultiPlayer.jsx new file mode 100644 index 0000000000..dfb3225231 --- /dev/null +++ b/frontend/src/AppBuilder/Header/UpdatePresenceMultiPlayer.jsx @@ -0,0 +1,33 @@ +import React, { useEffect } from 'react'; +// eslint-disable-next-line import/no-unresolved +import { useUpdatePresence } from '@y-presence/react'; +import useStore from '@/AppBuilder/_stores/store'; +import { shallow } from 'zustand/shallow'; + +export default function UpdatePresenceMultiPlayer() { + const { user } = useStore( + (state) => ({ + user: state.user, + }), + shallow + ); + const updatePresence = useUpdatePresence(); + + useEffect(() => { + if (user) { + const initialPresence = { + firstName: user.firstName ?? '', + lastName: user.lastName ?? '', + email: user.email ?? '', + image: '', + editingVersionId: '', + x: 0, + y: 0, + color: '', + }; + updatePresence(initialPresence); + } + }, [user, updatePresence]); + + return <>; +} diff --git a/frontend/src/AppBuilder/Viewer/PreviewSettings.jsx b/frontend/src/AppBuilder/Viewer/PreviewSettings.jsx index 2a8d6c66e5..644fe7348f 100644 --- a/frontend/src/AppBuilder/Viewer/PreviewSettings.jsx +++ b/frontend/src/AppBuilder/Viewer/PreviewSettings.jsx @@ -33,7 +33,6 @@ const PreviewSettings = ({ isMobileLayout, showHeader, darkMode }) => { <>
- {/* */} )} @@ -78,8 +77,6 @@ const PreviewSettings = ({ isMobileLayout, showHeader, darkMode }) => { {previewNavbar && ( - {/* */} -
diff --git a/frontend/src/AppBuilder/Viewer/Viewer.jsx b/frontend/src/AppBuilder/Viewer/Viewer.jsx index 8bd548fe88..18d01867ef 100644 --- a/frontend/src/AppBuilder/Viewer/Viewer.jsx +++ b/frontend/src/AppBuilder/Viewer/Viewer.jsx @@ -97,10 +97,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod const switchPage = useStore((state) => state.switchPage); const showHeader = !globalSettings?.hideHeader && isAppLoaded; - // ---remove - const handleAppEnvironmentChanged = useCallback((environment) => { - console.log('setAppVersionCurrentEnvironment', environment); - }, []); useEffect(() => { updateCanvasHeight(currentPageComponents); @@ -151,7 +147,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod pages={pages} currentPageId={currentPageId ?? homePageId} showViewerNavigation={!isPagesSidebarHidden} - handleAppEnvironmentChanged={handleAppEnvironmentChanged} changeToDarkMode={changeToDarkMode} /> )} @@ -163,7 +158,6 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod pages={pages} currentPageId={currentPageId ?? homePageId} showViewerNavigation={!isPagesSidebarHidden} - handleAppEnvironmentChanged={handleAppEnvironmentChanged} changeToDarkMode={changeToDarkMode} /> )} @@ -216,14 +210,13 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod pages={pages} currentPageId={currentPageId ?? homePageId} showViewerNavigation={!isPagesSidebarHidden} - handleAppEnvironmentChanged={handleAppEnvironmentChanged} switchPage={switchPage} changeToDarkMode={changeToDarkMode} /> )}
- {isAppLoaded && } + {isMobilePreviewMode &&
} {isMobilePreviewMode &&
} diff --git a/frontend/src/AppBuilder/_hooks/useAppData.js b/frontend/src/AppBuilder/_hooks/useAppData.js index cdec282c31..604339f9e1 100644 --- a/frontend/src/AppBuilder/_hooks/useAppData.js +++ b/frontend/src/AppBuilder/_hooks/useAppData.js @@ -163,7 +163,7 @@ const useAppData = (appId, moduleId, mode = 'edit', { environmentId, versionId } } const constantsResp = isPublicAccess ? await orgEnvironmentConstantService.getConstantsFromPublicApp(slug) - : await orgEnvironmentConstantService.getConstantsFromEnvironment(editorEnvironmentId); + : await orgEnvironmentConstantService.getConstantsFromApp(editorEnvironmentId); const pages = appData.pages.map((page) => { return page; diff --git a/frontend/src/Editor/Components/BoundedBox/BoundedBox.jsx b/frontend/src/Editor/Components/BoundedBox/BoundedBox.jsx index ac15814252..b44c19483f 100644 --- a/frontend/src/Editor/Components/BoundedBox/BoundedBox.jsx +++ b/frontend/src/Editor/Components/BoundedBox/BoundedBox.jsx @@ -26,7 +26,7 @@ export const BoundedBox = ({ properties, fireEvent, darkMode, setExposedVariable useEffect(() => { const handleImageLoad = () => { - const wrapperElement = document.querySelector(`.widget-${id} .lmGPCf`); + const wrapperElement = document.querySelector(`[widgetid="${id}"] .lmGPCf`); if (wrapperElement) { const { width, height } = wrapperElement.getBoundingClientRect(); // Use the width and height of bounding image for further calculations @@ -35,7 +35,7 @@ export const BoundedBox = ({ properties, fireEvent, darkMode, setExposedVariable } }; - const imageElement = document.querySelector(`.widget-${id} .gVmiLs`); + const imageElement = document.querySelector(`[widgetid="${id}"] .gVmiLs`); if (imageElement) { imageElement.addEventListener('load', handleImageLoad); } diff --git a/frontend/src/Editor/Components/Button.jsx b/frontend/src/Editor/Components/Button.jsx index ab32b219f3..c0d4716edd 100644 --- a/frontend/src/Editor/Components/Button.jsx +++ b/frontend/src/Editor/Components/Button.jsx @@ -95,7 +95,7 @@ export const Button = function Button(props) { useEffect(() => { const exposedVariables = { click: async function () { - if (!disable) { + if (!disable && !loading) { fireEvent('onClick'); } }, @@ -170,9 +170,11 @@ export const Button = function Button(props) { computedStyles['--tblr-btn-color-clicked'] = tinycolor(computedBgColor).darken(15).toString(); } const handleClick = () => { - const event1 = new CustomEvent('submitForm', { detail: { buttonComponentId: id } }); - document.dispatchEvent(event1); - fireEvent('onClick'); + if (!disable && !loading) { + const event1 = new CustomEvent('submitForm', { detail: { buttonComponentId: id } }); + document.dispatchEvent(event1); + fireEvent('onClick'); + } }; const renderButton = () => (
{ @@ -51,7 +53,13 @@ export const Chart = function Chart({ const jsonData = typeof jsonDescription === 'object' ? JSON.stringify(jsonDescription) : jsonDescription; - const isDescriptionJson = plotFromJson ? isJson(jsonData) : false; + let isDescriptionJson = false; + if (plotFromJson) { + isDescriptionJson = isStringValidJson(jsonData); + if (!isDescriptionJson) { + console.log('Throw error'); + } + } const jsonChartData = isDescriptionJson ? JSON.parse(jsonData).data : []; @@ -67,6 +75,7 @@ export const Chart = function Chart({ const chartTitle = plotFromJson ? chartLayout?.title ?? title : title; useEffect(() => { + if (isInitialRender.current) return; const { xaxis, yaxis } = chartLayout; let xAxisTitle, yAxisTitle; if (xaxis) { @@ -172,7 +181,7 @@ export const Chart = function Chart({ ); const handleClick = useCallback((data) => { - if (data.length > 0) { + if (!disabledState && data.length > 0) { const { x: xAxisLabel, y: yAxisLabel, @@ -194,13 +203,32 @@ export const Chart = function Chart({ }, []); const handleDoubleClick = useCallback(() => { - fireEvent('onDoubleClick'); + if (!disabledState) { + fireEvent('onDoubleClick'); + } }, []); useEffect(() => { - setExposedVariable('clearClickedPoint', () => { - setExposedVariable('clickedDataPoint', {}); - }); + const { xaxis, yaxis } = chartLayout; + let xAxisTitle, yAxisTitle; + if (xaxis) { + xAxisTitle = xaxis?.title?.text; + } + if (yaxis) { + yAxisTitle = yaxis?.title?.text; + } + const exposedVariables = { + chartTitle: chartTitle, + xAxisTitle: xAxisTitle, + yAxisTitle: yAxisTitle, + clearClickedPoint: () => { + setExposedVariable('clickedDataPoint', {}); + }, + }; + + setExposedVariables(exposedVariables); + isInitialRender.current = false; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -221,6 +249,7 @@ export const Chart = function Chart({ }} onClick={handleClick} onDoubleClick={handleDoubleClick} + disabledState={disabledState} /> )}
@@ -229,17 +258,17 @@ export const Chart = function Chart({ // onClick event was not working when the component is re-rendered for every click. Hance, memoization is used const PlotComponent = memo( - ({ data, layout, config, onClick, onDoubleClick }) => { + ({ data, layout, config, onClick, onDoubleClick, disabledState }) => { return ( { - onClick(e.points); + if (!disabledState) onClick(e.points); }} onDoubleClick={() => { - onDoubleClick(); + if (!disabledState) onDoubleClick(); }} /> ); diff --git a/frontend/src/Editor/Components/Checkbox.jsx b/frontend/src/Editor/Components/Checkbox.jsx index bce29bad1f..5b0021114c 100644 --- a/frontend/src/Editor/Components/Checkbox.jsx +++ b/frontend/src/Editor/Components/Checkbox.jsx @@ -1,5 +1,4 @@ -import React, { useEffect, useState } from 'react'; -import { resolveWidgetFieldValue } from '@/_helpers/utils'; +import React, { useEffect, useRef, useState } from 'react'; import Loader from '@/ToolJetUI/Loader/Loader'; import OverflowTooltip from '@/_components/OverflowTooltip'; @@ -8,21 +7,26 @@ export const Checkbox = ({ properties, styles, fireEvent, + componentName, setExposedVariable, setExposedVariables, + validation, dataCy, - component, validate, width, }) => { + const isInitialRender = useRef(true); const defaultValueFromProperties = properties.defaultValue ?? false; + const isMandatory = validation?.mandatory ?? false; const [defaultValue, setDefaultValue] = useState(defaultValueFromProperties); const [checked, setChecked] = useState(defaultValueFromProperties); const [value, setValue] = React.useState(defaultValueFromProperties); const [userInteracted, setUserInteracted] = useState(false); const { label } = properties; - const textColor = styles.textColor === '#1B1F24' ? 'var(--text-primary)' : styles.textColor; + const textColor = ['#1B1F24', '#000', '#000000ff'].includes(styles.textColor) + ? 'var(--text-primary)' + : styles.textColor; const { loadingState, disabledState } = properties; const { checkboxColor, boxShadow, alignment, uncheckedColor, borderColor, handleColor } = styles; @@ -31,8 +35,6 @@ export const Checkbox = ({ const [visibility, setVisibility] = useState(properties.visibility); const { isValid, validationError } = validate(checked); - const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value); - const toggleValue = (e) => { const isChecked = e.target.checked; setChecked(isChecked); @@ -48,28 +50,11 @@ export const Checkbox = ({ }; useEffect(() => { - const setCheckedAndNotify = async (status) => { - await setExposedVariable('value', status); - if (status) { - fireEvent('onCheck'); - } else { - fireEvent('onUnCheck'); - } - setChecked(status); - setValue(status); - }; - - const exposedVariables = { - value: defaultValueFromProperties, - setChecked: setCheckedAndNotify, - setValue: setCheckedAndNotify, - }; - + if (isInitialRender.current) return; setDefaultValue(defaultValueFromProperties); setChecked(defaultValueFromProperties); setValue(defaultValueFromProperties); - setExposedVariables(exposedVariables); - + setExposedVariable('value', defaultValueFromProperties); // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultValueFromProperties]); @@ -89,79 +74,108 @@ export const Checkbox = ({ }, [loadingState]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('label', label); // eslint-disable-next-line react-hooks/exhaustive-deps }, [label]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isMandatory', isMandatory); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMandatory]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isLoading', loading); // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isVisible', visibility); // eslint-disable-next-line react-hooks/exhaustive-deps }, [visibility]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isDisabled', disable); // eslint-disable-next-line react-hooks/exhaustive-deps }, [disable]); + useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isValid', isValid); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isValid]); - useEffect(() => { - setExposedVariable('setLoading', async function (loading) { - setLoading(loading); - setExposedVariable('isLoading', loading); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.loadingState]); useEffect(() => { - setExposedVariable('setVisibility', async function (state) { - setVisibility(state); - setExposedVariable('isVisible', state); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.visibility]); + const setCheckedAndNotify = async (status) => { + await setExposedVariable('value', status); + if (status) { + fireEvent('onCheck'); + } else { + fireEvent('onUnCheck'); + } + setChecked(status); + setValue(status); + }; - useEffect(() => { - setExposedVariable('setDisable', async function (disable) { - setDisable(disable); - setExposedVariable('isDisabled', disable); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disable]); + const exposedVariables = { + value: defaultValueFromProperties, + setChecked: setCheckedAndNotify, + setValue: setCheckedAndNotify, + setLoading: async function (loading) { + setLoading(loading); + setExposedVariable('isLoading', loading); + }, + setVisibility: async function (visibility) { + setVisibility(visibility); + setExposedVariable('isVisible', visibility); + }, + setDisable: async function (disable) { + setDisable(disable); + setExposedVariable('isDisabled', disable); + }, + toggle: () => { + setExposedVariable('toggle', async function () { + setExposedVariable('value', !checked); + fireEvent('onChange'); + setChecked(!checked); + setValue(!checked); + setUserInteracted(true); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + label: label, + isMandatory: isMandatory, + isLoading: loading, + isVisible: visibility, + isDisabled: disable, + isValid: isValid, + }; + + setDefaultValue(defaultValueFromProperties); + setChecked(defaultValueFromProperties); + setValue(defaultValueFromProperties); + + setExposedVariables(exposedVariables); + + isInitialRender.current = false; - useEffect(() => { - setExposedVariable('toggle', async function () { - setExposedVariable('value', !checked); - fireEvent('onChange'); - setChecked(!checked); - setValue(!checked); - setUserInteracted(true); - }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [checked, value]); + }, []); const handleToggleChange = () => { const newCheckedState = !checked; setChecked(newCheckedState); setValue(newCheckedState); - setExposedVariable('value', newCheckedState).then(() => { - fireEvent('onChange'); - if (newCheckedState) { - fireEvent('onCheck'); - } else { - fireEvent('onUnCheck'); - } - }); + setExposedVariable('value', newCheckedState); + fireEvent('onChange'); + if (newCheckedState) { + fireEvent('onCheck'); + } else { + fireEvent('onUnCheck'); + } setUserInteracted(true); }; @@ -239,7 +253,7 @@ export const Checkbox = ({ {validationError && visibility && !checked && userInteracted && (
{ +export const CodeEditor = ({ id, height, darkMode, properties, styles, setExposedVariable, dataCy }) => { const { enableLineNumber, mode, placeholder } = properties; const { visibility, disabledState } = styles; + const [value, setValue] = useState(''); const codeChanged = debounce((code) => { setExposedVariable('value', code); + setValue(code); }, 500); const editorStyles = { @@ -63,7 +65,7 @@ export const CodeEditor = ({ height, darkMode, properties, styles, exposedVariab }} > { let c = hex.substring(1).split(''); @@ -82,21 +83,19 @@ export const ColorPicker = function ({ } }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setColor]); + }, []); useEffect(() => { let exposedVariables = {}; if (/^#(([\dA-Fa-f]{3}){1,2}|([\dA-Fa-f]{4}){1,2})$/.test(defaultColor)) { - if (defaultColor !== color) { - exposedVariables = { - selectedColorHex: defaultColor, - selectedColorRGB: hexToRgb(defaultColor), - selectedColorRGBA: hexToRgba(defaultColor), - }; - setExposedVariables(exposedVariables); + exposedVariables = { + selectedColorHex: defaultColor, + selectedColorRGB: hexToRgb(defaultColor), + selectedColorRGBA: hexToRgba(defaultColor), + }; + setExposedVariables(exposedVariables); - setColor(defaultColor); - } + setColor(defaultColor); } else { exposedVariables = { selectedColorHex: undefined, @@ -110,6 +109,21 @@ export const ColorPicker = function ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [defaultColor]); + useEffect(() => { + if (showColorPicker) { + const handleClickOutside = (event) => { + if (colorPickerRef.current && !colorPickerRef.current.contains(event.target)) { + setShowColorPicker(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + } + }, [showColorPicker]); + const handleColorChange = (colorCode) => { let exposedVariables = {}; const { r, g, b, a } = colorCode.rgb; @@ -147,23 +161,20 @@ export const ColorPicker = function ({ : { display: 'none' }; return ( -
-
setShowColorPicker(true)}> - {color} - {!(color === `Invalid Color`) &&
} +
+
+
setShowColorPicker(true)} + > + {color} + {!(color === `Invalid Color`) &&
} +
{showColorPicker && ( - <> -
setShowColorPicker(false)} - width={width} - > - -
-
setShowColorPicker(false)}>
- +
+ +
)}
); diff --git a/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx b/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx index fa96cec8b9..5bfe9818e5 100644 --- a/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx +++ b/frontend/src/Editor/Components/CustomComponent/CustomComponent.jsx @@ -1,33 +1,18 @@ import React, { useEffect, useState, useRef } from 'react'; import { isEqual } from 'lodash'; import iframeContent from './iframe.html'; - -import { useDataQueries } from '@/_stores/dataQueriesStore'; -import { useGridStore } from '@/_stores/gridStore'; -import { isQueryRunnable } from '@/_helpers/utils'; +import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; export const CustomComponent = (props) => { - const { height, properties, styles, id, setExposedVariable, exposedVariables, fireEvent, dataCy, component } = props; - const dataQueries = useDataQueries(); - - const showPlaceholder = useGridStore((state) => { - const { resizingComponentId, draggingComponentId } = state; - if ( - (resizingComponentId === null && draggingComponentId === id) || - (draggingComponentId === null && resizingComponentId === id) || - id === 'resizingComponentId' - ) { - return true; - } - return false; - }, shallow); - + const { height, properties, styles, id, setExposedVariable, dataCy } = props; + const exposedVariables = useStore((state) => state.getExposedValueOfComponent(id), shallow); + const onEvent = useStore((state) => state.eventsSlice.onEvent, shallow); const { visibility, boxShadow } = styles; const { code, data } = properties; const [customProps, setCustomProps] = useState(data); const iFrameRef = useRef(null); - const dataQueryRef = useRef(dataQueries); + const customPropRef = useRef(data); useEffect(() => { @@ -42,17 +27,13 @@ export const CustomComponent = (props) => { sendMessageToIframe({ message: 'DATA_UPDATED' }); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setExposedVariable, customProps, exposedVariables.data]); + }, [customProps, exposedVariables.data]); useEffect(() => { sendMessageToIframe({ message: 'CODE_UPDATED' }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [code]); - useEffect(() => { - dataQueryRef.current = dataQueries; - }, [dataQueries]); - useEffect(() => { window.addEventListener('message', (e) => { try { @@ -60,17 +41,11 @@ export const CustomComponent = (props) => { if (e.data.message === 'UPDATE_DATA') { setCustomProps({ ...customPropRef.current, ...e.data.updatedObj }); } else if (e.data.message === 'RUN_QUERY') { - const filteredQuery = dataQueryRef.current.filter( - (query) => query.name === e.data.queryName && isQueryRunnable(query) - ); - const parameters = e.data.parameters ? JSON.parse(e.data.parameters) : {}; - filteredQuery.length === 1 && - fireEvent('onTrigger', { - component, - queryId: filteredQuery[0].id, - queryName: filteredQuery[0].name, - parameters, - }); + const options = { + parameters: e.data.parameters, + queryName: e.data.queryName, + }; + onEvent('onTrigger', [], options); } else { sendMessageToIframe(e.data); } @@ -90,7 +65,7 @@ export const CustomComponent = (props) => { { message: 'INIT_RESPONSE', componentId: id, - data: customProps, + data: customPropRef.current, code: code, }, '*' @@ -121,14 +96,12 @@ export const CustomComponent = (props) => { return (
- {showPlaceholder ? null : ( - - )} +
); }; diff --git a/frontend/src/Editor/Components/Datepicker.jsx b/frontend/src/Editor/Components/Datepicker.jsx index e7c624cbad..3e04df1e4d 100644 --- a/frontend/src/Editor/Components/Datepicker.jsx +++ b/frontend/src/Editor/Components/Datepicker.jsx @@ -1,27 +1,29 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import DatePickerComponent from 'react-datepicker'; import moment from 'moment'; import 'react-datepicker/dist/react-datepicker.css'; +import './datepicker.scss'; +import cx from 'classnames'; export const Datepicker = function Datepicker({ height, properties, styles, - exposedVariables, setExposedVariable, + setExposedVariables, validate, onComponentClick, - component, id, darkMode, fireEvent, dataCy, }) { + const isInitialRender = useRef(true); const { enableTime, enableDate, defaultValue, disabledDates } = properties; const format = typeof properties.format === 'string' ? properties.format : ''; const { visibility, disabledState, borderRadius, boxShadow } = styles; - const [date, setDate] = useState(null); + const [date, setDate] = useState(defaultValue); const [excludedDates, setExcludedDates] = useState([]); const [showValidationError, setShowValidationError] = useState(false); @@ -46,6 +48,7 @@ export const Datepicker = function Datepicker({ }; useEffect(() => { + if (isInitialRender.current) return; const dateMomentInstance = defaultValue && moment(defaultValue, selectedDateFormat); if (dateMomentInstance && dateMomentInstance.isValid()) { setDate(dateMomentInstance.toDate()); @@ -70,14 +73,32 @@ export const Datepicker = function Datepicker({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [disabledDates, format]); - const validationData = validate(exposedVariables.value); + const validationData = validate(computeDateString(date)); const { isValid, validationError } = validationData; useEffect(() => { + isInitialRender.current = false; setExposedVariable('isValid', isValid); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isValid]); + useEffect(() => { + const exposedVariables = { + isValid, + }; + const dateMomentInstance = defaultValue && moment(defaultValue, selectedDateFormat); + if (dateMomentInstance && dateMomentInstance.isValid()) { + setDate(dateMomentInstance.toDate()); + exposedVariables.value = defaultValue; + } else { + setDate(null); + exposedVariables.value = undefined; + } + setExposedVariables(exposedVariables); + isInitialRender.current = false; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return (
onDateChange(date)} showTimeInput={enableTime ? true : false} showTimeSelectOnly={enableDate ? false : true} onFocus={(event) => { - onComponentClick(id, component, event); + onComponentClick(id); }} showMonthDropdown showYearDropdown diff --git a/frontend/src/Editor/Components/DaterangePicker.jsx b/frontend/src/Editor/Components/DaterangePicker.jsx index 67cfdd7f22..b30e189bd6 100644 --- a/frontend/src/Editor/Components/DaterangePicker.jsx +++ b/frontend/src/Editor/Components/DaterangePicker.jsx @@ -10,11 +10,14 @@ export const DaterangePicker = function DaterangePicker({ properties, styles, setExposedVariable, + setExposedVariables, width, darkMode, fireEvent, dataCy, + id, }) { + const isInitialRender = useRef(true); const { borderRadius, visibility, disabledState, boxShadow } = styles; const { defaultStartDate, defaultEndDate } = properties; const formatProp = typeof properties.format === 'string' ? properties.format : ''; @@ -26,12 +29,28 @@ export const DaterangePicker = function DaterangePicker({ const dateRangeRef = useRef(null); useEffect(() => { + if (isInitialRender.current) return; + setStartDate(moment(defaultStartDate, formatProp)); + setExposedVariable('startDate', moment(defaultStartDate, formatProp).format(formatProp)); + }, [defaultStartDate, formatProp]); + + useEffect(() => { + if (isInitialRender.current) return; + setEndDate(moment(defaultEndDate, formatProp)); + setExposedVariable('endDate', moment(defaultEndDate, formatProp).format(formatProp)); + }, [defaultEndDate, formatProp]); + + useEffect(() => { + const exposedVariables = { + startDate: moment(defaultStartDate, formatProp).format(formatProp), + endDate: moment(defaultEndDate, formatProp).format(formatProp), + }; + setExposedVariables(exposedVariables); setStartDate(moment(defaultStartDate, formatProp)); setEndDate(moment(defaultEndDate, formatProp)); - setExposedVariable('startDate', startDate.format(formatProp)); - setExposedVariable('endDate', endDate.format(formatProp)); + isInitialRender.current = false; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultEndDate, defaultStartDate, formatProp]); + }, []); useEffect(() => { dateRangeRef.current.container.querySelector('.DateRangePickerInput').style.borderRadius = `${Number.parseFloat( @@ -63,6 +82,11 @@ export const DaterangePicker = function DaterangePicker({ function focusChanged(focus) { setFocusedInput(focus); + if (focus) { + document.querySelector(`.ele-${id}`).style.zIndex = 3; + } else { + document.querySelector(`.ele-${id}`).style.zIndex = ''; + } } return ( diff --git a/frontend/src/Editor/Components/DropDown.jsx b/frontend/src/Editor/Components/DropDown.jsx index b0aefad133..723cb34c3a 100644 --- a/frontend/src/Editor/Components/DropDown.jsx +++ b/frontend/src/Editor/Components/DropDown.jsx @@ -1,5 +1,5 @@ import _ from 'lodash'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import Select, { components } from 'react-select'; import TriangleDownArrow from '@/_ui/Icon/bulkIcons/TriangleDownArrow'; import TriangleUpArrow from '@/_ui/Icon/bulkIcons/TriangleUpArrow'; @@ -10,22 +10,20 @@ export const DropDown = function DropDown({ properties, styles, setExposedVariable, + setExposedVariables, fireEvent, darkMode, onComponentClick, id, - component, - exposedVariables, dataCy, }) { + const isInitialRender = useRef(true); let { label, value, advanced, schema, placeholder, display_values, values } = properties; const { selectedTextColor, borderRadius, visibility, disabledState, justifyContent, boxShadow } = styles; const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value)); - const { value: exposedValue } = exposedVariables; const [showValidationError, setShowValidationError] = useState(false); const validationData = validate(value); const { isValid, validationError } = validationData; - function findDefaultItem(schema) { const foundItem = schema?.find((item) => item?.default === true); return !hasVisibleFalse(foundItem?.value) ? foundItem?.value : undefined; @@ -63,10 +61,13 @@ export const DropDown = function DropDown({ const setExposedItem = (value, index, onSelectFired = false) => { setCurrentValue(value); if (onSelectFired) { - setExposedVariable('value', value); fireEvent('onSelect'); - } else setExposedVariable('value', value); - setExposedVariable('selectedOptionLabel', index === undefined ? undefined : display_values?.[index]); + } + const exposedVariables = { + value, + selectedOptionLabel: index === undefined ? undefined : display_values?.[index], + }; + setExposedVariables(exposedVariables); }; function selectOption(value) { @@ -79,19 +80,71 @@ export const DropDown = function DropDown({ setExposedItem(undefined, undefined, true); } } - - useEffect(() => { - setExposedVariable('selectOption', async function (value) { - selectOption(value); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(values), setCurrentValue, JSON.stringify(display_values)]); - useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isValid', isValid); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isValid]); + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('value', currentValue); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentValue]); + + useEffect(() => { + if (isInitialRender.current) return; + const index = values?.indexOf(currentValue); + setExposedVariable('selectedOptionLabel', display_values?.[index]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentValue, JSON.stringify(display_values), JSON.stringify(values)]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('label', label); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [label]); + + useEffect(() => { + if (isInitialRender.current) return; + if (advanced) { + setExposedVariable( + 'optionLabels', + schema?.filter((item) => item?.visible)?.map((item) => item.label) + ); + if (hasVisibleFalse(currentValue)) { + setCurrentValue(findDefaultItem(schema)); + } + } else setExposedVariable('optionLabels', display_values); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(schema), advanced, JSON.stringify(display_values), currentValue]); + + useEffect(() => { + const index = values?.indexOf(currentValue); + let optionLabels = display_values; + if (advanced) { + optionLabels = schema?.filter((item) => item?.visible)?.map((item) => item.label); + } + const exposedVariables = { + selectOption: async function (value) { + selectOption(value); + }, + isValid: isValid, + value: currentValue, + selectedOptionLabel: display_values?.[index], + label: label, + optionLabels: optionLabels, + }; + + setExposedVariables(exposedVariables); + if (hasVisibleFalse(currentValue)) { + setCurrentValue(findDefaultItem(schema)); + } + isInitialRender.current = false; + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { let newValue = undefined; let index = null; @@ -104,16 +157,6 @@ export const DropDown = function DropDown({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(value), JSON.stringify(values)]); - useEffect(() => { - let index = null; - if (exposedValue !== currentValue) { - setExposedVariable('value', currentValue); - } - index = values?.indexOf(currentValue); - setExposedVariable('selectedOptionLabel', display_values?.[index]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentValue, JSON.stringify(display_values), JSON.stringify(values)]); - useEffect(() => { let newValue = undefined; let index = null; @@ -125,24 +168,6 @@ export const DropDown = function DropDown({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(values)]); - useEffect(() => { - setExposedVariable('label', label); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [label]); - - useEffect(() => { - if (advanced) { - setExposedVariable( - 'optionLabels', - schema?.filter((item) => item?.visible)?.map((item) => item.label) - ); - if (hasVisibleFalse(currentValue)) { - setCurrentValue(findDefaultItem(schema)); - } - } else setExposedVariable('optionLabels', display_values); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(schema), advanced, JSON.stringify(display_values), currentValue]); - function hasVisibleFalse(value) { for (let i = 0; i < schema?.length; i++) { if (schema[i].value === value && schema[i].visible === false) { @@ -227,42 +252,6 @@ export const DropDown = function DropDown({ backgroundColor: darkMode ? 'rgb(31,40,55)' : 'white', }), }; - const [isOpen, setIsOpen] = useState(false); - - const handleDropdownOpen = () => { - setIsOpen(true); - }; - - const handleDropdownClose = () => { - setIsOpen(false); - }; - - const DropdownIndicator = (props) => { - return ( - -
(isOpen ? handleDropdownClose() : handleDropdownOpen())}> - {isOpen ? ( - - ) : ( - - )} -
-
- ); - }; - - useEffect(() => { - const handleClickOutside = (event) => { - if (isOpen && !event.target.closest('.dropdown-widget')) { - handleDropdownClose(); - } - }; - - document.addEventListener('click', handleClickOutside); - return () => { - document.removeEventListener('click', handleClickOutside); - }; - }, [isOpen]); return ( <> @@ -271,7 +260,7 @@ export const DropDown = function DropDown({ style={{ height, display: visibility ? '' : 'none' }} onClick={(event) => { event.stopPropagation(); - onComponentClick(id, component, event); + onComponentClick(id); }} data-cy={dataCy} > @@ -297,13 +286,9 @@ export const DropDown = function DropDown({ styles={customStyles} isLoading={properties.loadingState} onInputChange={onSearchTextChange} - onFocus={(event) => onComponentClick(event, component, id)} + onFocus={(event) => onComponentClick(id)} menuPortalTarget={document.body} placeholder={placeholder} - onMenuOpen={handleDropdownOpen} - onMenuClose={handleDropdownClose} - menuIsOpen={isOpen} - components={{ DropdownIndicator }} />
diff --git a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx index df4697835c..9b1c5981e0 100644 --- a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx +++ b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx @@ -1,5 +1,3 @@ -import { resolveReferences } from '@/_helpers/utils'; -import { useCurrentState } from '@/_stores/currentStateStore'; import React, { useState, useEffect, useMemo, useRef } from 'react'; import Select, { components } from 'react-select'; import ClearIndicatorIcon from '@/_ui/Icon/bulkIcons/ClearIndicator'; @@ -16,6 +14,8 @@ import CustomOption from './CustomOption'; import Label from '@/_ui/Label'; import cx from 'classnames'; import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor } from './utils'; +import useStore from '@/AppBuilder/_stores/store'; +import { shallow } from 'zustand/shallow'; const { DropdownIndicator, ClearIndicator } = components; const INDICATOR_CONTAINER_WIDTH = 60; @@ -55,11 +55,20 @@ export const DropdownV2 = ({ darkMode, onComponentClick, id, - component, - exposedVariables, + componentName, + validation, dataCy, }) => { - const { label, value, advanced, schema, placeholder, loadingState: dropdownLoadingState, disabledState } = properties; + const { + label, + value, + advanced, + schema, + placeholder, + loadingState: dropdownLoadingState, + disabledState, + optionsLoadingState, + } = properties; const { selectedTextColor, fieldBorderRadius, @@ -79,11 +88,11 @@ export const DropdownV2 = ({ accentColor, padding, } = styles; + const isInitialRender = useRef(true); const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value)); - const { value: exposedValue } = exposedVariables; - const currentState = useCurrentState(); - const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState); - const options = component?.definition?.properties?.options?.value; + const getResolvedValue = useStore((state) => state.getResolvedValue, shallow); + const isMandatory = validation?.mandatory ?? false; + const options = properties?.options; const validationData = validate(currentValue); const { isValid, validationError } = validationData; const ref = React.useRef(null); @@ -106,13 +115,14 @@ export const DropdownV2 = ({ let _options = advanced ? schema : options; if (Array.isArray(_options)) { let _selectOptions = _options - .filter((data) => resolveReferences(advanced ? data?.visible : data?.visible?.value, currentState)) + .filter((data) => getResolvedValue(advanced ? data?.visible : data?.visible?.value) ?? true) .map((data) => ({ ...data, - label: resolveReferences(data?.label, currentState), - value: resolveReferences(data?.value, currentState), - isDisabled: resolveReferences(advanced ? data?.disable : data?.disable?.value, currentState), + label: String(getResolvedValue(data?.label)), + value: getResolvedValue(data?.value), + isDisabled: getResolvedValue(advanced ? data?.disable : data?.disable?.value) ?? false, })); + return _selectOptions; } else { return []; @@ -139,6 +149,7 @@ export const DropdownV2 = ({ const onSearchTextChange = (searchText, actionProps) => { if (actionProps.action === 'input-change') { setSearchInputValue(searchText); + setExposedVariable('searchText', searchText); fireEvent('onSearchTextChanged'); } }; @@ -174,11 +185,21 @@ export const DropdownV2 = ({ }, [properties.visibility, dropdownLoadingState, disabledState]); // Exposed variables + useEffect(() => { - if (exposedValue !== currentValue) { - const _selectedOption = selectOptions.find((option) => option.value === currentValue); - setExposedVariable('selectedOption', pick(_selectedOption, ['label', 'value'])); - } + if (isInitialRender.current) return; + setExposedVariable('value', currentValue); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentValue]); + + useEffect(() => { + const _selectedOption = selectOptions.find((option) => option.value === currentValue); + setExposedVariable('selectedOption', pick(_selectedOption, ['label', 'value'])); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentValue, JSON.stringify(selectOptions)]); + + useEffect(() => { + if (isInitialRender.current) return; const _options = selectOptions?.map(({ label, value }) => ({ label, value })); setExposedVariable('options', _options); @@ -191,33 +212,75 @@ export const DropdownV2 = ({ }, [currentValue, JSON.stringify(selectOptions)]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('label', label); - setExposedVariable('searchText', searchInputValue); - setExposedVariable('isValid', isValid); - setExposedVariable('isVisible', properties.visibility); - setExposedVariable('isLoading', dropdownLoadingState); - setExposedVariable('isDisabled', disabledState); - setExposedVariable('isMandatory', isMandatory); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.visibility, dropdownLoadingState, disabledState, isMandatory, label, searchInputValue, isValid]); + }, [label]); useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('searchText', searchInputValue); + }, [searchInputValue]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isValid', isValid); + }, [isValid]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isVisible', properties.visibility); + }, [properties.visibility]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isLoading', dropdownLoadingState); + }, [dropdownLoadingState]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isDisabled', disabledState); + }, [disabledState]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isMandatory', isMandatory); + }, [isMandatory]); + + useEffect(() => { + const _options = selectOptions?.map(({ label, value }) => ({ label, value })); const exposedVariables = { clear: async function () { setCurrentValue(null); }, setVisibility: async function (value) { setVisibility(value); + setExposedVariable('isVisible', value); }, setLoading: async function (value) { setIsDropdownLoading(value); + setExposedVariable('isLoading', value); }, setDisable: async function (value) { setIsDropdownDisabled(value); + setExposedVariable('isDisabled', value); }, + selectOption: async function (value) { + let _value = value; + if (isObject(value) && has(value, 'value')) _value = value?.value; + selectOption(_value); + }, + options: _options, + value: currentValue, + label: label, + searchText: searchInputValue, + isValid: isValid, + isVisible: properties.visibility, + isLoading: dropdownLoadingState, + isDisabled: disabledState, + isMandatory: isMandatory, }; setExposedVariables(exposedVariables); + isInitialRender.current = false; }, []); const customStyles = { @@ -343,7 +406,7 @@ export const DropdownV2 = ({ return ( <>
{ - onComponentClick(id, component, event); + onComponentClick(id); // This following line is needed because sometimes after clicking on canvas then also dropdown remains selected useEditorStore.getState().actions.setHoveredComponent(''); }} @@ -397,7 +460,7 @@ export const DropdownV2 = ({ isLoading={isDropdownLoading} onInputChange={onSearchTextChange} inputValue={searchInputValue} - onFocus={() => { + onMenuOpen={() => { fireEvent('onFocus'); }} onMenuInputFocus={() => setIsFocused(true)} @@ -425,7 +488,7 @@ export const DropdownV2 = ({ iconColor={iconColor} isSearchable={false} darkMode={darkMode} - optionsLoadingState={properties.optionsLoadingState} + optionsLoadingState={optionsLoadingState && advanced} menuPlacement="auto" />
diff --git a/frontend/src/Editor/Components/FilePicker.jsx b/frontend/src/Editor/Components/FilePicker.jsx index e73abe3356..b2e2b1ff86 100644 --- a/frontend/src/Editor/Components/FilePicker.jsx +++ b/frontend/src/Editor/Components/FilePicker.jsx @@ -1,57 +1,46 @@ -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; import { useDropzone } from 'react-dropzone'; -import { resolveWidgetFieldValue } from '@/_helpers/utils'; import { toast } from 'react-hot-toast'; // eslint-disable-next-line import/no-unresolved import * as XLSX from 'xlsx/xlsx.mjs'; import { useAppInfo } from '@/_stores/appDataStore'; +import useStore from '@/AppBuilder/_stores/store'; export const FilePicker = ({ id, width, height, component, + fireEvent, onComponentOptionChanged, - onEvent, darkMode, styles, + properties, setExposedVariable, + setExposedVariables, dataCy, }) => { - //* properties definitions - const instructionText = - component.definition.properties.instructionText?.value ?? 'Drag and drop files here or click to select files'; - const enableDropzone = component.definition.properties.enableDropzone.value ?? true; - const enablePicker = component.definition.properties?.enablePicker?.value ?? true; - const maxFileCount = component.definition.properties.maxFileCount?.value ?? 2; - const enableMultiple = component.definition.properties.enableMultiple?.value ?? false; - const fileType = component.definition.properties.fileType?.value ?? 'image/*'; - const maxSize = component.definition.properties.maxSize?.value ?? 1048576; - const minSize = component.definition.properties.minSize?.value ?? 0; - const parseContent = resolveWidgetFieldValue(component.definition.properties.parseContent?.value); - const fileTypeFromExtension = component.definition.properties.parseFileType?.value ?? 'auto-detect'; - const parsedEnableDropzone = typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone) : true; - const parsedEnablePicker = typeof enablePicker !== 'boolean' ? resolveWidgetFieldValue(enablePicker) : true; - - const parsedMaxFileCount = typeof maxFileCount !== 'number' ? resolveWidgetFieldValue(maxFileCount) : maxFileCount; - const parsedEnableMultiple = - typeof enableMultiple !== 'boolean' ? resolveWidgetFieldValue(enableMultiple) : enableMultiple; - const parsedFileType = resolveWidgetFieldValue(fileType); - const parsedMinSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(minSize) : minSize; - const parsedMaxSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(maxSize) : maxSize; - //* styles definitions - const widgetVisibility = component.definition.styles?.visibility?.value ?? true; - const disabledState = component.definition.styles?.disabledState?.value ?? false; - - const parsedDisabledState = - typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState; - const parsedWidgetVisibility = - typeof widgetVisibility !== 'boolean' ? resolveWidgetFieldValue(widgetVisibility) : widgetVisibility; + //* resolved properties d + const isInitialRender = useRef(true); + const instructionText = properties?.instructionText ?? 'Drag and drop files here or click to select files'; + const enableDropzone = properties?.enableDropzone ?? true; + const enablePicker = properties?.enablePicker ?? true; + const maxFileCount = properties?.maxFileCount ?? 2; + const enableMultiple = properties?.enableMultiple ?? false; + const fileType = properties?.fileType ?? 'image/*'; + const maxSize = properties?.maxSize ?? 1048576; + const minSize = properties?.minSize ?? 0; + const parseContent = properties.parseContent; + const fileTypeFromExtension = properties.parseFileType ?? 'auto-detect'; + //* resolved styles + const widgetVisibility = styles?.visibility ?? true; + const disabledState = styles?.disabledState ?? false; const { events: allAppEvents } = useAppInfo(); const filePickerEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id); + console.log(filePickerEvents); const bgThemeColor = darkMode ? '#232E3C' : '#fff'; @@ -68,9 +57,9 @@ export const FilePicker = ({ color: '#bdbdbd', outline: 'none', transition: 'border .24s ease-in-out', - display: parsedWidgetVisibility ? 'flex' : 'none', + display: widgetVisibility ? 'flex' : 'none', height, - backgroundColor: !parsedDisabledState && bgThemeColor, + backgroundColor: !disabledState && bgThemeColor, boxShadow: styles.boxShadow, }; @@ -90,14 +79,14 @@ export const FilePicker = ({ const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles, fileRejections } = useDropzone({ - accept: { parsedFileType: [parsedFileType] }, - noClick: !parsedEnablePicker || disablePicker, - noDrag: !parsedEnableDropzone || disablePicker, + accept: { parsedFileType: [fileType] }, + noClick: !enablePicker || disablePicker, + noDrag: !enableDropzone || disablePicker, noKeyboard: true, - maxFiles: parsedMaxFileCount, - minSize: parsedMinSize, - maxSize: parsedMaxSize, - multiple: parsedEnableMultiple, + maxFiles: maxFileCount, + minSize: minSize, + maxSize: maxSize, + multiple: enableMultiple, disabled: disablePicker, validator: validateFileExists, onDropRejected: () => (selectedFiles.length > 0 ? setShowSelectedFiles(true) : setShowSelectedFiles(false)), @@ -107,9 +96,9 @@ export const FilePicker = ({ const style = useMemo( () => ({ ...baseStyle, - ...(isDragActive && parsedEnableDropzone ? activeStyle : {}), - ...(isDragAccept && parsedEnableDropzone ? acceptStyle : {}), - ...(isDragReject && parsedEnableDropzone ? rejectStyle : {}), + ...(isDragActive && enableDropzone ? activeStyle : {}), + ...(isDragAccept && enableDropzone ? acceptStyle : {}), + ...(isDragReject && enableDropzone ? rejectStyle : {}), }), // eslint-disable-next-line react-hooks/exhaustive-deps [baseStyle, isDragActive, isDragAccept, acceptStyle, isDragReject] @@ -123,7 +112,7 @@ export const FilePicker = ({ function validateFileExists(_file) { const selectedFilesCount = selectedFiles.length; - if (selectedFilesCount === parsedMaxFileCount) { + if (selectedFilesCount === maxFileCount) { return { code: 'max_file_count_reached', message: `Max file count reached`, @@ -134,14 +123,13 @@ export const FilePicker = ({ } useEffect(() => { - if (parsedDisabledState) setDisablePicker(true); - - if (selectedFiles.length === parsedMaxFileCount && parsedEnableMultiple) { + if (disabledState) setDisablePicker(true); + else if (selectedFiles.length === maxFileCount && enableMultiple) { setDisablePicker(true); } else { setDisablePicker(false); } - }, [selectedFiles.length, parsedDisabledState, parsedMaxFileCount, parsedEnableMultiple]); + }, [selectedFiles.length, disabledState, maxFileCount, enableMultiple]); /** * *getFileData() @@ -220,35 +208,38 @@ export const FilePicker = ({ const fileSize = formatFileSize(rejectedFileSize); if (code === errorType.MIN_SIZE) { - return `File size ${fileSize} is too small. Minimum size is ${formatFileSize(parsedMinSize)}`; + return `File size ${fileSize} is too small. Minimum size is ${formatFileSize(minSize)}`; } if (code === errorType.MAX_SIZE) { - return `File size ${fileSize} is too large. Maximum size is ${formatFileSize(parsedMaxSize)}`; + return `File size ${fileSize} is too large. Maximum size is ${formatFileSize(maxSize)}`; } return message; }; useEffect(() => { + if (isInitialRender.current) return; if (acceptedFiles.length === 0 && selectedFiles.length === 0) { - onComponentOptionChanged(component, 'file', [], id); + setExposedVariable('file', []); + // onComponentOptionChanged(component, 'file', [], id); } - if (acceptedFiles.length !== 0 && onEvent) { - const fileData = parsedEnableMultiple ? [...selectedFiles] : []; + if (acceptedFiles.length !== 0 && fireEvent) { + const fileData = enableMultiple ? [...selectedFiles] : []; if (parseContent) { - onComponentOptionChanged(component, 'isParsing', true, id); + setExposedVariable('isParsing', true); + // onComponentOptionChanged(component, 'isParsing', true, id); } acceptedFiles.map((acceptedFile) => { const acceptedFileData = fileReader(acceptedFile); acceptedFileData.then((data) => { - if (fileData.length < parsedMaxFileCount) { + if (fileData.length < maxFileCount) { fileData.push(data); } }); }); - onEvent('onFileSelected', filePickerEvents, { component }) + fireEvent('onFileSelected', id, 'canvas', { component }) .then(() => { setAccepted(true); // eslint-disable-next-line no-unused-vars @@ -256,15 +247,17 @@ export const FilePicker = ({ setTimeout(() => { setShowSelectedFiles(true); setAccepted(false); - onComponentOptionChanged(component, 'isParsing', false, id); + setExposedVariable('isParsing', false); + // onComponentOptionChanged(component, 'isParsing', false, id); resolve(); }, 600); }); }) - .then(() => onEvent('onFileLoaded', filePickerEvents, { component })) + .then(() => fireEvent('onFileLoaded', id, 'canvas', { component })) .then(() => { setSelectedFiles(fileData); - onComponentOptionChanged(component, 'file', fileData, id); + setExposedVariable('file', fileData); + // onComponentOptionChanged(component, 'file', fileData, id); }); } @@ -288,20 +281,32 @@ export const FilePicker = ({ copy.splice(index, 1); return copy; }); - onEvent('onFileDeselected', filePickerEvents); + fireEvent('onFileDeselected', id); }; useEffect(() => { + isInitialRender.current = false; if (selectedFiles.length === 0) { setShowSelectedFiles(false); } - onComponentOptionChanged(component, 'file', selectedFiles, id); - setExposedVariable('clearFiles', async function () { - setSelectedFiles([]); - }); + setExposedVariable('file', selectedFiles); + // onComponentOptionChanged(component, 'file', selectedFiles, id); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedFiles]); + useEffect(() => { + const exposedVariables = { + clearFiles: async function () { + setSelectedFiles([]); + }, + file: [], + }; + setExposedVariables(exposedVariables); + setShowSelectedFiles(false); + isInitialRender.current = false; + }, []); + return (
@@ -342,12 +347,12 @@ export const FilePicker = ({ )} diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 286525e328..34bbdec67e 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -77,7 +77,6 @@ export const Form = function Form(props) { fireEvent('onInvalid'); } }); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -112,7 +111,7 @@ export const Form = function Form(props) { }, [advanced]); useEffect(() => { - setUIComponents(generateUIComponents(JSONSchema, advanced)); + setUIComponents(generateUIComponents(JSONSchema, advanced, component.name)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(JSONSchema), advanced]); @@ -142,7 +141,8 @@ export const Form = function Form(props) { } else { Object.keys(childComponents ?? {}).forEach((childId) => { if (childrenData[childId]?.name) { - formattedChildData[childrenData[childId].name] = { ...omit(childrenData[childId], 'name'), id: childId }; + const componentName = childComponents?.[childId]?.component?.name; + formattedChildData[componentName] = { ...omit(childrenData[childId], 'name'), id: childId }; childValidation = childValidation && (childrenData[childId]?.isValid ?? true); } }); @@ -247,7 +247,7 @@ export const Form = function Form(props) { ) : (
{!advanced && ( - <> +
- +
)} {advanced && uiComponents?.map((item, index) => { @@ -287,8 +287,8 @@ export const Form = function Form(props) { 'ToggleSwitch', 'ToggleSwitchV2', ].includes(uiComponents?.[index + 1]?.component) - ? `json-form-wrapper` - : `json-form-wrapper form-label-restricted` + ? `json-form-wrapper json-form-wrapper-disabled` + : `json-form-wrapper json-form-wrapper-disabled form-label-restricted` } key={index} > diff --git a/frontend/src/Editor/Components/Form/FormUtils.js b/frontend/src/Editor/Components/Form/FormUtils.js index b4f049c9eb..378636932d 100644 --- a/frontend/src/Editor/Components/Form/FormUtils.js +++ b/frontend/src/Editor/Components/Form/FormUtils.js @@ -1,5 +1,7 @@ import { componentTypes } from '@/Editor/WidgetManager/components'; -export function generateUIComponents(JSONSchema, advanced) { +import { useCurrentStateStore } from '@/_stores/currentStateStore'; + +export function generateUIComponents(JSONSchema, advanced, componentName) { if (advanced) { if (typeof JSONSchema?.properties !== 'object' || JSONSchema?.properties == null) { return; @@ -11,6 +13,17 @@ export function generateUIComponents(JSONSchema, advanced) { if (itemType) { uiComponentsDraft.push(structuredClone(componentTypes.find((component) => component?.component == 'Text'))); //only add if there is a valid item type + } else { + useCurrentStateStore.getState().actions.setErrors({ + [componentName]: { + type: 'component', + data: { + message: `JSON Schema consists of invalid input type: ${value?.type}`, + status: 'Failed', + }, + }, + }); + uiComponentsDraft.push(undefined); } uiComponentsDraft.push(structuredClone(componentTypes.find((component) => component?.component == itemType))); }); diff --git a/frontend/src/Editor/Components/Icon.jsx b/frontend/src/Editor/Components/Icon.jsx index cec0bbb348..02417947e5 100644 --- a/frontend/src/Editor/Components/Icon.jsx +++ b/frontend/src/Editor/Components/Icon.jsx @@ -3,24 +3,11 @@ import React, { useState, useEffect } from 'react'; import * as Icons from '@tabler/icons-react'; import cx from 'classnames'; -export const Icon = ({ - properties, - styles, - fireEvent, - width, - height, - setExposedVariable, - setExposedVariables, - darkMode, - dataCy, - component, -}) => { +export const Icon = ({ properties, styles, fireEvent, width, height, setExposedVariables, darkMode, dataCy }) => { const { icon } = properties; const { iconColor, visibility, boxShadow } = styles; // eslint-disable-next-line import/namespace const IconElement = Icons[icon]; - const { definition } = component; - const { events = [] } = definition; const color = iconColor === '#000' ? (darkMode ? '#fff' : '#000') : iconColor; @@ -42,13 +29,10 @@ export const Icon = ({ }; setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setIconVisibility]); + }, []); return ( -
0 })} - data-cy={dataCy} - > +
event.eventId === 'onClick'); + const hasOnClickEvent = false; const widgetVisibility = visibility ?? true; const imageRef = useRef(null); const [imageOffset, setImageOffset] = useState(0); @@ -130,7 +127,7 @@ export const Image = function Image({ return (
state.getExposedValueOfComponent(kanbanProps.id), shallow); + const { id, containerProps, component } = kanbanProps; const renderCloseButton = () => { return ( diff --git a/frontend/src/Editor/Components/Listview.jsx b/frontend/src/Editor/Components/Listview.jsx index fee12be460..a8d37ab6f3 100644 --- a/frontend/src/Editor/Components/Listview.jsx +++ b/frontend/src/Editor/Components/Listview.jsx @@ -106,6 +106,16 @@ export const Listview = function Listview({ ); const filteredData = deepClone(childrenData); if (filteredData?.[0]) { + // update the name of the component in the data + Object.keys(filteredData?.[0]).forEach((item) => { + const { id } = _.get(filteredData?.[0], item, {}); + const oldName = item; + const newName = _.get(childComponents, `${id}.component.name`, ''); + if (oldName !== newName) { + _.set(filteredData[0], newName, _.get(filteredData[0], oldName)); + _.unset(filteredData[0], oldName); + } + }); Object.keys(filteredData?.[0]).forEach((item) => { if (!componentNamesSet?.has(item)) { for (const key in filteredData) { @@ -173,13 +183,17 @@ export const Listview = function Listview({ removeComponent={removeComponent} listViewItemOptions={{ index }} exposedVariables={childrenData[index]} - onOptionChange={function ({ component, optionName, value }) { + onOptionChange={function ({ component, optionName, value, componentId }) { setChildrenData((prevData) => { const changedData = { [component.name]: { [optionName]: value } }; const existingDataAtIndex = prevData[index] ?? {}; const newDataAtIndex = { ...prevData[index], - [component.name]: { ...existingDataAtIndex[component.name], ...changedData[component.name] }, + [component.name]: { + ...existingDataAtIndex[component.name], + ...changedData[component.name], + id: componentId, + }, }; const newChildrenData = { ...prevData, [index]: newDataAtIndex }; return { ...prevData, ...newChildrenData }; diff --git a/frontend/src/Editor/Components/Map/Map.jsx b/frontend/src/Editor/Components/Map/Map.jsx index f448722d8d..1068fa89ab 100644 --- a/frontend/src/Editor/Components/Map/Map.jsx +++ b/frontend/src/Editor/Components/Map/Map.jsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState, useCallback, useEffect, useRef } from 'react'; import { GoogleMap, LoadScript, Marker, Autocomplete, Polygon } from '@react-google-maps/api'; import { resolveWidgetFieldValue } from '@/_helpers/utils'; import { darkModeStyles } from './styles'; @@ -9,32 +9,30 @@ export const Map = function Map({ id, width, height, - component, darkMode, - onComponentClick, - onComponentOptionChanged, - onComponentOptionsChanged, + onComponentClick = () => {}, + onComponentOptionChanged = () => {}, + onComponentOptionsChanged = () => {}, styles, setExposedVariable, + setExposedVariables, dataCy, properties, fireEvent, }) { - const center = component.definition.properties.initialLocation.value; + const isInitialRender = useRef(true); + const center = properties?.initialLocation ?? { lat: 0, lng: 0 }; const { polygonPoints = [], defaultMarkers = [] } = properties; const { t } = useTranslation(); - const addNewMarkersProp = component.definition.properties.addNewMarkers; - const canAddNewMarkers = addNewMarkersProp ? resolveWidgetFieldValue(addNewMarkersProp.value) : false; + const canAddNewMarkers = properties?.addNewMarkers ?? false; + const canSearch = properties?.canSearch ?? false; + const widgetVisibility = styles?.visibility ?? true; + const disabledState = styles?.disabledState ?? false; - const canSearchProp = component.definition.properties.canSearch; - const canSearch = canSearchProp ? resolveWidgetFieldValue(canSearchProp.value) : false; - const widgetVisibility = component.definition.styles?.visibility?.value ?? true; - const disabledState = component.definition.styles?.disabledState?.value ?? false; - - const parsedDisabledState = - typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState; + // const parsedDisabledState = + // typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState) : disabledState; let parsedWidgetVisibility = widgetVisibility; @@ -58,6 +56,16 @@ export const Map = function Map({ setMarkers(defaultMarkers); }, [JSON.stringify(defaultMarkers)]); + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('center', center); + }, [center]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('markers', defaultMarkers); + }, [defaultMarkers]); + function handleMapClick(e) { if (!canAddNewMarkers) { return; @@ -66,11 +74,12 @@ export const Map = function Map({ const lat = e.latLng.lat(); const lng = e.latLng.lng(); - const newMarkers = markers; + const newMarkers = [...markers]; newMarkers.push({ lat, lng }); setMarkers(newMarkers); - - onComponentOptionChanged(component, 'markers', newMarkers).then(() => fireEvent('onCreateMarker')); + setExposedVariable('markers', newMarkers); + fireEvent('onCreateMarker'); + // onComponentOptionChanged(component, 'markers', newMarkers).then(() => fireEvent('onCreateMarker')); } function addMapUrlToJson(centerJson) { @@ -88,17 +97,24 @@ export const Map = function Map({ const newCenter = gmap.center?.toJSON(); setMapCenter(newCenter); - - onComponentOptionsChanged(component, [ - ['bounds', bounds], - ['center', addMapUrlToJson(newCenter)], - ]).then(() => fireEvent('onBoundsChange')); + const exposedVariables = { + bounds, + center: addMapUrlToJson(newCenter), + }; + setExposedVariables(exposedVariables); + fireEvent('onBoundsChange'); + // onComponentOptionsChanged(component, [ + // ['bounds', bounds], + // ['center', addMapUrlToJson(newCenter)], + // ]).then(() => fireEvent('onBoundsChange')); } useEffect(() => { + if (isInitialRender.current) return; const resolvedCenter = resolveWidgetFieldValue(center); setMapCenter(resolvedCenter); - onComponentOptionsChanged(component, [['center', addMapUrlToJson(resolvedCenter)]]); + setExposedVariable('center', addMapUrlToJson(resolvedCenter)); + // onComponentOptionsChanged(component, [['center', addMapUrlToJson(resolvedCenter)]]); // eslint-disable-next-line react-hooks/exhaustive-deps }, [center]); @@ -106,11 +122,14 @@ export const Map = function Map({ const onLoad = useCallback(function onLoad(mapInstance) { setGmap(mapInstance); const centerJson = mapInstance.center?.toJSON(); - onComponentOptionsChanged(component, [['center', addMapUrlToJson(centerJson)]]); + setExposedVariable('center', addMapUrlToJson(centerJson)); + // onComponentOptionsChanged(component, [['center', addMapUrlToJson(centerJson)]]); }); function handleMarkerClick(index) { - onComponentOptionChanged(component, 'selectedMarker', markers[index]).then(() => fireEvent('onMarkerClick')); + setExposedVariable('selectedMarker', markers[index]); + fireEvent('onMarkerClick'); + // onComponentOptionChanged(component, 'selectedMarker', markers[index]).then(() => fireEvent('onMarkerClick')); } function onPlaceChanged() { @@ -124,18 +143,27 @@ export const Map = function Map({ } useEffect(() => { - setExposedVariable('setLocation', async function (lat, lng) { - if (lat && lng) setMapCenter(resolveWidgetFieldValue({ lat, lng })); - }); - }, [setMapCenter]); + const resolvedCenter = resolveWidgetFieldValue(center); + const exposedVariables = { + setLocation: async function (lat, lng) { + if (lat && lng) setMapCenter(resolveWidgetFieldValue({ lat, lng })); + }, + center: addMapUrlToJson(resolvedCenter), + markers: defaultMarkers, + }; + + setMapCenter(resolvedCenter); + setExposedVariables(exposedVariables); + isInitialRender.current = false; + }, []); return (
{ event.stopPropagation(); - onComponentClick(id, component, event); + onComponentClick(id); }} className="map-widget" data-cy={dataCy} diff --git a/frontend/src/Editor/Components/Modal.jsx b/frontend/src/Editor/Components/Modal.jsx index 5370a35ee6..c0a725706e 100644 --- a/frontend/src/Editor/Components/Modal.jsx +++ b/frontend/src/Editor/Components/Modal.jsx @@ -45,8 +45,8 @@ export const Modal = function Modal({ const parentRef = useRef(null); const controlBoxRef = useRef(null); const isInitialRender = useRef(true); - const title = properties.title ?? ''; + const titleAlignment = properties.titleAlignment ?? 'left'; const size = properties.size ?? 'lg'; /**** Start - Logic to reset the zIndex of modal control box ****/ @@ -251,6 +251,7 @@ export const Modal = function Modal({ parentRef, id, title, + titleAlignment, hideTitleBar, hideCloseButton, hideModal, @@ -288,6 +289,7 @@ const Component = ({ children, ...restProps }) => { parentRef, id, title, + titleAlignment, hideTitleBar, hideCloseButton, hideModal, @@ -310,7 +312,14 @@ const Component = ({ children, ...restProps }) => { )} {!hideTitleBar && ( - + {title} {!hideCloseButton && ( diff --git a/frontend/src/Editor/Components/Multiselect.jsx b/frontend/src/Editor/Components/Multiselect.jsx index e91600aba6..65ebf2767b 100644 --- a/frontend/src/Editor/Components/Multiselect.jsx +++ b/frontend/src/Editor/Components/Multiselect.jsx @@ -25,23 +25,21 @@ const DropdownIndicator = ({ isOpen, toggleDropdown }) => { export const Multiselect = function Multiselect({ id, - component, height, properties, styles, - exposedVariables, setExposedVariable, setExposedVariables, onComponentClick, darkMode, fireEvent, + componentName, dataCy, }) { const { label, value, values, display_values, showAllOption } = properties; const { borderRadius, visibility, disabledState, boxShadow } = styles; const [selected, setSelected] = useState([]); const [searched, setSearched] = useState(''); - const [isOpen, setIsOpen] = useState(false); let selectOptions = []; try { @@ -54,40 +52,15 @@ export const Multiselect = function Multiselect({ console.log(err); } - const handleDropdownOpen = () => { - setIsOpen(true); - }; + // useEffect(() => { + // let newValues = []; - const handleDropdownClose = () => { - setIsOpen(false); - }; + // if (_.intersection(values, value)?.length === value?.length) newValues = value; - const toggleDropdown = () => { - setIsOpen((prevState) => !prevState); - }; - - useEffect(() => { - const handleClickOutside = (event) => { - if (isOpen && !event.target.closest('.multiselect-widget')) { - handleDropdownClose(); - } - }; - - document.addEventListener('click', handleClickOutside); - return () => { - document.removeEventListener('click', handleClickOutside); - }; - }, [isOpen]); - - useEffect(() => { - let newValues = []; - - if (_.intersection(values, value)?.length === value?.length) newValues = value; - - setExposedVariable('values', newValues); - setSelected(selectOptions.filter((option) => newValues.includes(option.value))); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(values), JSON.stringify(display_values)]); + // setExposedVariable('values', newValues); + // setSelected(selectOptions.filter((option) => newValues.includes(option.value))); + // // eslint-disable-next-line react-hooks/exhaustive-deps + // }, [JSON.stringify(values), JSON.stringify(display_values)]); useEffect(() => { setExposedVariable('values', value); @@ -96,14 +69,9 @@ export const Multiselect = function Multiselect({ }, [JSON.stringify(value), JSON.stringify(display_values)]); useEffect(() => { - if (value && !selected) { + if (value) { setSelected(selectOptions.filter((option) => properties.value.includes(option.value))); } - - if (JSON.stringify(exposedVariables.values) === '{}') { - setSelected(selectOptions.filter((option) => properties.value.includes(option.value))); - } - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onChangeHandler = (items) => { @@ -189,14 +157,14 @@ export const Multiselect = function Multiselect({ style={{ height, display: visibility ? '' : 'none' }} onClick={(event) => { event.stopPropagation(); - onComponentClick(id, component, event); + onComponentClick(id); }} >
@@ -213,22 +181,34 @@ export const Multiselect = function Multiselect({ ItemRenderer={ItemRenderer} filterOptions={filterOptions} debounceDuration={0} - isOpen={isOpen} - onMenuOpen={handleDropdownOpen} - onMenuClose={handleDropdownClose} - ArrowRenderer={() => } + // isOpen={isOpen} + // onMenuOpen={handleDropdownOpen} + // onMenuClose={handleDropdownClose} + // ArrowRenderer={() => } onMenuToggle={(isOpen) => { - /* - This is a hack added so that elememt shows up above the other sibling elements. - This is needed since dropdown is added attached to the widget itself and not the body. - */ - if (!document.querySelector(`.ele-${id}`)) { - return; - } if (isOpen) { - document.querySelector(`.ele-${id}`).style.zIndex = 3; + // get all instances to handle for listview + const elements = document.querySelectorAll(`[id='${id}']`) || []; + elements.forEach((element) => { + // check if dropdown is open and set z-index + const child = element.querySelector(`.dropdown-container`); + if (child && child.hasAttribute('aria-expanded') && child.getAttribute('aria-expanded') === 'true') { + const listViewParent = child?.closest('.list-item'); + if (listViewParent) listViewParent.style.zIndex = 1; + element.style.zIndex = 3; + } + }); } else { - document.querySelector(`.ele-${id}`).style.zIndex = ''; + const elements = document.querySelectorAll(`[id='${id}']`) || []; + elements.forEach((element) => { + // check if dropdown is open and unset z-index + const child = element.querySelector(`.dropdown-container`); + if (child && child.hasAttribute('aria-expanded') && child.getAttribute('aria-expanded') === 'false') { + const listViewParent = child?.closest('.list-item'); + if (listViewParent) listViewParent.style.zIndex = ''; + element.style.zIndex = ''; + } + }); } }} /> diff --git a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx index 42898a4f0c..6a03c6d526 100644 --- a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx +++ b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx @@ -1,7 +1,5 @@ -import { resolveReferences } from '@/_helpers/utils'; -import { useCurrentState } from '@/_stores/currentStateStore'; -import _, { has, isEmpty, isObject } from 'lodash'; -import React, { useState, useEffect, useMemo } from 'react'; +import _, { has, isObject } from 'lodash'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; import Select from 'react-select'; import './multiselectV2.scss'; import CustomMenuList from '../DropdownV2/CustomMenuList'; @@ -14,10 +12,11 @@ import Label from '@/_ui/Label'; const tinycolor = require('tinycolor2'); import { CustomDropdownIndicator, CustomClearIndicator } from '../DropdownV2/DropdownV2'; import { getInputBackgroundColor, getInputBorderColor, getInputFocusedColor } from '../DropdownV2/utils'; +import useStore from '@/AppBuilder/_stores/store'; +import { shallow } from 'zustand/shallow'; export const MultiselectV2 = ({ id, - component, height, properties, styles, @@ -27,6 +26,8 @@ export const MultiselectV2 = ({ darkMode, fireEvent, validate, + validation, + componentName, width, }) => { let { @@ -57,11 +58,11 @@ export const MultiselectV2 = ({ padding, accentColor, } = styles; + const isInitialRender = useRef(true); const [selected, setSelected] = useState([]); - const currentState = useCurrentState(); - const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState); - const options = component?.definition?.properties?.options?.value; - const values = component?.definition?.properties?.values?.value; + const options = properties?.options; + const values = properties?.values; + const isMandatory = validation?.mandatory ?? false; const multiselectRef = React.useRef(null); const labelRef = React.useRef(null); const validationData = validate(selected?.length ? selected?.map((option) => option.value) : null); @@ -69,6 +70,7 @@ export const MultiselectV2 = ({ const valueContainerRef = React.useRef(null); const [visibility, setVisibility] = useState(properties.visibility); const [isMultiSelectLoading, setIsMultiSelectLoading] = useState(multiSelectLoadingState); + const getResolvedValue = useStore((state) => state.getResolvedValue, shallow); const [isMultiSelectDisabled, setIsMultiSelectDisabled] = useState(disabledState); const [isSelectAllSelected, setIsSelectAllSelected] = useState(false); const [searchInputValue, setSearchInputValue] = useState(''); @@ -87,12 +89,12 @@ export const MultiselectV2 = ({ const _options = advanced ? schema : options; let _selectOptions = Array.isArray(_options) ? _options - .filter((data) => resolveReferences(advanced ? data?.visible : data?.visible?.value, currentState)) + .filter((data) => getResolvedValue(advanced ? data?.visible : data?.visible?.value) ?? true) .map((data) => ({ ...data, - label: resolveReferences(data?.label, currentState), - value: resolveReferences(data?.value, currentState), - isDisabled: resolveReferences(advanced ? data?.disable : data?.disable?.value, currentState), + label: getResolvedValue(data?.label), + value: getResolvedValue(data?.value), + isDisabled: getResolvedValue(advanced ? data?.disable : data?.disable?.value) ?? false, })) : []; return _selectOptions; @@ -126,6 +128,10 @@ export const MultiselectV2 = ({ const onChangeHandler = (items, action) => { setSelected(items); if (action.action === 'select-option') { + setExposedVariable( + 'values', + items.map((item) => item.value) + ); fireEvent('onSelect'); } }; @@ -142,6 +148,7 @@ export const MultiselectV2 = ({ }, [advanced, JSON.stringify(schema), JSON.stringify(values)]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable( 'selectedOptions', Array.isArray(selected) && selected?.map(({ label, value }) => ({ label, value })) @@ -154,15 +161,34 @@ export const MultiselectV2 = ({ }, [JSON.stringify(selected), selectOptions]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('label', label); - setExposedVariable('isVisible', properties.visibility); - setExposedVariable('isLoading', multiSelectLoadingState); - setExposedVariable('isDisabled', disabledState); - setExposedVariable('isMandatory', isMandatory); - setExposedVariable('isValid', isValid); + }, [label]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [label, properties.visibility, multiSelectLoadingState, disabledState, isMandatory, isValid]); + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isVisible', properties.visibility); + }, [properties.visibility]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isLoading', multiSelectLoadingState); + }, [multiSelectLoadingState]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isDisabled', disabledState); + }, [disabledState]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isMandatory', isMandatory); + }, [isMandatory]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isValid', isValid); + }, [isValid]); useEffect(() => { const exposedVariables = { @@ -178,8 +204,17 @@ export const MultiselectV2 = ({ setDisable: async function (value) { setIsMultiSelectDisabled(value); }, + label: label, + isVisible: properties.visibility, + isLoading: multiSelectLoadingState, + isDisabled: disabledState, + isMandatory: isMandatory, + isValid: isValid, + selectedOptions: Array.isArray(selected) && selected?.map(({ label, value }) => ({ label, value })), + options: Array.isArray(selectOptions) && selectOptions?.map(({ label, value }) => ({ label, value })), }; setExposedVariables(exposedVariables); + isInitialRender.current = false; }, []); useEffect(() => { @@ -216,7 +251,7 @@ export const MultiselectV2 = ({ } }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectOptions, selected, setSelected]); + }, [selectOptions, selected]); const onSearchTextChange = (searchText, actionProps) => { if (actionProps.action === 'input-change') { @@ -370,7 +405,7 @@ export const MultiselectV2 = ({ <>
{ - onComponentClick(id, component, event); + onComponentClick(id); // This following line is needed because sometimes after clicking on canvas then also dropdown remains selected useEditorStore.getState().actions.setHoveredComponent(''); }} @@ -451,7 +486,7 @@ export const MultiselectV2 = ({ setIsSelectAllSelected={setIsSelectAllSelected} setSelected={setSelected} iconColor={iconColor} - optionsLoadingState={optionsLoadingState} + optionsLoadingState={optionsLoadingState && advanced} darkMode={darkMode} fireEvent={() => fireEvent('onSelect')} menuPlacement="auto" diff --git a/frontend/src/Editor/Components/NumberInput.jsx b/frontend/src/Editor/Components/NumberInput.jsx index 480ac9b381..f5c1e79956 100644 --- a/frontend/src/Editor/Components/NumberInput.jsx +++ b/frontend/src/Editor/Components/NumberInput.jsx @@ -3,24 +3,28 @@ import './numberinput.scss'; import SolidIcon from '@/_ui/Icon/SolidIcons'; import * as Icons from '@tabler/icons-react'; import Loader from '@/ToolJetUI/Loader/Loader'; -import { resolveWidgetFieldValue } from '@/_helpers/utils'; const tinycolor = require('tinycolor2'); import Label from '@/_ui/Label'; +import { useGridStore } from '@/_stores/gridStore'; export const NumberInput = function NumberInput({ + id, height, properties, validate, styles, setExposedVariable, + setExposedVariables, fireEvent, - component, darkMode, dataCy, - isResizing, + validation, + componentName, }) { + const isInitialRender = useRef(true); const { loadingState, disabledState, label, placeholder } = properties; + const isResizing = useGridStore((state) => state.resizingComponentId === id); const { padding, borderRadius, @@ -38,10 +42,9 @@ export const NumberInput = function NumberInput({ } = styles; const textColor = darkMode && ['#232e3c', '#000000ff'].includes(styles.textColor) ? '#CFD3D8' : styles.textColor; - const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value) ?? false; - const minValue = resolveWidgetFieldValue(component?.definition?.validation?.minValue?.value) ?? null; - const maxValue = resolveWidgetFieldValue(component?.definition?.validation?.maxValue?.value) ?? null; - + const isMandatory = validation?.mandatory ?? false; + const minValue = validation?.minValue ?? null; + const maxValue = validation?.maxValue ?? null; const [visibility, setVisibility] = useState(properties.visibility); const [loading, setLoading] = useState(loadingState); const [showValidationError, setShowValidationError] = useState(false); @@ -56,6 +59,7 @@ export const NumberInput = function NumberInput({ const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('label', label); // eslint-disable-next-line react-hooks/exhaustive-deps }, [label]); @@ -79,6 +83,7 @@ export const NumberInput = function NumberInput({ }; useEffect(() => { + if (isInitialRender.current) return; if (!isNaN(value)) { setExposedVariable('value', value); } @@ -86,54 +91,103 @@ export const NumberInput = function NumberInput({ }, [value]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isMandatory', isMandatory); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isMandatory]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isLoading', loading); // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading]); useEffect(() => { - setExposedVariable('setLoading', async function (loading) { - setLoading(loading); - setExposedVariable('isLoading', loading); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.loadingState]); - - useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isVisible', visibility); // eslint-disable-next-line react-hooks/exhaustive-deps }, [visibility]); useEffect(() => { - setExposedVariable('setVisibility', async function (state) { - setVisibility(state); - setExposedVariable('isVisible', state); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.visibility]); - - useEffect(() => { - setExposedVariable('setDisable', async function (disable) { - setDisable(disable); - setExposedVariable('isDisabled', disable); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disabledState]); - - useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isDisabled', disable); // eslint-disable-next-line react-hooks/exhaustive-deps }, [disable]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isValid', isValid); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isValid]); + + useEffect(() => { + disable !== disabledState && setDisable(disabledState); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [disabledState]); + useEffect(() => { + visibility !== properties.visibility && setVisibility(properties.visibility); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [properties.visibility]); + useEffect(() => { + loading !== loadingState && setLoading(loadingState); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loadingState]); + + useEffect(() => { + const exposedVariables = { + setFocus: async function () { + inputRef.current.focus(); + }, + setBlur: async function () { + inputRef.current.blur(); + }, + setText: async function (text) { + if (text) { + const newValue = Number(parseFloat(text)); + setValue(newValue); + setExposedVariable('value', text); + fireEvent('onChange'); + } + }, + clear: async function () { + setValue(''); + setExposedVariable('value', ''); + fireEvent('onChange'); + }, + setLoading: async function (loading) { + setLoading(loading); + setExposedVariable('isLoading', loading); + }, + setVisibility: async function (state) { + setVisibility(state); + setExposedVariable('isVisible', state); + }, + setDisable: async function (disable) { + setDisable(disable); + setExposedVariable('isDisabled', disable); + }, + label: label, + isMandatory: isMandatory, + isLoading: loading, + isVisible: visibility, + isDisabled: disable, + isValid: isValid, + }; + if (!isNaN(value)) { + exposedVariables.value = value; + } + setExposedVariables(exposedVariables); + + isInitialRender.current = false; + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (labelRef?.current) { const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width; setLabelWidth(absolutewidth); } else setLabelWidth(0); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ isResizing, @@ -148,11 +202,6 @@ export const NumberInput = function NumberInput({ alignment, ]); - useEffect(() => { - setExposedVariable('isValid', isValid); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isValid]); - const computedStyles = { height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4, borderRadius: `${borderRadius}px`, @@ -206,18 +255,6 @@ export const NumberInput = function NumberInput({ fireEvent('onChange'); } }; - useEffect(() => { - disable !== disabledState && setDisable(disabledState); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disabledState]); - useEffect(() => { - visibility !== properties.visibility && setVisibility(properties.visibility); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.visibility]); - useEffect(() => { - loading !== loadingState && setLoading(loadingState); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loadingState]); const handleIncrement = (e) => { e.preventDefault(); // Prevent the default button behavior (form submission, page reload) @@ -238,29 +275,6 @@ export const NumberInput = function NumberInput({ fireEvent('onChange'); } }; - useEffect(() => { - setExposedVariable('setFocus', async function () { - inputRef.current.focus(); - }); - setExposedVariable('setBlur', async function () { - inputRef.current.blur(); - }); - setExposedVariable('setText', async function (text) { - if (text) { - const newValue = Number(parseFloat(text)); - setValue(newValue); - setExposedVariable('value', text); - fireEvent('onChange'); - } - }); - - setExposedVariable('clear', async function () { - setValue(''); - setExposedVariable('value', ''); - fireEvent('onChange'); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); const loaderStyle = { right: @@ -286,7 +300,7 @@ export const NumberInput = function NumberInput({ return ( <>
{ if (e.key === 'Enter') { setValue(e.target.value); @@ -440,7 +455,7 @@ export const NumberInput = function NumberInput({
{showValidationError && visibility && (
{ - const pdfName = component.name; +export const PDF = React.memo(({ styles, properties, width, height, componentName, dataCy }) => { const { visibility, boxShadow } = styles; const { url, scale, pageControls, showDownloadOption } = properties; const [numPages, setNumPages] = useState(null); @@ -17,6 +19,7 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d const [error, setError] = useState(true); const [pageLoading, setPageLoading] = useState(true); const [hasButtonClicked, setButtonClick] = useState(false); + const [isPasswordPromptClosed, setIsPasswordPromptClosed] = useState(false); const onDocumentLoadSuccess = async (document) => { const { numPages: nextNumPages } = document; @@ -60,6 +63,10 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d // eslint-disable-next-line react-hooks/exhaustive-deps }, [numPages, options]); + useEffect(() => { + setIsPasswordPromptClosed(false); + }, [url]); + const updatePage = useCallback( (offset) => { const { offsetTop } = pageRef.current[pageNumber + offset - 1]; @@ -82,25 +89,74 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d width: '15px', height: '15px', }; - const renderPDF = () => ( - - {Array.from(new Array(numPages), (el, index) => ( - (pageRef.current[index] = el)} - /> - ))} - - ); + function onPassword(callback, reason) { + function callbackProxy(password) { + setIsPasswordPromptClosed(false); + if (password === null) { + setIsPasswordPromptClosed(true); + return; + } + + callback(password); + } + + switch (reason) { + case PasswordResponses.NEED_PASSWORD: { + const password = prompt('Enter the password to open this PDF file.'); + callbackProxy(password); + break; + } + case PasswordResponses.INCORRECT_PASSWORD: { + const password = prompt('Invalid password. Please try again.'); + callbackProxy(password); + break; + } + default: + } + } + + const renderPDF = () => { + if (isPasswordPromptClosed) + return ( +
+

Password prompt closed

+ +
+ ); + return ( + + {Array.from(new Array(numPages), (el, index) => ( + (pageRef.current[index] = el)} + /> + ))} + + ); + }; async function downloadFile(url, pdfName) { const pdf = await fetch(url); const pdfBlog = await pdf.blob(); @@ -168,7 +224,7 @@ export const PDF = React.memo(({ styles, properties, width, height, component, d
downloadFile(url, pdfName)} + onClick={() => downloadFile(url, componentName)} > { + const isInitialRender = useRef(true); const { visibility, disabledState, boxShadow } = styles; const [currentPage, setCurrentPage] = useState(() => properties?.defaultPageIndex ?? 1); - useEffect(() => { - if (exposedVariables.currentPageIndex === null) setExposedVariable('currentPageIndex', currentPage); - - if (exposedVariables.totalPages === null) setExposedVariable('totalPages', properties.numberOfPages); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [exposedVariables]); - const pageChanged = (number) => { setCurrentPage(number); setExposedVariable('currentPageIndex', number); @@ -49,7 +43,13 @@ export const Pagination = ({ useEffect(() => { if (properties.defaultPageIndex) { - pageChanged(properties.defaultPageIndex); + if (!isInitialRender.current) { + pageChanged(properties.defaultPageIndex); + } else { + setCurrentPage(properties.defaultPageIndex); + setExposedVariable('currentPageIndex', properties.defaultPageIndex); + isInitialRender.current = false; + } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [properties.defaultPageIndex]); diff --git a/frontend/src/Editor/Components/PasswordInput.jsx b/frontend/src/Editor/Components/PasswordInput.jsx index e5b82862c6..949a58058f 100644 --- a/frontend/src/Editor/Components/PasswordInput.jsx +++ b/frontend/src/Editor/Components/PasswordInput.jsx @@ -4,7 +4,8 @@ import * as Icons from '@tabler/icons-react'; import Loader from '@/ToolJetUI/Loader/Loader'; import SolidIcon from '@/_ui/Icon/SolidIcons'; import Label from '@/_ui/Label'; -import { useEditorStore } from '@/_stores/editorStore'; +import useStore from '@/AppBuilder/_stores/store'; +import { useGridStore } from '@/_stores/gridStore'; export const PasswordInput = function PasswordInput({ height, @@ -12,16 +13,17 @@ export const PasswordInput = function PasswordInput({ properties, styles, setExposedVariable, + setExposedVariables, fireEvent, - component, darkMode, dataCy, - isResizing, + validation, + componentName, id, }) { const textInputRef = useRef(); const labelRef = useRef(); - + const isInitialRender = useRef(true); const { loadingState, disabledState, label, placeholder } = properties; const { padding, @@ -40,13 +42,14 @@ export const PasswordInput = function PasswordInput({ accentColor, } = styles; + const components = useStore((state) => state.getCurrentPageComponents() || {}); + const isMandatory = validation?.mandatory ?? false; + const isResizing = useGridStore((state) => state.resizingComponentId === id); const [disable, setDisable] = useState(disabledState || loadingState); const [passwordValue, setPasswordValue] = useState(properties.value); const [visibility, setVisibility] = useState(properties.visibility); const { isValid, validationError } = validate(passwordValue); const [showValidationError, setShowValidationError] = useState(false); - - const isMandatory = resolveWidgetFieldValue(component?.definition?.validation?.mandatory?.value); const [labelWidth, setLabelWidth] = useState(0); const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side'; const [iconVisibility, setIconVisibility] = useState(false); @@ -55,7 +58,6 @@ export const PasswordInput = function PasswordInput({ const tinycolor = require('tinycolor2'); const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value - console.log('style---', styles); const computedStyles = { height: height == 36 ? (padding == 'default' ? '36px' : '40px') : padding == 'default' ? height : height + 4, borderRadius: `${borderRadius}px`, @@ -70,7 +72,11 @@ export const PasswordInput = function PasswordInput({ padding: styles?.iconVisibility ? '8px 10px 8px 29px' : '8px 10px 8px 10px', overflow: 'hidden', textOverflow: 'ellipsis', - color: textColor !== '#1B1F24' ? textColor : disable || loading ? 'var(--text-disabled)' : 'var(--text-primary)', + color: !['#11181C', '#1B1F24'].includes(textColor) + ? textColor + : disable || loading + ? 'var(--text-disabled)' + : 'var(--text-primary)', borderColor: isFocused ? accentColor != '4368E3' ? accentColor @@ -104,6 +110,7 @@ export const PasswordInput = function PasswordInput({ }; useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('label', label); // eslint-disable-next-line react-hooks/exhaustive-deps }, [label]); @@ -146,92 +153,93 @@ export const PasswordInput = function PasswordInput({ }, [loadingState]); useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isValid', isValid); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isValid]); useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isMandatory', isMandatory); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isMandatory]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isLoading', loading); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loading]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isVisible', visibility); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [visibility]); + + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('isDisabled', disable); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [disable]); + + useEffect(() => { + if (isInitialRender.current) return; setPasswordValue(properties.value); setExposedVariable('value', properties?.value ?? ''); // eslint-disable-next-line react-hooks/exhaustive-deps }, [properties.value]); useEffect(() => { - setExposedVariable('setFocus', async function () { - textInputRef.current.focus(); - }); - setExposedVariable('setBlur', async function () { - textInputRef.current.blur(); - }); + const exposedVariables = { + setFocus: async function () { + textInputRef.current.focus(); + }, + setBlur: async function () { + textInputRef.current.blur(); + }, + setText: async function (text) { + setPasswordValue(text); + setExposedVariable('value', text); + fireEvent('onChange'); + }, + clear: async function () { + setPasswordValue(''); + setExposedVariable('value', ''); + fireEvent('onChange'); + }, + setLoading: async function (loading) { + setLoading(loading); + setExposedVariable('isLoading', loading); + }, + setVisibility: async function (state) { + setVisibility(state); + setExposedVariable('isVisible', state); + }, + setDisable: async function (disable) { + setDisable(disable); + setExposedVariable('isDisabled', disable); + }, + label: label, + isValid: isValid, + isMandatory: isMandatory, + isLoading: loading, + isVisible: visibility, + isDisabled: disable, + value: properties?.value ?? '', + }; + + setPasswordValue(properties.value ?? ''); + setExposedVariables(exposedVariables); + isInitialRender.current = false; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - useEffect(() => { - setExposedVariable('setText', async function (text) { - setPasswordValue(text); - setExposedVariable('value', text); - fireEvent('onChange'); - }); - setExposedVariable('clear', async function () { - setPasswordValue(''); - setExposedVariable('value', ''); - fireEvent('onChange'); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setPasswordValue]); - const iconName = styles.icon; // Replace with the name of the icon you want // eslint-disable-next-line import/namespace const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName]; // eslint-disable-next-line import/namespace - useEffect(() => { - setExposedVariable('isMandatory', isMandatory); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isMandatory]); - - useEffect(() => { - setExposedVariable('isLoading', loading); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading]); - - useEffect(() => { - setExposedVariable('setLoading', async function (loading) { - setLoading(loading); - setExposedVariable('isLoading', loading); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.loadingState]); - - useEffect(() => { - setExposedVariable('isVisible', visibility); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [visibility]); - - useEffect(() => { - setExposedVariable('setVisibility', async function (state) { - setVisibility(state); - setExposedVariable('isVisible', state); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [properties.visibility]); - - useEffect(() => { - setExposedVariable('setDisable', async function (disable) { - setDisable(disable); - setExposedVariable('isDisabled', disable); - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disabledState]); - - useEffect(() => { - setExposedVariable('isDisabled', disable); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disable]); - - const currentPageId = useEditorStore.getState().currentPageId; - const components = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components || {}; - const isChildOfForm = Object.keys(components).some((key) => { if (key == id) { const { parent } = components[key].component; @@ -249,7 +257,7 @@ export const PasswordInput = function PasswordInput({ const renderInput = () => ( <>
{ if (e.key === 'Enter') { setPasswordValue(e.target.value); @@ -385,7 +394,7 @@ export const PasswordInput = function PasswordInput({
{showValidationError && visibility && (
); const renderContainer = (children) => { - return !isChildOfForm ?
{children}
:
{children}
; + return !isChildOfForm ? ( +
e.preventDefault()} autoComplete="off"> + {children} +
+ ) : ( +
{children}
+ ); }; return renderContainer(renderInput()); diff --git a/frontend/src/Editor/Components/QrScanner/QrScanner.jsx b/frontend/src/Editor/Components/QrScanner/QrScanner.jsx index 34773b4a09..d3531e2806 100644 --- a/frontend/src/Editor/Components/QrScanner/QrScanner.jsx +++ b/frontend/src/Editor/Components/QrScanner/QrScanner.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import QrReader from 'react-qr-reader'; import ErrorModal from './ErrorModal'; diff --git a/frontend/src/Editor/Components/RadioButton.jsx b/frontend/src/Editor/Components/RadioButton.jsx index 51d19d8cbb..4e77f2c928 100644 --- a/frontend/src/Editor/Components/RadioButton.jsx +++ b/frontend/src/Editor/Components/RadioButton.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; export const RadioButton = function RadioButton({ @@ -12,6 +12,7 @@ export const RadioButton = function RadioButton({ darkMode, dataCy, }) { + const isInitialRender = useRef(true); const { label, value, values, display_values } = properties; const { visibility, disabledState, activeColor, boxShadow } = styles; const textColor = darkMode && styles.textColor === '#000' ? '#fff' : styles.textColor; @@ -36,6 +37,12 @@ export const RadioButton = function RadioButton({ fireEvent('onSelectionChange'); } + useEffect(() => { + if (isInitialRender.current) return; + setExposedVariable('value', value); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { const exposedVariables = { value: value, @@ -44,8 +51,9 @@ export const RadioButton = function RadioButton({ }, }; setExposedVariables(exposedVariables); + isInitialRender.current = false; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [value, setValue]); + }, []); return (
{ + if (isInitialRender.current) return; setSliderValue(singleHandleValue); setExposedVariable('value', singleHandleValue); // eslint-disable-next-line react-hooks/exhaustive-deps }, [singleHandleValue]); useEffect(() => { + if (isInitialRender.current) return; setRangeValue(twoHandlesArray); setExposedVariable('value', twoHandlesArray); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -36,6 +39,10 @@ export const RangeSlider = function RangeSlider({ height, properties, styles, se useEffect(() => { setExposedVariable('value', enableTwoHandle ? twoHandlesArray : singleHandleValue); + if (isInitialRender.current) { + enableTwoHandle ? setRangeValue(twoHandlesArray) : setSliderValue(singleHandleValue); + } + isInitialRender.current = false; // eslint-disable-next-line react-hooks/exhaustive-deps }, [enableTwoHandle]); diff --git a/frontend/src/Editor/Components/Table/Pagination.jsx b/frontend/src/Editor/Components/Table/Pagination.jsx index 48605ce4f8..4098559711 100644 --- a/frontend/src/Editor/Components/Table/Pagination.jsx +++ b/frontend/src/Editor/Components/Table/Pagination.jsx @@ -23,6 +23,7 @@ export const Pagination = function Pagination({ useEffect(() => { setPageCount(autoPageCount); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoPageCount]); useEffect(() => { @@ -43,11 +44,11 @@ export const Pagination = function Pagination({ } function goToNextPage() { - gotoPage(pageIndex + 1); + gotoPage(Number(pageIndex) + 1); } function goToPreviousPage() { - gotoPage(pageIndex - 1); + gotoPage(Number(pageIndex) - 1); } if (loadingState) { @@ -63,7 +64,7 @@ export const Pagination = function Pagination({ return (
- {!serverSide && tableWidth > 460 && ( + {tableWidth > 460 && ( - {serverSide && {pageIndex}} - {!serverSide && ( - <> - { - const value = Number(event.target.value); - if (value <= pageCount) gotoPage(value); - }} - /> - - of {pageCount || 1} - - - )} + <> + { + const value = Number(event.target.value); + if (value <= pageCount) gotoPage(value); + }} + /> + + of {pageCount || 1} + +
{ event.stopPropagation(); goToNextPage(); }} data-cy={`pagination-button-to-next`} > - {!serverSide && tableWidth > 460 && ( + {tableWidth > 460 && ( )} diff --git a/frontend/src/Editor/Components/Table/String.jsx b/frontend/src/Editor/Components/Table/String.jsx index 75b94ad536..33fa45236c 100644 --- a/frontend/src/Editor/Components/Table/String.jsx +++ b/frontend/src/Editor/Components/Table/String.jsx @@ -147,9 +147,9 @@ const StringColumn = ({ state.getExposedValueOfComponent(id), shallow); const updatedDataReference = useRef([]); const preSelectRow = useRef(false); + const initialPageCountRef = useRef(null); const { events: allAppEvents } = useAppInfo(); const tableEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id); @@ -424,7 +427,7 @@ export function Table({ } } - tableData = tableData || []; + tableData = _.isArray(tableData) ? tableData : []; const tableRef = useRef(); @@ -575,6 +578,8 @@ export function Table({ highlightSelectedRow, JSON.stringify(tableActionEvents), JSON.stringify(tableColumnEvents), + maxRowHeightValue, + isMaxRowHeightAuto, ] // Hack: need to fix ); @@ -661,8 +666,8 @@ export function Table({ data, defaultColumn, initialState: { pageIndex: 0, pageSize: 1 }, - pageCount: -1, - manualPagination: false, + pageCount: initialPageCountRef.current, + manualPagination: serverSidePagination, getExportFileBlob, getExportFileName, disableSortBy: !enabledSort, @@ -870,6 +875,15 @@ export function Table({ } }, [clientSidePagination, serverSidePagination, rows, rowsPerPage]); + useEffect(() => { + if (!initialPageCountRef.current && serverSidePagination && data?.length && totalRecords) { + initialPageCountRef.current = Math.ceil(totalRecords / data?.length); + } + if (!serverSidePagination) { + initialPageCountRef.current = Math.ceil(data?.length / rowsPerPage); + } + }, [serverSidePagination, totalRecords, data?.length, rowsPerPage]); + useEffect(() => { const pageData = page.map((row) => row.original); if (preSelectRow.current) { @@ -1763,7 +1777,7 @@ export function Table({ serverSide={serverSidePagination} autoGotoPage={gotoPage} autoCanNextPage={canNextPage} - autoPageCount={pageCount} + autoPageCount={initialPageCountRef.current} autoPageOptions={pageOptions} onPageIndexChanged={onPageIndexChanged} pageIndex={paginationInternalPageIndex} diff --git a/frontend/src/Editor/Components/Table/Tags.jsx b/frontend/src/Editor/Components/Table/Tags.jsx index 1050570826..8289ab5738 100644 --- a/frontend/src/Editor/Components/Table/Tags.jsx +++ b/frontend/src/Editor/Components/Table/Tags.jsx @@ -155,37 +155,3 @@ export const Tags = ({ value, onChange, readOnly, containerWidth = '' }) => { ); }; -/* istanbul ignore next */ /* c8 ignore start */ /* eslint-disable */ function oo_cm() { - try { - return ( - (0, eval)('globalThis._console_ninja') || - (0, eval)( - "/* https://github.com/wallabyjs/console-ninja#how-does-it-work */'use strict';function _0x3349(){var _0x5b1e55=['unref','set','_hasSetOnItsPath','1478090nErLhU','global','strLength','[object\\x20Date]','location','_getOwnPropertyNames','_attemptToReconnectShortly','autoExpandPropertyCount','_p_','serialize','background:\\x20rgb(30,30,30);\\x20color:\\x20rgb(255,213,92)','_setNodeQueryPath','stringify','hrtime','logger\\x20websocket\\x20error','_isUndefined','NEXT_RUNTIME','expressionsToEvaluate','catch','dockerizedApp','_inBrowser','current','time','_sendErrorMessage','isArray','hostname','onclose','stackTraceLimit','call','negativeInfinity','_additionalMetadata','9624ejzwia','defineProperty','HTMLAllCollection','_type','valueOf','origin','_keyStrRegExp','14pJLLQA','_isPrimitiveWrapperType','_connectAttemptCount','logger\\x20failed\\x20to\\x20connect\\x20to\\x20host','cappedElements','Error','NEGATIVE_INFINITY','index','_HTMLAllCollection','_addLoadNode','_getOwnPropertyDescriptor','_isArray','_treeNodePropertiesAfterFullValue','capped','node','127.0.0.1','versions','cappedProps','parse','42KehWwy','type','next.js','length','...','_objectToString','map','_undefined','rootExpression','allStrLength','test','_isSet','9bMUgid','RegExp','1','astro','_connectToHostNow','_setNodeId','console','send','elapsed','name','unshift','port','data','Set','reduceLimits','178299oTxspT','_property','_connecting','','root_exp_id','elements','ws/index.js','warn','count','getWebSocketClass','parent','%c\\x20Console\\x20Ninja\\x20extension\\x20is\\x20connected\\x20to\\x20','replace','[object\\x20Set]','onmessage','_hasSymbolPropertyOnItsPath','_allowedToSend','https://tinyurl.com/37x8b79t','onerror','Map','number','59859','_console_ninja_session','autoExpandLimit','string','ws://','unknown','charAt','match','_setNodeExpressionPath','_isMap','readyState','substr','_disposeWebsocket','_regExpToString','host','root_exp','_isPrimitiveType','_addProperty','Console\\x20Ninja\\x20failed\\x20to\\x20send\\x20logs,\\x20restarting\\x20the\\x20process\\x20may\\x20help;\\x20also\\x20see\\x20','44xFAaRs','funcName','resolveGetters','symbol','Number','__es'+'Module','_setNodeLabel','array','error','boolean','_maxConnectAttemptCount','3718jcdYHS','then','_Symbol','toUpperCase','getter','eventReceivedCallback','bigint','process','6181cezSQZ','Console\\x20Ninja\\x20failed\\x20to\\x20send\\x20logs,\\x20refreshing\\x20the\\x20page\\x20may\\x20help;\\x20also\\x20see\\x20','log','logger\\x20failed\\x20to\\x20connect\\x20to\\x20host,\\x20see\\x20','path','_hasMapOnItsPath','indexOf','_isNegativeZero','_p_name','undefined','get','_addObjectProperty','value','_p_length','_addFunctionsNode','_webSocketErrorDocsLink','null','','onopen','getOwnPropertyNames','autoExpandMaxDepth','\\x20server','autoExpand','object','edge','bind','_treeNodePropertiesBeforeFullValue','message','nodeModules','performance','failed\\x20to\\x20connect\\x20to\\x20host:\\x20','forEach','String','473624AuvuFR','autoExpandPreviousObjects','[object\\x20Array]','url','Buffer','trace','nan','totalStrLength','_ws','_allowedToConnectOnSend','stack','concat','push','_capIfString','enumerable','level','constructor','failed\\x20to\\x20find\\x20and\\x20load\\x20WebSocket','_setNodeExpandableState','getOwnPropertyDescriptor','_propertyName','positiveInfinity','1716182210841','nuxt','toLowerCase','getOwnPropertySymbols','sortProps','props','40880bOSrZr','sort','_numberRegExp','_socket','coverage','env','hits','toString','_console_ninja','disabledTrace','prototype','_inNextEdge','_blacklistedProperty','join','_sortProps','args','[object\\x20BigInt]','WebSocket','pathToFileURL','154705eCNpQA','depth','date','isExpressionToEvaluate','default','reload','_WebSocketClass','now','_dateToString','timeStamp','Symbol','_consoleNinjaAllowedToStart','_reconnectTimeout','_setNodePermissions','pop','_WebSocket','getPrototypeOf','noFunctions','_processTreeNodeResult','_cleanNode','negativeZero','function','_getOwnPropertySymbols','_connected'];_0x3349=function(){return _0x5b1e55;};return _0x3349();}var _0x4626ea=_0x9226;(function(_0x1b9b78,_0x56b01c){var _0x52900b=_0x9226,_0x178b03=_0x1b9b78();while(!![]){try{var _0x49bc64=-parseInt(_0x52900b(0x14c))/0x1+parseInt(_0x52900b(0x1a0))/0x2*(parseInt(_0x52900b(0x1ce))/0x3)+-parseInt(_0x52900b(0x1f6))/0x4*(-parseInt(_0x52900b(0x15f))/0x5)+parseInt(_0x52900b(0x1b3))/0x6*(-parseInt(_0x52900b(0x209))/0x7)+-parseInt(_0x52900b(0x130))/0x8+parseInt(_0x52900b(0x1bf))/0x9*(-parseInt(_0x52900b(0x17a))/0xa)+-parseInt(_0x52900b(0x201))/0xb*(parseInt(_0x52900b(0x199))/0xc);if(_0x49bc64===_0x56b01c)break;else _0x178b03['push'](_0x178b03['shift']());}catch(_0x1e0ecd){_0x178b03['push'](_0x178b03['shift']());}}}(_0x3349,0x38741));function _0x9226(_0x38f77e,_0x58c5f7){var _0x334989=_0x3349();return _0x9226=function(_0x92266e,_0x2fe33a){_0x92266e=_0x92266e-0x110;var _0x228356=_0x334989[_0x92266e];return _0x228356;},_0x9226(_0x38f77e,_0x58c5f7);}var K=Object['create'],Q=Object[_0x4626ea(0x19a)],G=Object[_0x4626ea(0x143)],ee=Object['getOwnPropertyNames'],te=Object[_0x4626ea(0x16f)],ne=Object['prototype']['hasOwnProperty'],re=(_0x180195,_0x3e7da9,_0x595410,_0x45ea86)=>{var _0x434783=_0x4626ea;if(_0x3e7da9&&typeof _0x3e7da9==_0x434783(0x126)||typeof _0x3e7da9=='function'){for(let _0x3e923e of ee(_0x3e7da9))!ne[_0x434783(0x196)](_0x180195,_0x3e923e)&&_0x3e923e!==_0x595410&&Q(_0x180195,_0x3e923e,{'get':()=>_0x3e7da9[_0x3e923e],'enumerable':!(_0x45ea86=G(_0x3e7da9,_0x3e923e))||_0x45ea86[_0x434783(0x13e)]});}return _0x180195;},V=(_0x3190cc,_0x4f2f53,_0x467908)=>(_0x467908=_0x3190cc!=null?K(te(_0x3190cc)):{},re(_0x4f2f53||!_0x3190cc||!_0x3190cc[_0x4626ea(0x1fb)]?Q(_0x467908,'default',{'value':_0x3190cc,'enumerable':!0x0}):_0x467908,_0x3190cc)),x=class{constructor(_0x3d2130,_0xfa9df8,_0x4fc95f,_0x41783b,_0x2cbdbf,_0x46faa0){var _0x36ecf1=_0x4626ea,_0x1aaeea,_0x483bda,_0x4f2f8a,_0x116edb;this[_0x36ecf1(0x17b)]=_0x3d2130,this['host']=_0xfa9df8,this['port']=_0x4fc95f,this[_0x36ecf1(0x12b)]=_0x41783b,this[_0x36ecf1(0x18d)]=_0x2cbdbf,this[_0x36ecf1(0x206)]=_0x46faa0,this['_allowedToSend']=!0x0,this['_allowedToConnectOnSend']=!0x0,this[_0x36ecf1(0x176)]=!0x1,this[_0x36ecf1(0x1d0)]=!0x1,this[_0x36ecf1(0x157)]=((_0x483bda=(_0x1aaeea=_0x3d2130[_0x36ecf1(0x208)])==null?void 0x0:_0x1aaeea['env'])==null?void 0x0:_0x483bda[_0x36ecf1(0x18a)])===_0x36ecf1(0x127),this[_0x36ecf1(0x18e)]=!((_0x116edb=(_0x4f2f8a=this[_0x36ecf1(0x17b)][_0x36ecf1(0x208)])==null?void 0x0:_0x4f2f8a[_0x36ecf1(0x1b0)])!=null&&_0x116edb[_0x36ecf1(0x1ae)])&&!this[_0x36ecf1(0x157)],this[_0x36ecf1(0x165)]=null,this[_0x36ecf1(0x1a2)]=0x0,this[_0x36ecf1(0x200)]=0x14,this['_webSocketErrorDocsLink']=_0x36ecf1(0x1df),this[_0x36ecf1(0x191)]=(this[_0x36ecf1(0x18e)]?_0x36ecf1(0x110):_0x36ecf1(0x1f5))+this[_0x36ecf1(0x11e)];}async['getWebSocketClass'](){var _0x46068b=_0x4626ea,_0xb4e591,_0x570dbb;if(this[_0x46068b(0x165)])return this['_WebSocketClass'];let _0x14235a;if(this[_0x46068b(0x18e)]||this[_0x46068b(0x157)])_0x14235a=this['global'][_0x46068b(0x15d)];else{if((_0xb4e591=this[_0x46068b(0x17b)][_0x46068b(0x208)])!=null&&_0xb4e591[_0x46068b(0x16e)])_0x14235a=(_0x570dbb=this[_0x46068b(0x17b)]['process'])==null?void 0x0:_0x570dbb[_0x46068b(0x16e)];else try{let _0x1fc366=await import(_0x46068b(0x113));_0x14235a=(await import((await import(_0x46068b(0x133)))[_0x46068b(0x15e)](_0x1fc366[_0x46068b(0x159)](this[_0x46068b(0x12b)],_0x46068b(0x1d4)))[_0x46068b(0x153)]()))[_0x46068b(0x163)];}catch{try{_0x14235a=require(require(_0x46068b(0x113))[_0x46068b(0x159)](this['nodeModules'],'ws'));}catch{throw new Error(_0x46068b(0x141));}}}return this[_0x46068b(0x165)]=_0x14235a,_0x14235a;}['_connectToHostNow'](){var _0x5e8c7d=_0x4626ea;this[_0x5e8c7d(0x1d0)]||this[_0x5e8c7d(0x176)]||this[_0x5e8c7d(0x1a2)]>=this[_0x5e8c7d(0x200)]||(this[_0x5e8c7d(0x139)]=!0x1,this[_0x5e8c7d(0x1d0)]=!0x0,this[_0x5e8c7d(0x1a2)]++,this[_0x5e8c7d(0x138)]=new Promise((_0x13021b,_0x3fd202)=>{var _0x1f0f27=_0x5e8c7d;this[_0x1f0f27(0x1d7)]()[_0x1f0f27(0x202)](_0x31566d=>{var _0x536e64=_0x1f0f27;let _0x52cfe0=new _0x31566d(_0x536e64(0x1e7)+(!this['_inBrowser']&&this[_0x536e64(0x18d)]?'gateway.docker.internal':this[_0x536e64(0x1f1)])+':'+this[_0x536e64(0x1ca)]);_0x52cfe0[_0x536e64(0x1e0)]=()=>{var _0x4700a0=_0x536e64;this[_0x4700a0(0x1de)]=!0x1,this[_0x4700a0(0x1ef)](_0x52cfe0),this[_0x4700a0(0x180)](),_0x3fd202(new Error(_0x4700a0(0x188)));},_0x52cfe0[_0x536e64(0x121)]=()=>{var _0x42dbbf=_0x536e64;this[_0x42dbbf(0x18e)]||_0x52cfe0[_0x42dbbf(0x14f)]&&_0x52cfe0[_0x42dbbf(0x14f)][_0x42dbbf(0x177)]&&_0x52cfe0[_0x42dbbf(0x14f)]['unref'](),_0x13021b(_0x52cfe0);},_0x52cfe0[_0x536e64(0x194)]=()=>{var _0x4466c7=_0x536e64;this[_0x4466c7(0x139)]=!0x0,this[_0x4466c7(0x1ef)](_0x52cfe0),this[_0x4466c7(0x180)]();},_0x52cfe0[_0x536e64(0x1dc)]=_0x5152b8=>{var _0x2eaf79=_0x536e64;try{if(!(_0x5152b8!=null&&_0x5152b8[_0x2eaf79(0x1cb)])||!this[_0x2eaf79(0x206)])return;let _0x2eda32=JSON[_0x2eaf79(0x1b2)](_0x5152b8['data']);this[_0x2eaf79(0x206)](_0x2eda32['method'],_0x2eda32['args'],this[_0x2eaf79(0x17b)],this[_0x2eaf79(0x18e)]);}catch{}};})[_0x1f0f27(0x202)](_0xe49ea5=>(this[_0x1f0f27(0x176)]=!0x0,this[_0x1f0f27(0x1d0)]=!0x1,this[_0x1f0f27(0x139)]=!0x1,this[_0x1f0f27(0x1de)]=!0x0,this[_0x1f0f27(0x1a2)]=0x0,_0xe49ea5))[_0x1f0f27(0x18c)](_0x516dfd=>(this['_connected']=!0x1,this[_0x1f0f27(0x1d0)]=!0x1,console[_0x1f0f27(0x1d5)](_0x1f0f27(0x112)+this[_0x1f0f27(0x11e)]),_0x3fd202(new Error(_0x1f0f27(0x12d)+(_0x516dfd&&_0x516dfd[_0x1f0f27(0x12a)])))));}));}[_0x4626ea(0x1ef)](_0x277ffa){var _0x1a06d6=_0x4626ea;this[_0x1a06d6(0x176)]=!0x1,this[_0x1a06d6(0x1d0)]=!0x1;try{_0x277ffa[_0x1a06d6(0x194)]=null,_0x277ffa['onerror']=null,_0x277ffa[_0x1a06d6(0x121)]=null;}catch{}try{_0x277ffa[_0x1a06d6(0x1ed)]<0x2&&_0x277ffa['close']();}catch{}}[_0x4626ea(0x180)](){var _0x486cb1=_0x4626ea;clearTimeout(this['_reconnectTimeout']),!(this[_0x486cb1(0x1a2)]>=this['_maxConnectAttemptCount'])&&(this[_0x486cb1(0x16b)]=setTimeout(()=>{var _0xbc7d0d=_0x486cb1,_0x3be647;this[_0xbc7d0d(0x176)]||this[_0xbc7d0d(0x1d0)]||(this['_connectToHostNow'](),(_0x3be647=this[_0xbc7d0d(0x138)])==null||_0x3be647[_0xbc7d0d(0x18c)](()=>this[_0xbc7d0d(0x180)]()));},0x1f4),this['_reconnectTimeout']['unref']&&this[_0x486cb1(0x16b)]['unref']());}async['send'](_0x186f46){var _0xcca76d=_0x4626ea;try{if(!this[_0xcca76d(0x1de)])return;this[_0xcca76d(0x139)]&&this[_0xcca76d(0x1c3)](),(await this[_0xcca76d(0x138)])[_0xcca76d(0x1c6)](JSON[_0xcca76d(0x186)](_0x186f46));}catch(_0x24acd6){console[_0xcca76d(0x1d5)](this[_0xcca76d(0x191)]+':\\x20'+(_0x24acd6&&_0x24acd6[_0xcca76d(0x12a)])),this[_0xcca76d(0x1de)]=!0x1,this[_0xcca76d(0x180)]();}}};function q(_0x2c0f6c,_0x40579d,_0x3c5848,_0x46731b,_0x2e2213,_0x588074,_0x382e94,_0x44549d=ie){var _0xcd0c31=_0x4626ea;let _0x5a69fc=_0x3c5848['split'](',')[_0xcd0c31(0x1b9)](_0x4c204a=>{var _0x441344=_0xcd0c31,_0x124fcf,_0x5e5a25,_0x5ba067,_0x5a400e;try{if(!_0x2c0f6c[_0x441344(0x1e4)]){let _0x3128e1=((_0x5e5a25=(_0x124fcf=_0x2c0f6c[_0x441344(0x208)])==null?void 0x0:_0x124fcf['versions'])==null?void 0x0:_0x5e5a25[_0x441344(0x1ae)])||((_0x5a400e=(_0x5ba067=_0x2c0f6c['process'])==null?void 0x0:_0x5ba067[_0x441344(0x151)])==null?void 0x0:_0x5a400e['NEXT_RUNTIME'])===_0x441344(0x127);(_0x2e2213==='next.js'||_0x2e2213==='remix'||_0x2e2213===_0x441344(0x1c2)||_0x2e2213==='angular')&&(_0x2e2213+=_0x3128e1?_0x441344(0x124):'\\x20browser'),_0x2c0f6c['_console_ninja_session']={'id':+new Date(),'tool':_0x2e2213},_0x382e94&&_0x2e2213&&!_0x3128e1&&console[_0x441344(0x111)](_0x441344(0x1d9)+(_0x2e2213[_0x441344(0x1e9)](0x0)[_0x441344(0x204)]()+_0x2e2213['substr'](0x1))+',',_0x441344(0x184),'see\\x20https://tinyurl.com/2vt8jxzw\\x20for\\x20more\\x20info.');}let _0x338f00=new x(_0x2c0f6c,_0x40579d,_0x4c204a,_0x46731b,_0x588074,_0x44549d);return _0x338f00[_0x441344(0x1c6)][_0x441344(0x128)](_0x338f00);}catch(_0x13925e){return console['warn'](_0x441344(0x1a3),_0x13925e&&_0x13925e[_0x441344(0x12a)]),()=>{};}});return _0x52d6fc=>_0x5a69fc[_0xcd0c31(0x12e)](_0x304222=>_0x304222(_0x52d6fc));}function ie(_0x248522,_0x22b47,_0x101d81,_0x49f9d5){var _0x2152db=_0x4626ea;_0x49f9d5&&_0x248522===_0x2152db(0x164)&&_0x101d81[_0x2152db(0x17e)]['reload']();}function b(_0x2f8a94){var _0x2c3352=_0x4626ea,_0x39dd14,_0x5cd825;let _0xff2cb3=function(_0x2707a5,_0x1f13e1){return _0x1f13e1-_0x2707a5;},_0x5714e8;if(_0x2f8a94['performance'])_0x5714e8=function(){var _0x6fee0f=_0x9226;return _0x2f8a94[_0x6fee0f(0x12c)]['now']();};else{if(_0x2f8a94[_0x2c3352(0x208)]&&_0x2f8a94['process'][_0x2c3352(0x187)]&&((_0x5cd825=(_0x39dd14=_0x2f8a94['process'])==null?void 0x0:_0x39dd14[_0x2c3352(0x151)])==null?void 0x0:_0x5cd825[_0x2c3352(0x18a)])!=='edge')_0x5714e8=function(){var _0x510a95=_0x2c3352;return _0x2f8a94['process'][_0x510a95(0x187)]();},_0xff2cb3=function(_0x1872da,_0xa7dc7d){return 0x3e8*(_0xa7dc7d[0x0]-_0x1872da[0x0])+(_0xa7dc7d[0x1]-_0x1872da[0x1])/0xf4240;};else try{let {performance:_0x46fdf0}=require('perf_hooks');_0x5714e8=function(){var _0x35a440=_0x2c3352;return _0x46fdf0[_0x35a440(0x166)]();};}catch{_0x5714e8=function(){return+new Date();};}}return{'elapsed':_0xff2cb3,'timeStamp':_0x5714e8,'now':()=>Date[_0x2c3352(0x166)]()};}function X(_0x11aedc,_0x1ca58d,_0x37c59b){var _0x2e4e28=_0x4626ea,_0x43e98c,_0x5da067,_0x37df21,_0x2f7d64,_0x541c33;if(_0x11aedc[_0x2e4e28(0x16a)]!==void 0x0)return _0x11aedc['_consoleNinjaAllowedToStart'];let _0x593073=((_0x5da067=(_0x43e98c=_0x11aedc[_0x2e4e28(0x208)])==null?void 0x0:_0x43e98c[_0x2e4e28(0x1b0)])==null?void 0x0:_0x5da067[_0x2e4e28(0x1ae)])||((_0x2f7d64=(_0x37df21=_0x11aedc[_0x2e4e28(0x208)])==null?void 0x0:_0x37df21[_0x2e4e28(0x151)])==null?void 0x0:_0x2f7d64[_0x2e4e28(0x18a)])===_0x2e4e28(0x127);return _0x593073&&_0x37c59b===_0x2e4e28(0x147)?_0x11aedc[_0x2e4e28(0x16a)]=!0x1:_0x11aedc['_consoleNinjaAllowedToStart']=_0x593073||!_0x1ca58d||((_0x541c33=_0x11aedc[_0x2e4e28(0x17e)])==null?void 0x0:_0x541c33[_0x2e4e28(0x193)])&&_0x1ca58d['includes'](_0x11aedc[_0x2e4e28(0x17e)]['hostname']),_0x11aedc[_0x2e4e28(0x16a)];}function H(_0x510064,_0x33a768,_0x5d605e,_0x2f5141){var _0x391961=_0x4626ea;_0x510064=_0x510064,_0x33a768=_0x33a768,_0x5d605e=_0x5d605e,_0x2f5141=_0x2f5141;let _0x187be4=b(_0x510064),_0x496f24=_0x187be4['elapsed'],_0x2c3d14=_0x187be4[_0x391961(0x168)];class _0x8237c4{constructor(){var _0xe23f1a=_0x391961;this[_0xe23f1a(0x19f)]=/^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)[_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*$/,this[_0xe23f1a(0x14e)]=/^(0|[1-9][0-9]*)$/,this['_quotedRegExp']=/'([^\\\\']|\\\\')*'/,this[_0xe23f1a(0x1ba)]=_0x510064['undefined'],this[_0xe23f1a(0x1a8)]=_0x510064[_0xe23f1a(0x19b)],this[_0xe23f1a(0x1aa)]=Object[_0xe23f1a(0x143)],this[_0xe23f1a(0x17f)]=Object[_0xe23f1a(0x122)],this[_0xe23f1a(0x203)]=_0x510064[_0xe23f1a(0x169)],this[_0xe23f1a(0x1f0)]=RegExp[_0xe23f1a(0x156)][_0xe23f1a(0x153)],this[_0xe23f1a(0x167)]=Date[_0xe23f1a(0x156)][_0xe23f1a(0x153)];}['serialize'](_0x5d18a7,_0x2b1f18,_0x4b5a75,_0xc8f070){var _0x216aa1=_0x391961,_0x767bb1=this,_0xd96c8a=_0x4b5a75['autoExpand'];function _0x1d3adf(_0x598192,_0x1e7c16,_0x5ddf6e){var _0x50bd5a=_0x9226;_0x1e7c16['type']=_0x50bd5a(0x1e8),_0x1e7c16[_0x50bd5a(0x1fe)]=_0x598192[_0x50bd5a(0x12a)],_0x2a9e62=_0x5ddf6e[_0x50bd5a(0x1ae)][_0x50bd5a(0x18f)],_0x5ddf6e[_0x50bd5a(0x1ae)]['current']=_0x1e7c16,_0x767bb1['_treeNodePropertiesBeforeFullValue'](_0x1e7c16,_0x5ddf6e);}try{_0x4b5a75[_0x216aa1(0x13f)]++,_0x4b5a75[_0x216aa1(0x125)]&&_0x4b5a75[_0x216aa1(0x131)][_0x216aa1(0x13c)](_0x2b1f18);var _0xba12f2,_0x3d2ea1,_0x210bc5,_0x5c98a5,_0x540845=[],_0x4bc002=[],_0x852303,_0x4888d1=this['_type'](_0x2b1f18),_0x33ea32=_0x4888d1===_0x216aa1(0x1fd),_0xccd31a=!0x1,_0x5258d3=_0x4888d1===_0x216aa1(0x174),_0x743b81=this[_0x216aa1(0x1f3)](_0x4888d1),_0xfe0678=this[_0x216aa1(0x1a1)](_0x4888d1),_0x22d6ec=_0x743b81||_0xfe0678,_0x4f2440={},_0x3f9058=0x0,_0x18fb20=!0x1,_0x2a9e62,_0x3d1f2d=/^(([1-9]{1}[0-9]*)|0)$/;if(_0x4b5a75['depth']){if(_0x33ea32){if(_0x3d2ea1=_0x2b1f18[_0x216aa1(0x1b6)],_0x3d2ea1>_0x4b5a75['elements']){for(_0x210bc5=0x0,_0x5c98a5=_0x4b5a75[_0x216aa1(0x1d3)],_0xba12f2=_0x210bc5;_0xba12f2<_0x5c98a5;_0xba12f2++)_0x4bc002['push'](_0x767bb1[_0x216aa1(0x1f4)](_0x540845,_0x2b1f18,_0x4888d1,_0xba12f2,_0x4b5a75));_0x5d18a7[_0x216aa1(0x1a4)]=!0x0;}else{for(_0x210bc5=0x0,_0x5c98a5=_0x3d2ea1,_0xba12f2=_0x210bc5;_0xba12f2<_0x5c98a5;_0xba12f2++)_0x4bc002[_0x216aa1(0x13c)](_0x767bb1[_0x216aa1(0x1f4)](_0x540845,_0x2b1f18,_0x4888d1,_0xba12f2,_0x4b5a75));}_0x4b5a75[_0x216aa1(0x181)]+=_0x4bc002[_0x216aa1(0x1b6)];}if(!(_0x4888d1===_0x216aa1(0x11f)||_0x4888d1===_0x216aa1(0x118))&&!_0x743b81&&_0x4888d1!==_0x216aa1(0x12f)&&_0x4888d1!==_0x216aa1(0x134)&&_0x4888d1!==_0x216aa1(0x207)){var _0x1971d5=_0xc8f070['props']||_0x4b5a75[_0x216aa1(0x14b)];if(this[_0x216aa1(0x1be)](_0x2b1f18)?(_0xba12f2=0x0,_0x2b1f18[_0x216aa1(0x12e)](function(_0x27ec34){var _0x1aafe2=_0x216aa1;if(_0x3f9058++,_0x4b5a75[_0x1aafe2(0x181)]++,_0x3f9058>_0x1971d5){_0x18fb20=!0x0;return;}if(!_0x4b5a75[_0x1aafe2(0x162)]&&_0x4b5a75['autoExpand']&&_0x4b5a75['autoExpandPropertyCount']>_0x4b5a75['autoExpandLimit']){_0x18fb20=!0x0;return;}_0x4bc002[_0x1aafe2(0x13c)](_0x767bb1[_0x1aafe2(0x1f4)](_0x540845,_0x2b1f18,_0x1aafe2(0x1cc),_0xba12f2++,_0x4b5a75,function(_0x729ea1){return function(){return _0x729ea1;};}(_0x27ec34)));})):this[_0x216aa1(0x1ec)](_0x2b1f18)&&_0x2b1f18[_0x216aa1(0x12e)](function(_0x518f09,_0x375058){var _0x57432c=_0x216aa1;if(_0x3f9058++,_0x4b5a75[_0x57432c(0x181)]++,_0x3f9058>_0x1971d5){_0x18fb20=!0x0;return;}if(!_0x4b5a75['isExpressionToEvaluate']&&_0x4b5a75[_0x57432c(0x125)]&&_0x4b5a75['autoExpandPropertyCount']>_0x4b5a75[_0x57432c(0x1e5)]){_0x18fb20=!0x0;return;}var _0x135ea4=_0x375058[_0x57432c(0x153)]();_0x135ea4[_0x57432c(0x1b6)]>0x64&&(_0x135ea4=_0x135ea4['slice'](0x0,0x64)+_0x57432c(0x1b7)),_0x4bc002[_0x57432c(0x13c)](_0x767bb1[_0x57432c(0x1f4)](_0x540845,_0x2b1f18,_0x57432c(0x1e1),_0x135ea4,_0x4b5a75,function(_0x1c99a5){return function(){return _0x1c99a5;};}(_0x518f09)));}),!_0xccd31a){try{for(_0x852303 in _0x2b1f18)if(!(_0x33ea32&&_0x3d1f2d['test'](_0x852303))&&!this[_0x216aa1(0x158)](_0x2b1f18,_0x852303,_0x4b5a75)){if(_0x3f9058++,_0x4b5a75['autoExpandPropertyCount']++,_0x3f9058>_0x1971d5){_0x18fb20=!0x0;break;}if(!_0x4b5a75[_0x216aa1(0x162)]&&_0x4b5a75[_0x216aa1(0x125)]&&_0x4b5a75[_0x216aa1(0x181)]>_0x4b5a75[_0x216aa1(0x1e5)]){_0x18fb20=!0x0;break;}_0x4bc002[_0x216aa1(0x13c)](_0x767bb1[_0x216aa1(0x11a)](_0x540845,_0x4f2440,_0x2b1f18,_0x4888d1,_0x852303,_0x4b5a75));}}catch{}if(_0x4f2440[_0x216aa1(0x11c)]=!0x0,_0x5258d3&&(_0x4f2440[_0x216aa1(0x117)]=!0x0),!_0x18fb20){var _0x484cfa=[][_0x216aa1(0x13b)](this['_getOwnPropertyNames'](_0x2b1f18))[_0x216aa1(0x13b)](this[_0x216aa1(0x175)](_0x2b1f18));for(_0xba12f2=0x0,_0x3d2ea1=_0x484cfa[_0x216aa1(0x1b6)];_0xba12f2<_0x3d2ea1;_0xba12f2++)if(_0x852303=_0x484cfa[_0xba12f2],!(_0x33ea32&&_0x3d1f2d[_0x216aa1(0x1bd)](_0x852303[_0x216aa1(0x153)]()))&&!this[_0x216aa1(0x158)](_0x2b1f18,_0x852303,_0x4b5a75)&&!_0x4f2440['_p_'+_0x852303[_0x216aa1(0x153)]()]){if(_0x3f9058++,_0x4b5a75[_0x216aa1(0x181)]++,_0x3f9058>_0x1971d5){_0x18fb20=!0x0;break;}if(!_0x4b5a75['isExpressionToEvaluate']&&_0x4b5a75[_0x216aa1(0x125)]&&_0x4b5a75['autoExpandPropertyCount']>_0x4b5a75[_0x216aa1(0x1e5)]){_0x18fb20=!0x0;break;}_0x4bc002[_0x216aa1(0x13c)](_0x767bb1[_0x216aa1(0x11a)](_0x540845,_0x4f2440,_0x2b1f18,_0x4888d1,_0x852303,_0x4b5a75));}}}}}if(_0x5d18a7['type']=_0x4888d1,_0x22d6ec?(_0x5d18a7[_0x216aa1(0x11b)]=_0x2b1f18[_0x216aa1(0x19d)](),this[_0x216aa1(0x13d)](_0x4888d1,_0x5d18a7,_0x4b5a75,_0xc8f070)):_0x4888d1==='date'?_0x5d18a7[_0x216aa1(0x11b)]=this[_0x216aa1(0x167)][_0x216aa1(0x196)](_0x2b1f18):_0x4888d1===_0x216aa1(0x207)?_0x5d18a7[_0x216aa1(0x11b)]=_0x2b1f18['toString']():_0x4888d1===_0x216aa1(0x1c0)?_0x5d18a7[_0x216aa1(0x11b)]=this['_regExpToString']['call'](_0x2b1f18):_0x4888d1==='symbol'&&this[_0x216aa1(0x203)]?_0x5d18a7[_0x216aa1(0x11b)]=this[_0x216aa1(0x203)][_0x216aa1(0x156)]['toString'][_0x216aa1(0x196)](_0x2b1f18):!_0x4b5a75[_0x216aa1(0x160)]&&!(_0x4888d1===_0x216aa1(0x11f)||_0x4888d1===_0x216aa1(0x118))&&(delete _0x5d18a7[_0x216aa1(0x11b)],_0x5d18a7[_0x216aa1(0x1ad)]=!0x0),_0x18fb20&&(_0x5d18a7[_0x216aa1(0x1b1)]=!0x0),_0x2a9e62=_0x4b5a75[_0x216aa1(0x1ae)][_0x216aa1(0x18f)],_0x4b5a75[_0x216aa1(0x1ae)][_0x216aa1(0x18f)]=_0x5d18a7,this['_treeNodePropertiesBeforeFullValue'](_0x5d18a7,_0x4b5a75),_0x4bc002[_0x216aa1(0x1b6)]){for(_0xba12f2=0x0,_0x3d2ea1=_0x4bc002[_0x216aa1(0x1b6)];_0xba12f2<_0x3d2ea1;_0xba12f2++)_0x4bc002[_0xba12f2](_0xba12f2);}_0x540845[_0x216aa1(0x1b6)]&&(_0x5d18a7['props']=_0x540845);}catch(_0xb4f8d5){_0x1d3adf(_0xb4f8d5,_0x5d18a7,_0x4b5a75);}return this[_0x216aa1(0x198)](_0x2b1f18,_0x5d18a7),this[_0x216aa1(0x1ac)](_0x5d18a7,_0x4b5a75),_0x4b5a75[_0x216aa1(0x1ae)][_0x216aa1(0x18f)]=_0x2a9e62,_0x4b5a75[_0x216aa1(0x13f)]--,_0x4b5a75[_0x216aa1(0x125)]=_0xd96c8a,_0x4b5a75[_0x216aa1(0x125)]&&_0x4b5a75[_0x216aa1(0x131)][_0x216aa1(0x16d)](),_0x5d18a7;}[_0x391961(0x175)](_0x1352b4){var _0x317466=_0x391961;return Object[_0x317466(0x149)]?Object[_0x317466(0x149)](_0x1352b4):[];}[_0x391961(0x1be)](_0x407d32){var _0x105bbc=_0x391961;return!!(_0x407d32&&_0x510064[_0x105bbc(0x1cc)]&&this[_0x105bbc(0x1b8)](_0x407d32)===_0x105bbc(0x1db)&&_0x407d32['forEach']);}[_0x391961(0x158)](_0x105e25,_0x40447,_0x1e1069){var _0x402cc6=_0x391961;return _0x1e1069[_0x402cc6(0x170)]?typeof _0x105e25[_0x40447]=='function':!0x1;}[_0x391961(0x19c)](_0x4e070f){var _0x38ce28=_0x391961,_0x13c348='';return _0x13c348=typeof _0x4e070f,_0x13c348===_0x38ce28(0x126)?this[_0x38ce28(0x1b8)](_0x4e070f)===_0x38ce28(0x132)?_0x13c348=_0x38ce28(0x1fd):this[_0x38ce28(0x1b8)](_0x4e070f)===_0x38ce28(0x17d)?_0x13c348=_0x38ce28(0x161):this[_0x38ce28(0x1b8)](_0x4e070f)===_0x38ce28(0x15c)?_0x13c348=_0x38ce28(0x207):_0x4e070f===null?_0x13c348='null':_0x4e070f[_0x38ce28(0x140)]&&(_0x13c348=_0x4e070f['constructor']['name']||_0x13c348):_0x13c348===_0x38ce28(0x118)&&this[_0x38ce28(0x1a8)]&&_0x4e070f instanceof this['_HTMLAllCollection']&&(_0x13c348='HTMLAllCollection'),_0x13c348;}['_objectToString'](_0xc97f51){var _0x176479=_0x391961;return Object[_0x176479(0x156)]['toString'][_0x176479(0x196)](_0xc97f51);}[_0x391961(0x1f3)](_0x53b2c5){var _0x17f551=_0x391961;return _0x53b2c5===_0x17f551(0x1ff)||_0x53b2c5==='string'||_0x53b2c5===_0x17f551(0x1e2);}[_0x391961(0x1a1)](_0x9f0f9a){var _0x457596=_0x391961;return _0x9f0f9a==='Boolean'||_0x9f0f9a===_0x457596(0x12f)||_0x9f0f9a===_0x457596(0x1fa);}[_0x391961(0x1f4)](_0x232d9b,_0xdc38d1,_0x58d536,_0x3ad627,_0x37f014,_0xb21601){var _0x2bffb6=this;return function(_0x2c7468){var _0x1a7116=_0x9226,_0x54a698=_0x37f014[_0x1a7116(0x1ae)][_0x1a7116(0x18f)],_0x465fad=_0x37f014[_0x1a7116(0x1ae)][_0x1a7116(0x1a7)],_0xafec7b=_0x37f014[_0x1a7116(0x1ae)][_0x1a7116(0x1d8)];_0x37f014['node'][_0x1a7116(0x1d8)]=_0x54a698,_0x37f014[_0x1a7116(0x1ae)]['index']=typeof _0x3ad627==_0x1a7116(0x1e2)?_0x3ad627:_0x2c7468,_0x232d9b[_0x1a7116(0x13c)](_0x2bffb6[_0x1a7116(0x1cf)](_0xdc38d1,_0x58d536,_0x3ad627,_0x37f014,_0xb21601)),_0x37f014['node'][_0x1a7116(0x1d8)]=_0xafec7b,_0x37f014[_0x1a7116(0x1ae)][_0x1a7116(0x1a7)]=_0x465fad;};}[_0x391961(0x11a)](_0x187213,_0x25b0f0,_0x319acf,_0xf744d3,_0x574c7e,_0x173630,_0x2ac11c){var _0x311022=_0x391961,_0x4bc3c4=this;return _0x25b0f0[_0x311022(0x182)+_0x574c7e[_0x311022(0x153)]()]=!0x0,function(_0x747146){var _0x5e5b41=_0x311022,_0x3b8e0b=_0x173630['node'][_0x5e5b41(0x18f)],_0x1554b2=_0x173630[_0x5e5b41(0x1ae)][_0x5e5b41(0x1a7)],_0x287b34=_0x173630[_0x5e5b41(0x1ae)]['parent'];_0x173630[_0x5e5b41(0x1ae)][_0x5e5b41(0x1d8)]=_0x3b8e0b,_0x173630['node']['index']=_0x747146,_0x187213['push'](_0x4bc3c4[_0x5e5b41(0x1cf)](_0x319acf,_0xf744d3,_0x574c7e,_0x173630,_0x2ac11c)),_0x173630[_0x5e5b41(0x1ae)][_0x5e5b41(0x1d8)]=_0x287b34,_0x173630['node'][_0x5e5b41(0x1a7)]=_0x1554b2;};}[_0x391961(0x1cf)](_0x3db910,_0x4e06af,_0x120900,_0xd2f9f5,_0x3e1e95){var _0x2d7627=_0x391961,_0x1c58f2=this;_0x3e1e95||(_0x3e1e95=function(_0x534986,_0x4def4b){return _0x534986[_0x4def4b];});var _0x28d910=_0x120900['toString'](),_0x3bdf47=_0xd2f9f5[_0x2d7627(0x18b)]||{},_0x41e3ce=_0xd2f9f5[_0x2d7627(0x160)],_0x64c7cf=_0xd2f9f5[_0x2d7627(0x162)];try{var _0x427ef3=this[_0x2d7627(0x1ec)](_0x3db910),_0x563948=_0x28d910;_0x427ef3&&_0x563948[0x0]==='\\x27'&&(_0x563948=_0x563948['substr'](0x1,_0x563948[_0x2d7627(0x1b6)]-0x2));var _0x2ce170=_0xd2f9f5[_0x2d7627(0x18b)]=_0x3bdf47[_0x2d7627(0x182)+_0x563948];_0x2ce170&&(_0xd2f9f5['depth']=_0xd2f9f5[_0x2d7627(0x160)]+0x1),_0xd2f9f5[_0x2d7627(0x162)]=!!_0x2ce170;var _0x3a303b=typeof _0x120900==_0x2d7627(0x1f9),_0x2b5081={'name':_0x3a303b||_0x427ef3?_0x28d910:this[_0x2d7627(0x144)](_0x28d910)};if(_0x3a303b&&(_0x2b5081[_0x2d7627(0x1f9)]=!0x0),!(_0x4e06af===_0x2d7627(0x1fd)||_0x4e06af===_0x2d7627(0x1a5))){var _0x30c000=this[_0x2d7627(0x1aa)](_0x3db910,_0x120900);if(_0x30c000&&(_0x30c000[_0x2d7627(0x178)]&&(_0x2b5081['setter']=!0x0),_0x30c000[_0x2d7627(0x119)]&&!_0x2ce170&&!_0xd2f9f5[_0x2d7627(0x1f8)]))return _0x2b5081[_0x2d7627(0x205)]=!0x0,this[_0x2d7627(0x171)](_0x2b5081,_0xd2f9f5),_0x2b5081;}var _0x31489d;try{_0x31489d=_0x3e1e95(_0x3db910,_0x120900);}catch(_0x110ec0){return _0x2b5081={'name':_0x28d910,'type':_0x2d7627(0x1e8),'error':_0x110ec0[_0x2d7627(0x12a)]},this['_processTreeNodeResult'](_0x2b5081,_0xd2f9f5),_0x2b5081;}var _0x41ba38=this['_type'](_0x31489d),_0x525062=this['_isPrimitiveType'](_0x41ba38);if(_0x2b5081['type']=_0x41ba38,_0x525062)this['_processTreeNodeResult'](_0x2b5081,_0xd2f9f5,_0x31489d,function(){var _0x2548fe=_0x2d7627;_0x2b5081['value']=_0x31489d['valueOf'](),!_0x2ce170&&_0x1c58f2[_0x2548fe(0x13d)](_0x41ba38,_0x2b5081,_0xd2f9f5,{});});else{var _0x307caf=_0xd2f9f5['autoExpand']&&_0xd2f9f5[_0x2d7627(0x13f)]<_0xd2f9f5['autoExpandMaxDepth']&&_0xd2f9f5['autoExpandPreviousObjects'][_0x2d7627(0x115)](_0x31489d)<0x0&&_0x41ba38!==_0x2d7627(0x174)&&_0xd2f9f5[_0x2d7627(0x181)]<_0xd2f9f5[_0x2d7627(0x1e5)];_0x307caf||_0xd2f9f5[_0x2d7627(0x13f)]<_0x41e3ce||_0x2ce170?(this[_0x2d7627(0x183)](_0x2b5081,_0x31489d,_0xd2f9f5,_0x2ce170||{}),this[_0x2d7627(0x198)](_0x31489d,_0x2b5081)):this[_0x2d7627(0x171)](_0x2b5081,_0xd2f9f5,_0x31489d,function(){var _0xfd0181=_0x2d7627;_0x41ba38==='null'||_0x41ba38==='undefined'||(delete _0x2b5081['value'],_0x2b5081[_0xfd0181(0x1ad)]=!0x0);});}return _0x2b5081;}finally{_0xd2f9f5[_0x2d7627(0x18b)]=_0x3bdf47,_0xd2f9f5[_0x2d7627(0x160)]=_0x41e3ce,_0xd2f9f5[_0x2d7627(0x162)]=_0x64c7cf;}}['_capIfString'](_0x541c7a,_0x18d80c,_0x1af37f,_0x3a12d7){var _0x3b4d9e=_0x391961,_0x1253d8=_0x3a12d7['strLength']||_0x1af37f['strLength'];if((_0x541c7a===_0x3b4d9e(0x1e6)||_0x541c7a===_0x3b4d9e(0x12f))&&_0x18d80c[_0x3b4d9e(0x11b)]){let _0x309e70=_0x18d80c[_0x3b4d9e(0x11b)][_0x3b4d9e(0x1b6)];_0x1af37f[_0x3b4d9e(0x1bc)]+=_0x309e70,_0x1af37f[_0x3b4d9e(0x1bc)]>_0x1af37f['totalStrLength']?(_0x18d80c[_0x3b4d9e(0x1ad)]='',delete _0x18d80c[_0x3b4d9e(0x11b)]):_0x309e70>_0x1253d8&&(_0x18d80c[_0x3b4d9e(0x1ad)]=_0x18d80c[_0x3b4d9e(0x11b)][_0x3b4d9e(0x1ee)](0x0,_0x1253d8),delete _0x18d80c[_0x3b4d9e(0x11b)]);}}[_0x391961(0x1ec)](_0x51064f){var _0x4e5228=_0x391961;return!!(_0x51064f&&_0x510064[_0x4e5228(0x1e1)]&&this[_0x4e5228(0x1b8)](_0x51064f)==='[object\\x20Map]'&&_0x51064f['forEach']);}[_0x391961(0x144)](_0x42b554){var _0x41991c=_0x391961;if(_0x42b554[_0x41991c(0x1ea)](/^\\d+$/))return _0x42b554;var _0xaeb2a7;try{_0xaeb2a7=JSON[_0x41991c(0x186)](''+_0x42b554);}catch{_0xaeb2a7='\\x22'+this[_0x41991c(0x1b8)](_0x42b554)+'\\x22';}return _0xaeb2a7[_0x41991c(0x1ea)](/^\"([a-zA-Z_][a-zA-Z_0-9]*)\"$/)?_0xaeb2a7=_0xaeb2a7['substr'](0x1,_0xaeb2a7[_0x41991c(0x1b6)]-0x2):_0xaeb2a7=_0xaeb2a7[_0x41991c(0x1da)](/'/g,'\\x5c\\x27')['replace'](/\\\\\"/g,'\\x22')[_0x41991c(0x1da)](/(^\"|\"$)/g,'\\x27'),_0xaeb2a7;}['_processTreeNodeResult'](_0x32aee4,_0x590d37,_0x35d66e,_0x5ac389){var _0x35578e=_0x391961;this[_0x35578e(0x129)](_0x32aee4,_0x590d37),_0x5ac389&&_0x5ac389(),this[_0x35578e(0x198)](_0x35d66e,_0x32aee4),this['_treeNodePropertiesAfterFullValue'](_0x32aee4,_0x590d37);}[_0x391961(0x129)](_0x4edcdf,_0x3e4b5a){var _0x420f=_0x391961;this['_setNodeId'](_0x4edcdf,_0x3e4b5a),this[_0x420f(0x185)](_0x4edcdf,_0x3e4b5a),this[_0x420f(0x1eb)](_0x4edcdf,_0x3e4b5a),this[_0x420f(0x16c)](_0x4edcdf,_0x3e4b5a);}[_0x391961(0x1c4)](_0x2f8c11,_0x58fabb){}['_setNodeQueryPath'](_0x1ae224,_0x1e829c){}[_0x391961(0x1fc)](_0x4a1918,_0x20d9c2){}[_0x391961(0x189)](_0x31bed1){return _0x31bed1===this['_undefined'];}[_0x391961(0x1ac)](_0x4ecc24,_0x763395){var _0x51f0f0=_0x391961;this['_setNodeLabel'](_0x4ecc24,_0x763395),this[_0x51f0f0(0x142)](_0x4ecc24),_0x763395[_0x51f0f0(0x14a)]&&this[_0x51f0f0(0x15a)](_0x4ecc24),this['_addFunctionsNode'](_0x4ecc24,_0x763395),this['_addLoadNode'](_0x4ecc24,_0x763395),this[_0x51f0f0(0x172)](_0x4ecc24);}[_0x391961(0x198)](_0x3779ec,_0x43d9da){var _0x92d911=_0x391961;let _0x3351b5;try{_0x510064['console']&&(_0x3351b5=_0x510064[_0x92d911(0x1c5)][_0x92d911(0x1fe)],_0x510064['console'][_0x92d911(0x1fe)]=function(){}),_0x3779ec&&typeof _0x3779ec[_0x92d911(0x1b6)]==_0x92d911(0x1e2)&&(_0x43d9da[_0x92d911(0x1b6)]=_0x3779ec[_0x92d911(0x1b6)]);}catch{}finally{_0x3351b5&&(_0x510064[_0x92d911(0x1c5)]['error']=_0x3351b5);}if(_0x43d9da[_0x92d911(0x1b4)]===_0x92d911(0x1e2)||_0x43d9da[_0x92d911(0x1b4)]===_0x92d911(0x1fa)){if(isNaN(_0x43d9da[_0x92d911(0x11b)]))_0x43d9da[_0x92d911(0x136)]=!0x0,delete _0x43d9da[_0x92d911(0x11b)];else switch(_0x43d9da['value']){case Number['POSITIVE_INFINITY']:_0x43d9da[_0x92d911(0x145)]=!0x0,delete _0x43d9da[_0x92d911(0x11b)];break;case Number[_0x92d911(0x1a6)]:_0x43d9da[_0x92d911(0x197)]=!0x0,delete _0x43d9da[_0x92d911(0x11b)];break;case 0x0:this['_isNegativeZero'](_0x43d9da[_0x92d911(0x11b)])&&(_0x43d9da[_0x92d911(0x173)]=!0x0);break;}}else _0x43d9da['type']===_0x92d911(0x174)&&typeof _0x3779ec['name']==_0x92d911(0x1e6)&&_0x3779ec[_0x92d911(0x1c8)]&&_0x43d9da[_0x92d911(0x1c8)]&&_0x3779ec[_0x92d911(0x1c8)]!==_0x43d9da[_0x92d911(0x1c8)]&&(_0x43d9da[_0x92d911(0x1f7)]=_0x3779ec['name']);}[_0x391961(0x116)](_0x4db2dd){var _0xf7b3e2=_0x391961;return 0x1/_0x4db2dd===Number[_0xf7b3e2(0x1a6)];}['_sortProps'](_0xa48a70){var _0x5dce5f=_0x391961;!_0xa48a70[_0x5dce5f(0x14b)]||!_0xa48a70[_0x5dce5f(0x14b)][_0x5dce5f(0x1b6)]||_0xa48a70[_0x5dce5f(0x1b4)]===_0x5dce5f(0x1fd)||_0xa48a70[_0x5dce5f(0x1b4)]===_0x5dce5f(0x1e1)||_0xa48a70[_0x5dce5f(0x1b4)]===_0x5dce5f(0x1cc)||_0xa48a70[_0x5dce5f(0x14b)][_0x5dce5f(0x14d)](function(_0xd2b2c3,_0x29d5cd){var _0x92d94b=_0x5dce5f,_0x538cfa=_0xd2b2c3['name'][_0x92d94b(0x148)](),_0x6b9285=_0x29d5cd[_0x92d94b(0x1c8)][_0x92d94b(0x148)]();return _0x538cfa<_0x6b9285?-0x1:_0x538cfa>_0x6b9285?0x1:0x0;});}[_0x391961(0x11d)](_0x583623,_0x46e3e8){var _0x3ce46e=_0x391961;if(!(_0x46e3e8['noFunctions']||!_0x583623[_0x3ce46e(0x14b)]||!_0x583623['props'][_0x3ce46e(0x1b6)])){for(var _0x42d08a=[],_0x52dbe5=[],_0x4b8335=0x0,_0x450baf=_0x583623['props'][_0x3ce46e(0x1b6)];_0x4b8335<_0x450baf;_0x4b8335++){var _0x398f8c=_0x583623['props'][_0x4b8335];_0x398f8c[_0x3ce46e(0x1b4)]==='function'?_0x42d08a[_0x3ce46e(0x13c)](_0x398f8c):_0x52dbe5[_0x3ce46e(0x13c)](_0x398f8c);}if(!(!_0x52dbe5[_0x3ce46e(0x1b6)]||_0x42d08a[_0x3ce46e(0x1b6)]<=0x1)){_0x583623[_0x3ce46e(0x14b)]=_0x52dbe5;var _0x56bfbd={'functionsNode':!0x0,'props':_0x42d08a};this[_0x3ce46e(0x1c4)](_0x56bfbd,_0x46e3e8),this[_0x3ce46e(0x1fc)](_0x56bfbd,_0x46e3e8),this[_0x3ce46e(0x142)](_0x56bfbd),this[_0x3ce46e(0x16c)](_0x56bfbd,_0x46e3e8),_0x56bfbd['id']+='\\x20f',_0x583623[_0x3ce46e(0x14b)][_0x3ce46e(0x1c9)](_0x56bfbd);}}}[_0x391961(0x1a9)](_0x430e38,_0x1fa560){}['_setNodeExpandableState'](_0x2fd910){}[_0x391961(0x1ab)](_0x54e605){var _0x19641c=_0x391961;return Array[_0x19641c(0x192)](_0x54e605)||typeof _0x54e605==_0x19641c(0x126)&&this[_0x19641c(0x1b8)](_0x54e605)==='[object\\x20Array]';}[_0x391961(0x16c)](_0x54cd11,_0x472425){}['_cleanNode'](_0x98c2ca){var _0x428c79=_0x391961;delete _0x98c2ca[_0x428c79(0x1dd)],delete _0x98c2ca[_0x428c79(0x179)],delete _0x98c2ca[_0x428c79(0x114)];}['_setNodeExpressionPath'](_0x372a40,_0x5986da){}}let _0x30d0d1=new _0x8237c4(),_0x1e7343={'props':0x64,'elements':0x64,'strLength':0x400*0x32,'totalStrLength':0x400*0x32,'autoExpandLimit':0x1388,'autoExpandMaxDepth':0xa},_0x21fae0={'props':0x5,'elements':0x5,'strLength':0x100,'totalStrLength':0x100*0x3,'autoExpandLimit':0x1e,'autoExpandMaxDepth':0x2};function _0x4bb77d(_0x284bbe,_0x113d13,_0x283f4b,_0x2585f4,_0xb7dbca,_0x4426e0){var _0x45c2c2=_0x391961;let _0x185714,_0x54555f;try{_0x54555f=_0x2c3d14(),_0x185714=_0x5d605e[_0x113d13],!_0x185714||_0x54555f-_0x185714['ts']>0x1f4&&_0x185714[_0x45c2c2(0x1d6)]&&_0x185714[_0x45c2c2(0x190)]/_0x185714['count']<0x64?(_0x5d605e[_0x113d13]=_0x185714={'count':0x0,'time':0x0,'ts':_0x54555f},_0x5d605e[_0x45c2c2(0x152)]={}):_0x54555f-_0x5d605e[_0x45c2c2(0x152)]['ts']>0x32&&_0x5d605e['hits'][_0x45c2c2(0x1d6)]&&_0x5d605e['hits'][_0x45c2c2(0x190)]/_0x5d605e[_0x45c2c2(0x152)][_0x45c2c2(0x1d6)]<0x64&&(_0x5d605e[_0x45c2c2(0x152)]={});let _0x297324=[],_0x3b32c9=_0x185714['reduceLimits']||_0x5d605e[_0x45c2c2(0x152)][_0x45c2c2(0x1cd)]?_0x21fae0:_0x1e7343,_0x292fa3=_0x4e1175=>{var _0x4c44eb=_0x45c2c2;let _0x2c5531={};return _0x2c5531[_0x4c44eb(0x14b)]=_0x4e1175[_0x4c44eb(0x14b)],_0x2c5531['elements']=_0x4e1175['elements'],_0x2c5531['strLength']=_0x4e1175[_0x4c44eb(0x17c)],_0x2c5531[_0x4c44eb(0x137)]=_0x4e1175[_0x4c44eb(0x137)],_0x2c5531['autoExpandLimit']=_0x4e1175[_0x4c44eb(0x1e5)],_0x2c5531[_0x4c44eb(0x123)]=_0x4e1175[_0x4c44eb(0x123)],_0x2c5531['sortProps']=!0x1,_0x2c5531[_0x4c44eb(0x170)]=!_0x33a768,_0x2c5531[_0x4c44eb(0x160)]=0x1,_0x2c5531[_0x4c44eb(0x13f)]=0x0,_0x2c5531['expId']=_0x4c44eb(0x1d2),_0x2c5531[_0x4c44eb(0x1bb)]=_0x4c44eb(0x1f2),_0x2c5531[_0x4c44eb(0x125)]=!0x0,_0x2c5531['autoExpandPreviousObjects']=[],_0x2c5531[_0x4c44eb(0x181)]=0x0,_0x2c5531[_0x4c44eb(0x1f8)]=!0x0,_0x2c5531[_0x4c44eb(0x1bc)]=0x0,_0x2c5531['node']={'current':void 0x0,'parent':void 0x0,'index':0x0},_0x2c5531;};for(var _0x271d05=0x0;_0x271d05<_0xb7dbca[_0x45c2c2(0x1b6)];_0x271d05++)_0x297324[_0x45c2c2(0x13c)](_0x30d0d1['serialize']({'timeNode':_0x284bbe===_0x45c2c2(0x190)||void 0x0},_0xb7dbca[_0x271d05],_0x292fa3(_0x3b32c9),{}));if(_0x284bbe==='trace'){let _0x2542c2=Error[_0x45c2c2(0x195)];try{Error[_0x45c2c2(0x195)]=0x1/0x0,_0x297324[_0x45c2c2(0x13c)](_0x30d0d1[_0x45c2c2(0x183)]({'stackNode':!0x0},new Error()[_0x45c2c2(0x13a)],_0x292fa3(_0x3b32c9),{'strLength':0x1/0x0}));}finally{Error[_0x45c2c2(0x195)]=_0x2542c2;}}return{'method':'log','version':_0x2f5141,'args':[{'ts':_0x283f4b,'session':_0x2585f4,'args':_0x297324,'id':_0x113d13,'context':_0x4426e0}]};}catch(_0x1e8623){return{'method':_0x45c2c2(0x111),'version':_0x2f5141,'args':[{'ts':_0x283f4b,'session':_0x2585f4,'args':[{'type':_0x45c2c2(0x1e8),'error':_0x1e8623&&_0x1e8623[_0x45c2c2(0x12a)]}],'id':_0x113d13,'context':_0x4426e0}]};}finally{try{if(_0x185714&&_0x54555f){let _0x42b843=_0x2c3d14();_0x185714['count']++,_0x185714['time']+=_0x496f24(_0x54555f,_0x42b843),_0x185714['ts']=_0x42b843,_0x5d605e[_0x45c2c2(0x152)][_0x45c2c2(0x1d6)]++,_0x5d605e[_0x45c2c2(0x152)][_0x45c2c2(0x190)]+=_0x496f24(_0x54555f,_0x42b843),_0x5d605e[_0x45c2c2(0x152)]['ts']=_0x42b843,(_0x185714[_0x45c2c2(0x1d6)]>0x32||_0x185714[_0x45c2c2(0x190)]>0x64)&&(_0x185714[_0x45c2c2(0x1cd)]=!0x0),(_0x5d605e[_0x45c2c2(0x152)][_0x45c2c2(0x1d6)]>0x3e8||_0x5d605e[_0x45c2c2(0x152)][_0x45c2c2(0x190)]>0x12c)&&(_0x5d605e[_0x45c2c2(0x152)]['reduceLimits']=!0x0);}}catch{}}}return _0x4bb77d;}((_0x166570,_0x13c185,_0x50ce36,_0x4c8613,_0xc64222,_0x1e8974,_0x228dba,_0x28bbaf,_0x328698,_0x40a2ab,_0x310429)=>{var _0x1f92ab=_0x4626ea;if(_0x166570[_0x1f92ab(0x154)])return _0x166570['_console_ninja'];if(!X(_0x166570,_0x28bbaf,_0xc64222))return _0x166570['_console_ninja']={'consoleLog':()=>{},'consoleTrace':()=>{},'consoleTime':()=>{},'consoleTimeEnd':()=>{},'autoLog':()=>{},'autoLogMany':()=>{},'autoTraceMany':()=>{},'coverage':()=>{},'autoTrace':()=>{},'autoTime':()=>{},'autoTimeEnd':()=>{}},_0x166570['_console_ninja'];let _0xdd87f3=b(_0x166570),_0x344045=_0xdd87f3[_0x1f92ab(0x1c7)],_0x23ac14=_0xdd87f3[_0x1f92ab(0x168)],_0x291990=_0xdd87f3[_0x1f92ab(0x166)],_0x4e4fa2={'hits':{},'ts':{}},_0x12dc4d=H(_0x166570,_0x328698,_0x4e4fa2,_0x1e8974),_0x31f310=_0xe6f4e=>{_0x4e4fa2['ts'][_0xe6f4e]=_0x23ac14();},_0x3614dd=(_0x9be044,_0x560c04)=>{var _0x323d35=_0x1f92ab;let _0x31fd9e=_0x4e4fa2['ts'][_0x560c04];if(delete _0x4e4fa2['ts'][_0x560c04],_0x31fd9e){let _0x1938e0=_0x344045(_0x31fd9e,_0x23ac14());_0x346395(_0x12dc4d(_0x323d35(0x190),_0x9be044,_0x291990(),_0x3a4102,[_0x1938e0],_0x560c04));}},_0x11e437=_0x545750=>{var _0xe1bfdf=_0x1f92ab,_0x5a609d;return _0xc64222===_0xe1bfdf(0x1b5)&&_0x166570[_0xe1bfdf(0x19e)]&&((_0x5a609d=_0x545750==null?void 0x0:_0x545750[_0xe1bfdf(0x15b)])==null?void 0x0:_0x5a609d[_0xe1bfdf(0x1b6)])&&(_0x545750[_0xe1bfdf(0x15b)][0x0][_0xe1bfdf(0x19e)]=_0x166570['origin']),_0x545750;};_0x166570['_console_ninja']={'consoleLog':(_0x21df8e,_0x23ebc6)=>{var _0x40def2=_0x1f92ab;_0x166570[_0x40def2(0x1c5)]['log'][_0x40def2(0x1c8)]!=='disabledLog'&&_0x346395(_0x12dc4d(_0x40def2(0x111),_0x21df8e,_0x291990(),_0x3a4102,_0x23ebc6));},'consoleTrace':(_0x398c73,_0x32b4fb)=>{var _0x1e52d8=_0x1f92ab;_0x166570['console'][_0x1e52d8(0x111)][_0x1e52d8(0x1c8)]!==_0x1e52d8(0x155)&&_0x346395(_0x11e437(_0x12dc4d(_0x1e52d8(0x135),_0x398c73,_0x291990(),_0x3a4102,_0x32b4fb)));},'consoleTime':_0x41442a=>{_0x31f310(_0x41442a);},'consoleTimeEnd':(_0x240419,_0x127bab)=>{_0x3614dd(_0x127bab,_0x240419);},'autoLog':(_0x5ea011,_0x4126e2)=>{var _0x30986a=_0x1f92ab;_0x346395(_0x12dc4d(_0x30986a(0x111),_0x4126e2,_0x291990(),_0x3a4102,[_0x5ea011]));},'autoLogMany':(_0x7e654b,_0x2fade8)=>{var _0x2b6440=_0x1f92ab;_0x346395(_0x12dc4d(_0x2b6440(0x111),_0x7e654b,_0x291990(),_0x3a4102,_0x2fade8));},'autoTrace':(_0x8aa6ee,_0x494eb2)=>{_0x346395(_0x11e437(_0x12dc4d('trace',_0x494eb2,_0x291990(),_0x3a4102,[_0x8aa6ee])));},'autoTraceMany':(_0x4b5ec1,_0x2bbad4)=>{var _0x545cf6=_0x1f92ab;_0x346395(_0x11e437(_0x12dc4d(_0x545cf6(0x135),_0x4b5ec1,_0x291990(),_0x3a4102,_0x2bbad4)));},'autoTime':(_0x50122b,_0x1430e7,_0x35bbd8)=>{_0x31f310(_0x35bbd8);},'autoTimeEnd':(_0xc3c915,_0x1478ec,_0x2151f3)=>{_0x3614dd(_0x1478ec,_0x2151f3);},'coverage':_0x4bb18d=>{var _0x1d9120=_0x1f92ab;_0x346395({'method':_0x1d9120(0x150),'version':_0x1e8974,'args':[{'id':_0x4bb18d}]});}};let _0x346395=q(_0x166570,_0x13c185,_0x50ce36,_0x4c8613,_0xc64222,_0x40a2ab,_0x310429),_0x3a4102=_0x166570[_0x1f92ab(0x1e4)];return _0x166570[_0x1f92ab(0x154)];})(globalThis,_0x4626ea(0x1af),_0x4626ea(0x1e3),\"/Users/kavin/.vscode/extensions/wallabyjs.console-ninja-1.0.322/node_modules\",'webpack','1.0.0',_0x4626ea(0x146),[\"localhost\",\"127.0.0.1\",\"example.cypress.io\",\"MacBook-Pro-5.local\",\"192.168.1.35\"],_0x4626ea(0x1d1),_0x4626ea(0x120),_0x4626ea(0x1c1));" - ) - ); - } catch (e) {} -} -/* istanbul ignore next */ function oo_oo(i, ...v) { - try { - oo_cm().consoleLog(i, v); - } catch (e) {} - return v; -} -/* istanbul ignore next */ function oo_tr(i, ...v) { - try { - oo_cm().consoleTrace(i, v); - } catch (e) {} - return v; -} -/* istanbul ignore next */ function oo_ts(v) { - try { - oo_cm().consoleTime(v); - } catch (e) {} - return v; -} -/* istanbul ignore next */ function oo_te(v, i) { - try { - oo_cm().consoleTimeEnd(v, i); - } catch (e) {} - return v; -} /*eslint unicorn/no-abusive-eslint-disable:,eslint-comments/disable-enable-pair:,eslint-comments/no-unlimited-disable:,eslint-comments/no-aggregating-enable:,eslint-comments/no-duplicate-disable:,eslint-comments/no-unused-disable:,eslint-comments/no-unused-enable:,*/ diff --git a/frontend/src/Editor/Components/Table/Text.jsx b/frontend/src/Editor/Components/Table/Text.jsx index ca4525012f..7697918fe7 100644 --- a/frontend/src/Editor/Components/Table/Text.jsx +++ b/frontend/src/Editor/Components/Table/Text.jsx @@ -109,12 +109,12 @@ const Text = ({ diff --git a/frontend/src/Editor/Components/Table/columns/index.jsx b/frontend/src/Editor/Components/Table/columns/index.jsx index 2c2495d348..2473d74674 100644 --- a/frontend/src/Editor/Components/Table/columns/index.jsx +++ b/frontend/src/Editor/Components/Table/columns/index.jsx @@ -35,6 +35,12 @@ export default function generateColumnsData({ cellSize, maxRowHeightValue, }) { + const isValueRowDependent = (value) => { + return ( + (typeof value === 'string' || Array.isArray(value)) && (value.includes('cellValue') || value.includes('rowData')) + ); + }; + return columnProperties.map((column) => { if (!column) return; const columnSize = columnSizes[column?.id] || columnSizes[column?.name] || column.columnSize; @@ -51,8 +57,8 @@ export default function generateColumnsData({ columnType === 'image' ) { columnOptions.selectOptions = []; - const values = resolveReferences(column.values, []); - const labels = resolveReferences(column.labels, []); + const values = resolveReferences(column.values); + const labels = resolveReferences(column.labels); if (Array.isArray(labels) && Array.isArray(values)) { columnOptions.selectOptions = labels.map((label, index) => { @@ -60,13 +66,34 @@ export default function generateColumnsData({ }); } } + + // This is done to ensure that if row dependent properties are not used, the default value is used and performance is not affected + const rowDependentMap = { + cellBackgroundColor: isValueRowDependent(column.cellBackgroundColor), + textColor: isValueRowDependent(column.textColor), + minValue: isValueRowDependent(column.minValue), + maxValue: isValueRowDependent(column.maxValue), + minLength: isValueRowDependent(column.minLength), + maxLength: isValueRowDependent(column.maxLength), + regex: isValueRowDependent(column.regex), + customRule: isValueRowDependent(column.customRule), + isEditable: isValueRowDependent(column.isEditable), + minDate: isValueRowDependent(column.minDate), + maxDate: isValueRowDependent(column.maxDate), + borderRadius: isValueRowDependent(column.borderRadius), + multiselect: false, + }; + + let useDynamicOptions = false; if (columnType === 'select' || columnType === 'newMultiSelect') { columnOptions.selectOptions = []; - const useDynamicOptions = resolveReferences(column?.useDynamicOptions); + useDynamicOptions = resolveReferences(column?.useDynamicOptions, currentState); if (useDynamicOptions) { - const dynamicOptions = resolveReferences(column?.dynamicOptions || []); + rowDependentMap['multiselect'] = isValueRowDependent(column?.dynamicOptions); + const dynamicOptions = resolveReferences(column?.dynamicOptions || [], currentState); columnOptions.selectOptions = Array.isArray(dynamicOptions) ? dynamicOptions : []; } else { + rowDependentMap['multiselect'] = isValueRowDependent(JSON.stringify(column?.options)); const options = column?.options ?? []; columnOptions.selectOptions = options?.map((option) => ({ @@ -101,6 +128,7 @@ export default function generateColumnsData({ }; } const width = columnSize || defaultColumn.width; + return { id: column.id, Header: resolveReferences(column.name) ?? '', @@ -146,6 +174,14 @@ export default function generateColumnsData({ exposeToCodeHinter((prevState) => ({ ...prevState, ...customResolvables })); } cellValue = cellValue === undefined || cellValue === null ? '' : cellValue; + column = { ...column }; + + Object.keys(rowDependentMap).map((key) => { + column[key] = rowDependentMap[key] + ? resolveReferences(cell.column?.[key], currentState, '', { cellValue, rowData }) + : column[key]; + }); + switch (columnType) { case 'string': case undefined: @@ -260,9 +296,8 @@ export default function generateColumnsData({ }; const allowedDecimalPlaces = column?.decimalPlaces ?? null; - const removingExcessDecimalPlaces = (cellValue, allowedDecimalPlaces) => { - allowedDecimalPlaces = resolveReferences(allowedDecimalPlaces); + allowedDecimalPlaces = resolveReferences(allowedDecimalPlaces, currentState, 2, { cellValue, rowData }); if (cellValue?.toString()?.includes('.')) { const splittedCellValue = cellValue?.toString()?.split('.'); const decimalPlacesUnderLimit = splittedCellValue[1].split('').splice(0, allowedDecimalPlaces).join(''); @@ -270,7 +305,6 @@ export default function generateColumnsData({ } return cellValue; }; - cellValue = allowedDecimalPlaces ? removingExcessDecimalPlaces(cellValue, allowedDecimalPlaces) : cellValue; if (isEditable) { @@ -445,8 +479,26 @@ export default function generateColumnsData({ currentState, customResolveObjects: { cellValue }, }); - const { isValid, validationError } = validationData; + let rowOptions = []; + if ((columnType === 'select' || columnType === 'newMultiSelect') && rowDependentMap['multiselect']) { + if (useDynamicOptions) { + const dynamicOptions = resolveReferences(column?.dynamicOptions || [], currentState, [], { + rowData, + cellValue, + }); + rowOptions = Array.isArray(dynamicOptions) ? dynamicOptions : []; + } else { + const options = column?.options ?? []; + + rowOptions = + options?.map((option) => ({ + label: resolveReferences(option.label, currentState, [], { rowData, cellValue }), + value: resolveReferences(option.value, currentState, [], { rowData, cellValue }), + })) ?? []; + } + } + return (
0 ? rowOptions : columnOptions.selectOptions} value={cellValue} search={true} onChange={(value) => { @@ -614,12 +666,23 @@ export default function generateColumnsData({ ); } case 'datepicker': { - const textColor = resolveReferences(column.textColor, '', { cellValue, rowData }); - const isTimeChecked = resolveReferences(column?.isTimeChecked); - const isTwentyFourHrFormatEnabled = resolveReferences(column?.isTwentyFourHrFormatEnabled); - const disabledDates = resolveReferences(column?.disabledDates); - const parseInUnixTimestamp = resolveReferences(column?.parseInUnixTimestamp); - const isDateSelectionEnabled = resolveReferences(column?.isDateSelectionEnabled); + const textColor = resolveReferences(column.textColor, currentState, '', { cellValue, rowData }); + const isTimeChecked = resolveReferences(column?.isTimeChecked, currentState, false, { cellValue, rowData }); + const isTwentyFourHrFormatEnabled = resolveReferences( + column?.isTwentyFourHrFormatEnabled, + currentState, + false, + { cellValue, rowData } + ); + const disabledDates = resolveReferences(column?.disabledDates, currentState, [], { cellValue, rowData }); + const parseInUnixTimestamp = resolveReferences(column?.parseInUnixTimestamp, currentState, '', { + cellValue, + rowData, + }); + const isDateSelectionEnabled = resolveReferences(column?.isDateSelectionEnabled, currentState, true, { + cellValue, + rowData, + }); const cellStyles = { color: textColor ?? '', }; @@ -688,20 +751,30 @@ export default function generateColumnsData({ ); } case 'link': { - const linkTarget = resolveReferences(column?.linkTarget ?? '{{true}}'); - const displayText = column?.displayText ? resolveReferences(column.displayText) : ''; - column = { - ...column, - linkColor: column?.linkColor ?? '#1B1F24', - underlineColor: column?.underlineColor ?? '#4368E3', - }; + const linkTarget = resolveReferences(column?.linkTarget ?? '{{true}}', currentState, '', { + cellValue, + rowData, + }); + const displayText = resolveReferences(column?.displayText ?? '{{}}', currentState, '', { + cellValue, + rowData, + }); + const linkColor = resolveReferences(column?.linkColor ?? '#1B1F24', currentState, '', { + cellValue, + rowData, + }); + const underlineColor = resolveReferences(column.underlineColor ?? '', currentState, '', { + cellValue, + rowData, + }); + return (
computeText()); const [visibility, setVisibility] = useState(properties.visibility); const [isLoading, setLoading] = useState(loadingState); const [isDisabled, setIsDisabled] = useState(disabledState); const color = ['#000', '#000000'].includes(textColor) ? (darkMode ? '#fff' : '#000') : textColor; - + count = count + 1; useEffect(() => { if (visibility !== properties.visibility) setVisibility(properties.visibility); if (isLoading !== loadingState) setLoading(loadingState); @@ -48,49 +60,63 @@ export const Text = function Text({ height, properties, fireEvent, styles, darkM }, [properties.visibility, loadingState, disabledState]); useEffect(() => { + if (isInitialRender.current) return; const text = computeText(); setText(text); setExposedVariable('text', text); + }, [properties.text]); - setExposedVariable('setText', async function (text) { - setText(text); - setExposedVariable('text', text); - }); - setExposedVariable('clear', async function (text) { - setText(''); - setExposedVariable('text', ''); - }); + useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isVisible', properties.visibility); + }, [properties.visibility]); + + useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isLoading', loadingState); + }, [loadingState]); + + useEffect(() => { + if (isInitialRender.current) return; setExposedVariable('isDisabled', disabledState); + }, [disabledState]); - setExposedVariable('visibility', async function (value) { - setVisibility(value); - }); - - setExposedVariable('setVisibility', async function (value) { - setVisibility(value); - }); - - setExposedVariable('setLoading', async function (value) { - setLoading(value); - }); - - setExposedVariable('setDisable', async function (value) { - setIsDisabled(value); - }); - + useEffect(() => { + const exposedVariables = { + text: computeText(), + setText: async function (text) { + setText(text); + setExposedVariable('text', text); + }, + clear: async function () { + setText(''); + setExposedVariable('text', ''); + }, + isVisible: properties.visibility, + isLoading: loadingState, + isDisabled: disabledState, + visibility: async function (value) { + setExposedVariable('isVisible', value); + setVisibility(value); + }, + setVisibility: async function (value) { + setExposedVariable('isVisible', value); + setVisibility(value); + }, + setLoading: async function (value) { + setExposedVariable('isLoading', value); + setLoading(value); + }, + setDisable: async function (value) { + setExposedVariable('isDisabled', value); + setIsDisabled(value); + }, + }; + setExposedVariables(exposedVariables); + setText(text); + isInitialRender.current = false; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - properties.text, - setText, - setVisibility, - properties.visibility, - loadingState, - disabledState, - setIsDisabled, - setLoading, - ]); + }, []); function computeText() { return properties.text === 0 || properties.text === false ? properties.text?.toString() : properties.text; diff --git a/frontend/src/Editor/Components/TextArea.jsx b/frontend/src/Editor/Components/TextArea.jsx index 40914f6e0a..cd7afc7e0d 100644 --- a/frontend/src/Editor/Components/TextArea.jsx +++ b/frontend/src/Editor/Components/TextArea.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; export const TextArea = function TextArea({ height, @@ -8,9 +8,11 @@ export const TextArea = function TextArea({ setExposedVariables, dataCy, }) { + const isInitialRender = useRef(true); const [value, setValue] = useState(properties.value); useEffect(() => { + if (isInitialRender.current) return; setValue(properties.value); setExposedVariable('value', properties.value); @@ -27,11 +29,14 @@ export const TextArea = function TextArea({ setValue(''); setExposedVariable('value', ''); }, + value: properties.value, }; setExposedVariables(exposedVariables); + setValue(properties.value); + isInitialRender.current = false; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setValue]); + }, []); return (