diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/ArrayNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/ArrayNode.jsx new file mode 100644 index 0000000000..f9a0acf5ef --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/ArrayNode.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const ArrayNode = ({ value }) => { + return ( +
+ {`[${value.length}]`} +
+ ); +}; + +export default ArrayNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/BooleanNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/BooleanNode.jsx new file mode 100644 index 0000000000..d692a9c10c --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/BooleanNode.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import OverflowTooltip from '@/_components/OverflowTooltip'; + +const BooleanNode = ({ value }) => { + return ( +
+ {value.toString()} +
+ ); +}; + +export default BooleanNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/FunctionNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/FunctionNode.jsx new file mode 100644 index 0000000000..456b8238c4 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/FunctionNode.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import OverflowTooltip from '@/_components/OverflowTooltip'; + +const FunctionNode = () => { + return ( +
+ function +
+ ); +}; + +export default FunctionNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/NullNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/NullNode.jsx new file mode 100644 index 0000000000..40ff32737a --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/NullNode.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import OverflowTooltip from '@/_components/OverflowTooltip'; + +const NullNode = ({ value }) => { + return ( +
+ {value === null ? 'null' : 'undefined'} +
+ ); +}; + +export default NullNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/NumberNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/NumberNode.jsx new file mode 100644 index 0000000000..8eb6e981b9 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/NumberNode.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import OverflowTooltip from '@/_components/OverflowTooltip'; + +const NumberNode = ({ value }) => { + return ( +
+ {value} +
+ ); +}; + +export default NumberNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/ObjectNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/ObjectNode.jsx new file mode 100644 index 0000000000..e89082cbd1 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/ObjectNode.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +const ObjectNode = ({ value }) => { + return ( +
+ {`{${Object.keys(value).length}}`} +
+ ); +}; + +export default ObjectNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx new file mode 100644 index 0000000000..ed5ef5a10c --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx @@ -0,0 +1,123 @@ +import React, { useState } from 'react'; +import StringNode from './StringNode'; +import FunctionNode from './FunctionNode'; +import NumberNode from './NumberNode'; +import BooleanNode from './BooleanNode'; +import NullNode from './NullNode'; +import ArrayNode from './ArrayNode'; +import ObjectNode from './ObjectNode'; +import SolidIcon from '@/_ui/Icon/SolidIcons'; +import { ToolTip } from '@/_components/ToolTip'; +import { DefaultCopyIcon } from '../../DefaultCopyIcon'; +import { copyToClipboard } from '../../utils'; + +const Row = ({ label, value, level = 1, absolutePath }) => { + const [isExpanded, setIsExpanded] = useState(false); + const Node = () => { + if (typeof value === 'string') { + return ; + } else if (typeof value === 'undefined' || value === null) { + return ; + } else if (typeof value === 'number') { + return ; + } else if (typeof value === 'boolean') { + return ; + } else if (Array.isArray(value)) { + return ; + } else if (typeof value === 'object') { + return ; + } else if (typeof value === 'function') { + return ; + } + }; + + const isObject = typeof value === 'object' && !Array.isArray(value) && value !== null; + const isArray = Array.isArray(value); + + return ( +
+
1 ? '8px' : '0px' }}> +
setIsExpanded((prev) => !prev)}> +
+ {(isArray || isObject) && + (isExpanded ? ( + + ) : ( + + ))} +
+
+ {label} +
+
+ +
+
+ + { + copyToClipboard(absolutePath); + }} + className="copy-to-clipboard json-viewer-action-icon" + > + + + + + { + copyToClipboard(value); + }} + className="json-viewer-action-icon" + > + + + +
+
+
+ {isExpanded && isObject && ( +
+ {Object.entries(value).map(([key, val]) => ( + + ))} +
+ )} + {isExpanded && isArray && ( +
+ {value.map((item, index) => { + return ( + + ); + })} +
+ )} +
+ ); +}; + +export default Row; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/StringNode.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/StringNode.jsx new file mode 100644 index 0000000000..3c4bcc3644 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/StringNode.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import OverflowTooltip from '@/_components/OverflowTooltip'; + +const StringNode = ({ value }) => { + return ( +
+ {`"${value}"`} +
+ ); +}; + +export default StringNode; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/CustomJSONViewer.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/CustomJSONViewer.jsx new file mode 100644 index 0000000000..b5e392ee66 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/CustomJSONViewer.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import Row from './Components/Row'; +import './styles.scss'; + +const CustomJSONViewer = ({ data, absolutePath }) => { + let modifiedData = data; + if (typeof data !== 'object') modifiedData = { '': data }; + return ( +
+ {Object.entries(modifiedData).map(([key, value], index) => { + return ; + })} +
+ ); +}; + +export default CustomJSONViewer; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/styles.scss b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/styles.scss new file mode 100644 index 0000000000..62a2adb902 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/styles.scss @@ -0,0 +1,74 @@ +.custom-json-viewer { + margin-left: 16px; + margin-right: 16px; + font-family: "IBM Plex Sans"; + font-size: 12px; + color: var(--text-default, #1B1F24); + + + .json-viewer-row-container { + &:hover { + background-color: var(--interactive-overlays-fill-hover); + + .json-viewer-actions-container { + display: flex; + } + } + } + + .json-viewer-row { + width: 100%; + display: flex; + height: 20px; + align-items: center; + overflow: hidden; + + .json-viewer-expand-icon { + width: 12px; + height: 12px; + display: flex; + align-items: center; + justify-content: center; + } + + .json-viewer-label-container { + margin-left: 8px; + margin-right: 4px; + flex-shrink: 0; /* don’t shrink */ + white-space: nowrap; + } + + .json-viewer-value-container { + flex: 1; /* take available space */ + min-width: 0; /* allow shrinkage */ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .json-viewer-actions-container { + display:none; + margin-left: auto; + margin-right: 4px; + height: 12px; + width:40px; + align-items: center; + flex-shrink: 0; + + .json-viewer-action-icon { + display: flex; + align-items: center; + justify-content: center; + height: 20px; + width: 20px; + + &:hover { + cursor: pointer; + background-color: var(--button-outline-hover); + border-radius: 4px; + } + } + } + + } +} \ No newline at end of file diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/DefaultCopyIcon.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/DefaultCopyIcon.jsx index fa46cf9694..6d26f2676b 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/DefaultCopyIcon.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/DefaultCopyIcon.jsx @@ -1,9 +1,9 @@ import React from 'react'; -export const DefaultCopyIcon = () => ( +export const DefaultCopyIcon = ({ height = 12, width = 12 }) => ( { const searchValue = useStore((state) => state.inspectorSearchValue, shallow); - // const getSelectedNodes = useStore((state) => state.getSelectedNodes, shallow); const getResolvedValue = useStore((state) => state.getResolvedValue, shallow); const setSearchValue = useStore((state) => state.setInspectorSearchValue, shallow); const [selectedNodePath, setSelectedNodePath] = React.useState(null); @@ -38,6 +37,7 @@ const JSONTreeViewerV2 = ({ data = {}, iconsList = [], darkMode, searchablePaths return [new Set(result), expandedIdSet]; }, [searchValue, JSON.stringify(searchablePaths)]); + // Do not remove this code, once we have the data in the correct format, we can use this function to filter the data // const recursiveFn = (obj) => { // if (!obj || typeof obj !== 'object') return []; // let isCompletelyExposed = false; @@ -89,10 +89,9 @@ const JSONTreeViewerV2 = ({ data = {}, iconsList = [], darkMode, searchablePaths return filtered.map((item) => item.id); }, [flattendedData, expandedIds]); - console.log('selectedData', selectedData); return ( <> - {!selectedNodePath || isEmpty(selectedData) ? ( + {!selectedNodePath || (typeof selectedData == 'object' && isEmpty(selectedData)) ? (
{ const { data, path, darkMode, backFn } = props; - const [theme, setTheme] = useState(() => getTheme(darkMode)); + const callbackActions = useCallbackActions() || []; const type = path.startsWith('components') ? 'components' : path.startsWith('queries') ? 'queries' : 'actions'; const nodeSpecificActions = callbackActions.filter((action) => [type].includes(action.for))?.[0]?.actions; @@ -23,10 +16,6 @@ export const JSONViewer = (props) => { const generalActions = callbackActions.filter((action) => action.for === 'all')?.[0]?.actions || []; - useEffect(() => { - setTheme(() => getTheme(darkMode)); - }, [darkMode]); - return (
{ data={optionsData} type={type} /> - - { - const key = keyPath[0]; - if (!key && key != 0) return ''; - return key; - }} - valueRenderer={(raw, value) => { - if (typeof value === 'function') { - return ( - - function - - ); - } - - return {raw}; - }} - /> +
); }; diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js index 6651e5813f..3b256afc2a 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/useCallbackActions.js @@ -1,7 +1,7 @@ import { toast } from 'react-hot-toast'; import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; -// import { runQuery } from '@/AppBuilder/_utils/queryPanel'; +import { copyToClipboard } from './utils'; const useCallbackActions = () => { const deleteComponents = useStore((state) => state.deleteComponents, shallow); @@ -40,12 +40,6 @@ const useCallbackActions = () => { setSelectedQuery(id); }; - const copyToClipboard = (data) => { - const stringified = JSON.stringify(data, null, 2).replace(/\\/g, ''); - navigator.clipboard.writeText(stringified); - return toast.success('Copied to the clipboard', { position: 'top-center' }); - }; - const handleAutoScrollToComponent = (data) => { const componentId = getComponentIdFromName(data.nodeName); const { isAccessible, computedComponentId, isOnCanvas } = getComponentIdToAutoScroll(componentId); diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js index 13ad6cc3f4..36a2afb8f5 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js @@ -1,3 +1,5 @@ +import { toast } from 'react-hot-toast'; + export const formatInspectorDataMisc = (obj, type, searchablePaths = new Set()) => { if (typeof obj !== 'object' || obj === null) return []; const data = Object.entries(obj).sort((a, b) => a[0].localeCompare(b[0], undefined, { sensitivity: 'base' })); @@ -125,25 +127,8 @@ export const extractComponentName = (path) => { } }; -export const getTheme = (darkMode) => { - return { - scheme: 'custom', - author: 'chris kempson (http://chriskempson.com)', - base00: 'transparent', - base01: '#303030', - base02: '#505050', - base03: '#b0b0b0', - base04: '#d0d0d0', - base05: '#1B1F24', - base06: '#f5f5f5', - base07: '#ffffff', - base08: '#fb0120', - base09: '#9467BD', - base0A: '#fda331', - base0B: '#2CA02C', - base0C: '#76c7b7', - base0D: '#e4e0db', - base0E: '#d381c3', - base0F: '#be643c', - }; +export const copyToClipboard = (data) => { + const stringified = JSON.stringify(data, null, 2).replace(/\\/g, ''); + navigator.clipboard.writeText(stringified); + return toast.success('Copied to the clipboard', { position: 'top-center' }); };