From f1820e96206ddf5ee993915a1df619effec3afbe Mon Sep 17 00:00:00 2001 From: Shaurya Sharma Date: Tue, 6 May 2025 03:35:22 +0530 Subject: [PATCH] Inspector revamp fixes --- .../CustomJSONViewer/Components/Row.jsx | 2 +- .../CustomJSONViewer/styles.scss | 1 + .../LeftSidebarInspector/JSONTreeViewerV2.jsx | 96 ++++++++++++------ .../LeftSidebarInspector.jsx | 23 ++--- .../LeftSidebar/LeftSidebarInspector/Node.jsx | 45 ++++++--- .../LeftSidebarInspector/TreeViewHeader.jsx | 2 + .../LeftSidebar/LeftSidebarInspector/utils.js | 45 +-------- .../_stores/slices/inspectorSlice.js | 99 +++++++++++++++++++ frontend/src/_styles/theme.scss | 40 ++++++-- 9 files changed, 241 insertions(+), 112 deletions(-) diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx index ed5ef5a10c..ca8396875b 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/CustomJSONViewer/Components/Row.jsx @@ -43,7 +43,7 @@ const Row = ({ label, value, level = 1, absolutePath }) => { (isExpanded ? ( { const searchValue = useStore((state) => state.inspectorSearchValue, shallow); + const getComponentIdFromName = useStore((state) => state.getComponentIdFromName, shallow); + const getComponentDefinition = useStore((state) => state.getComponentDefinition, shallow); const getResolvedValue = useStore((state) => state.getResolvedValue, shallow); const setSearchValue = useStore((state) => state.setInspectorSearchValue, shallow); const [selectedNodePath, setSelectedNodePath] = React.useState(null); @@ -76,7 +78,27 @@ const JSONTreeViewerV2 = ({ data = {}, iconsList = [], darkMode, searchablePaths setSelectedNodePath(null); }; - const selectedData = selectedNodePath ? getResolvedValue(`{{${selectedNodePath}}}`) : {}; + const selectedData = (() => { + if (selectedNodePath?.startsWith('components.')) { + // Split the selectedNode path using . and grab the second element if it exists + const pathArray = selectedNodePath.split('.'); + const componentName = pathArray?.[1]; + const componentId = getComponentIdFromName(componentName); + const component = getComponentDefinition(componentId); + const parent = component?.component?.parent; + if (parent) { + const parentComponent = getComponentDefinition(parent); + const parentType = parentComponent?.component?.component; + if (parentType === 'Form') { + return { + id: componentId, + }; + } + } + } + return selectedNodePath ? getResolvedValue(`{{${selectedNodePath}}}`) : {}; + })(); + const expandedIds = [...Array.from(pathSet), ...selectedNodes]; const filteredIds = useMemo(() => { @@ -86,7 +108,20 @@ const JSONTreeViewerV2 = ({ data = {}, iconsList = [], darkMode, searchablePaths const { path } = metadata || {}; return expandedIdsSet.has(path); }); - return filtered.map((item) => item.id); + + return filtered + .map((item) => item.id) + .filter((path) => { + const pathArray = path.split('.'); + // One by one combine and check if the path is in expandedIds or not + for (let i = pathArray.length - 1; i > 0; i--) { + const parentPath = pathArray.slice(0, i).join('.'); + if (!expandedIdsSet.has(parentPath)) { + return false; + } + } + return true; + }); }, [flattendedData, expandedIds]); return ( @@ -105,35 +140,36 @@ const JSONTreeViewerV2 = ({ data = {}, iconsList = [], darkMode, searchablePaths width={300} /> +
+ { + const { element } = props; + const { metadata } = element || {}; + const { path } = metadata || {}; + const data = { + nodeName: element.name, + selectedNodePath: path, + }; - { - const { element } = props; - const { metadata } = element || {}; - const { path } = metadata || {}; - const data = { - nodeName: element.name, - selectedNodePath: path, - }; - - return ( - - ); - }} - /> + return ( + + ); + }} + /> +
) : ( diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx index 4e3dbf9c73..734f863080 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/LeftSidebarInspector.jsx @@ -7,8 +7,8 @@ import JSONTreeViewerV2 from './JSONTreeViewerV2'; import _ from 'lodash'; import { ButtonSolid } from '@/_ui/AppButton/AppButton'; import useIconList from './useIconList'; - -import { formatInspectorComponentData, formatInspectorDataMisc, formatInspectorQueryData } from './utils'; +import { Button as ButtonComponent } from '@/components/ui/Button/Button'; +import { formatInspectorDataMisc, formatInspectorQueryData } from './utils'; const LeftSidebarInspector = ({ darkMode, pinned, setPinned }) => { const exposedComponentsVariables = useStore((state) => state.getAllExposedValues().components, shallow); @@ -18,6 +18,7 @@ const LeftSidebarInspector = ({ darkMode, pinned, setPinned }) => { const exposedPageVariables = useStore((state) => state.getAllExposedValues().page || {}, shallow); const exposedGlobalVariables = useStore((state) => state.getAllExposedValues().globals || {}, shallow); const componentIdNameMapping = useStore((state) => state.getComponentIdNameMapping(), shallow); + const formatInspectorComponentData = useStore((state) => state.formatInspectorComponentData, shallow); const queryNameIdMapping = useStore((state) => state.getQueryNameIdMapping(), shallow); const searchablePaths = useRef(new Set(['queries', 'components', 'globals', 'variables', 'page', 'constants'])); @@ -109,18 +110,14 @@ const LeftSidebarInspector = ({ darkMode, pinned, setPinned }) => {
- setPinned(!pinned)} - darkMode={darkMode} - styles={{ width: '28px', padding: 0 }} - data-cy={`left-sidebar-inspector`} - variant="ghostBlack" - className="left-sidebar-header-btn" - leftIcon={pinned ? 'unpin' : 'pin'} - iconWidth="14" - fill={`var(--slate12)`} - > + variant="ghost" + fill="var(--icon-strong,#6A727C)" + size="medium" + />
diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/Node.jsx b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/Node.jsx index 27fafb22ae..1b01f48daf 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/Node.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/Node.jsx @@ -9,6 +9,7 @@ import OverflowTooltip from '@/_components/OverflowTooltip'; import { HiddenOptions } from './HiddenOptions'; import useCallbackActions from './useCallbackActions'; import useStore from '@/AppBuilder/_stores/store'; +import { Button as ButtonComponent } from '@/components/ui/Button/Button'; import { shallow } from 'zustand/shallow'; const renderNodeIcons = (node, iconsList, darkMode) => { @@ -56,21 +57,26 @@ export const Node = (props) => { const setSelectedNodes = useStore((state) => state.setSelectedNodes, shallow); const callbackActions = useCallbackActions() || []; const nodeIcon = renderNodeIcons(element.name, iconsList, darkMode); + const getResolvedValue = useStore((state) => state.getResolvedValue, shallow); const metadata = element.metadata || {}; - const { type } = metadata; + const { type, path } = metadata; const nodeSpecificActions = callbackActions.filter((action) => [type].includes(action.for)); + const onExpand = (node) => { + const { element } = node || {}; + const { metadata } = element || {}; + const { path } = metadata || {}; + setSelectedNodes(path); + }; + const onSelect = (node) => { const { isBranch, element } = node || {}; const { metadata } = element || {}; - const { path } = metadata || {}; - if (!isBranch) { + const { path, type } = metadata || {}; + if (type) { setSelectedNodePath(path); - } else { - setSelectedNodePath(null); } - - setSelectedNodes(path); }; + const nodeSpecificFilteredActions = nodeSpecificActions?.[0]?.actions?.filter((action) => { return action.enableInspectorTreeView; @@ -84,27 +90,34 @@ export const Node = (props) => { return ( //
onSelect(props)} style={{ - marginLeft: 22 * (level - 1), + marginLeft: level > 1 ? 12 : 0, + // paddingLeft: '16px', opacity: isDisabled ? 0.5 : 1, height: level === 1 ? '28px' : '32px', display: 'flex', alignItems: 'center', color: level === 1 ? 'var(--text-placeholder, #6A727C)' : 'var(--text-default, #1B1F24)', - cursor: isBranch || level === 1 ? 'pointer' : 'default', + // borderLeft: level > 1 ? '1px solid var(--slate6, #D7DBDF)' : 'none', }} > - {(isBranch || level === 1) && ( + {!['queries', 'globals', 'variables'].includes(type) && (
- {isExpanded ? ( - - ) : ( - + {(isBranch || level === 1 || path === 'page.variables') && ( + onExpand(props)} + variant="ghost" + fill="var(--icon-default,#ACB2B9)" + size="small" + /> )}
)} +
onSelect(props)} className={cx('node-content', { 'node-content-hoverable': level !== 1, 'node-content-active': actionClicked, @@ -112,7 +125,7 @@ export const Node = (props) => { > {nodeIcon &&
{nodeIcon}
}
- + { className="copy-menu-options-icon json-viewer-options-btn" style={{ outline: 'none', + border: 'none', + boxShadow: 'none', }} > diff --git a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js index 36a2afb8f5..203b5ea298 100644 --- a/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js +++ b/frontend/src/AppBuilder/LeftSidebar/LeftSidebarInspector/utils.js @@ -19,7 +19,7 @@ export const formatInspectorDataMisc = (obj, type, searchablePaths = new Set()) name, children: reduceData(value, currentPath, level + 1), metadata: { - type: 'misc', + type: type, path: currentPath, ...((path === 'page.variables' ? level === 2 : level === 1) && { data: typeof value === 'object' ? JSON.stringify(value) : value, @@ -33,49 +33,6 @@ export const formatInspectorDataMisc = (obj, type, searchablePaths = new Set()) return reduceData(data, type); }; -export const formatInspectorComponentData = ( - componentIdNameMapping, - exposedComponentsVariables, - searchablePaths = new Set() -) => { - const data = Object.entries(componentIdNameMapping) - .map(([key, name]) => ({ - key, - name: name || key, - value: exposedComponentsVariables[key] ?? { id: key }, - })) - .sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })); - - const reduceData = (obj, path = 'components', level = 1) => { - let data = obj; - if (!obj || typeof obj !== 'object' || level > 1) return []; - else if (!Array.isArray(obj)) { - data = Object.entries(obj); - } - return data - .filter((item) => item.name) - .reduce((acc, { key, name, value }) => { - const currentPath = path + `.${name}`; - searchablePaths.add(currentPath); - return [ - ...acc, - { - id: currentPath, - name, - children: reduceData(value, currentPath, level + 1), - metadata: { - type: 'components', - path: currentPath, - ...(level === 1 && { data: typeof value === 'object' ? JSON.stringify(value) : value }), - }, - }, - ]; - }, []); - }; - - return reduceData(data); -}; - export const formatInspectorQueryData = (queryNameIdMapping, exposedQueries, searchablePaths = new Set()) => { const reverseMapping = Object.fromEntries(Object.entries(queryNameIdMapping).map(([name, id]) => [id, name])); const _sortedQueries = Object.entries(exposedQueries) diff --git a/frontend/src/AppBuilder/_stores/slices/inspectorSlice.js b/frontend/src/AppBuilder/_stores/slices/inspectorSlice.js index b0b8a95e84..521fff3b1b 100644 --- a/frontend/src/AppBuilder/_stores/slices/inspectorSlice.js +++ b/frontend/src/AppBuilder/_stores/slices/inspectorSlice.js @@ -31,4 +31,103 @@ export const createInspectorSlice = (set, get) => ({ setInspectorSearchResults: (results) => { set({ inspectorSearchResults: results }); }, + getAllComponentChildrenById: (id) => { + const { getComponentDefinition, getResolvedComponent } = get(); + const component = getComponentDefinition(id); + const componentType = component?.component?.component; + switch (componentType) { + case 'Container': + case 'Form': + case 'ModalV2': + return [ + ...get().getContainerChildrenMapping(id), + ...get().getContainerChildrenMapping(`${id}-header`), + ...get().getContainerChildrenMapping(`${id}-footer`), + ]; + case 'Tabs': { + const tabs = getResolvedComponent(id)?.properties?.tabs; + const children = Array.isArray(tabs) ? tabs : []; + const res = children + ?.map((tab) => { + const tabId = `${id}-${tab.id}`; + return get().getContainerChildrenMapping(tabId); + }) + .reduce((acc, curr) => { + return [...acc, ...curr]; + }, []); + return res; + } + default: + return get().getContainerChildrenMapping(id); + } + }, + + formatInspectorComponentData: ( + componentIdNameMapping, + exposedComponentsVariables, + searchablePaths = new Set(), + moduleId = 'canvas' + ) => { + const { getComponentDefinition, getAllComponentChildrenById } = get(); + const data = Object.entries(componentIdNameMapping) + .filter(([key]) => { + const component = getComponentDefinition(key, moduleId); + return !component?.component?.parent; + }) + .map(([key, name]) => { + const component = getComponentDefinition(key, moduleId); + let parentComponentType = null; + if (component?.component?.parent) { + const parentComponent = getComponentDefinition(component.component.parent, moduleId); + parentComponentType = parentComponent?.component?.component; + } + return { + key, + name: name || key, + parentType: parentComponentType, + }; + }) + .sort((a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })); + + const reduceData = (obj, path = 'components', level = 1) => { + let data = obj; + if (!obj || typeof obj !== 'object') return []; + + return data + .filter((item) => item.name) + .reduce((acc, { key, name, parentType }) => { + const currentPath = `components.${name}`; + searchablePaths.add(currentPath); + const children = getAllComponentChildrenById(key).map((childKey) => { + const childComponent = getComponentDefinition(childKey); + let parentComponentType = null; + if (childComponent?.component?.parent) { + const parentComponent = getComponentDefinition(childComponent.component.parent); + parentComponentType = parentComponent?.component?.component; + } + return { + key: childKey, + name: childComponent?.component?.name, + parentType: parentComponentType, + }; + }); + + return [ + ...acc, + { + id: currentPath, + name, + children: reduceData(children, currentPath, level + 1), + metadata: { + type: 'components', + path: currentPath, + parentType: parentType, + }, + }, + ]; + }, []); + }; + + return reduceData(data); + }, }); diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index 69bf8326e3..993dd6bdf3 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -18877,12 +18877,24 @@ section.ai-message-prompt-input-wrapper { } } +.json-tree-view { + ul { + margin-left:16px !important; + border-left: 1px solid var(--slate6, #D7DBDF); + } + + ul[role="tree"] { + border-left: none !important; + } +} .basic.tree { list-style: none; margin: 0; - padding: 20px; + padding: 0px; + padding-right:20px; padding-top: 0px; + } .basic .tree-node, @@ -18919,6 +18931,7 @@ section.ai-message-prompt-input-wrapper { display: flex; align-items: center; justify-content: center; + flex-shrink: 0; } .node-icon { @@ -18932,6 +18945,8 @@ section.ai-message-prompt-input-wrapper { display: flex; align-items: center; font-size:12px; + flex:1; + min-width: 0px; } @@ -19016,16 +19031,24 @@ section.ai-message-prompt-input-wrapper { } +// .json-viewer-options-btn { +// display: flex; +// align-items: center; +// margin-left: auto; + +// &:hover { +// cursor: pointer; +// background-color:var(--interactive-overlays-fill-hover); +// border-radius: 4px; +// } +// } + .json-viewer-options-btn { - display: flex; - align-items: center; margin-left: auto; - &:hover { - cursor: pointer; - background-color:var(--interactive-overlays-fill-hover); - border-radius: 4px; - } + + align-items: center; + } @@ -19052,6 +19075,7 @@ section.ai-message-prompt-input-wrapper { width: 20px; border-radius: 4px; margin-right:4px; + flex-shrink: 0; &:hover { background-color: var(--button-outline-hover);