From f493b5fd8552b65f720fc33f592e6d4fcbdc8290 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 15:00:15 -0300 Subject: [PATCH 1/8] feat: make panels resizable --- packages/@dcl/inspector/package-lock.json | 32 +++++--- packages/@dcl/inspector/package.json | 5 +- .../@dcl/inspector/src/components/App/App.css | 15 +++- .../@dcl/inspector/src/components/App/App.tsx | 69 +++++++++++------ .../src/components/Assets/Assets.css | 7 +- .../src/components/Assets/Assets.tsx | 21 +++--- .../@dcl/inspector/src/components/Box/Box.css | 5 ++ .../src/components/Container/Container.css | 8 +- .../EntityInspector/EntityInspector.css | 6 ++ .../EntityInspector/EntityInspector.tsx | 1 + .../src/components/Hierarchy/Hierarchy.css | 4 + .../src/components/Hierarchy/Hierarchy.tsx | 6 +- .../src/components/Resizable/Resizable.css | 30 -------- .../src/components/Resizable/Resizable.tsx | 74 ------------------- .../src/components/Resizable/index.ts | 2 - .../src/components/Resizable/types.ts | 34 --------- .../@dcl/inspector/src/hooks/useWindowSize.ts | 33 +++++++++ 17 files changed, 154 insertions(+), 198 deletions(-) create mode 100644 packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.css create mode 100644 packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.css delete mode 100644 packages/@dcl/inspector/src/components/Resizable/Resizable.css delete mode 100644 packages/@dcl/inspector/src/components/Resizable/Resizable.tsx delete mode 100644 packages/@dcl/inspector/src/components/Resizable/index.ts delete mode 100644 packages/@dcl/inspector/src/components/Resizable/types.ts create mode 100644 packages/@dcl/inspector/src/hooks/useWindowSize.ts diff --git a/packages/@dcl/inspector/package-lock.json b/packages/@dcl/inspector/package-lock.json index e45fbd01b..4b58ea45f 100644 --- a/packages/@dcl/inspector/package-lock.json +++ b/packages/@dcl/inspector/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "@dcl/inspector", "version": "0.1.0", + "dependencies": { + "react-resizable-panels": "^0.0.48" + }, "devDependencies": { "@babylonjs/core": "^5.48.0", "@babylonjs/gui": "^5.48.0", @@ -2712,8 +2715,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/jsdom": { "version": "20.0.3", @@ -2807,7 +2809,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -3153,7 +3154,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -3217,7 +3217,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -3287,6 +3286,15 @@ "react-dom": "^16.8.0 || ^17 || ^18" } }, + "node_modules/react-resizable-panels": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.48.tgz", + "integrity": "sha512-fJa3itmJ3HLLmVG7y8tka80wFW63N6ai76q7MGwU8nSXeA0qkX36vnmPyXm34lvtsGjn1Cgi5IPhPQnf42SVpA==", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-resize-detector": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.0.4.tgz", @@ -3512,7 +3520,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -6010,8 +6017,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "jsdom": { "version": "20.0.3", @@ -6091,7 +6097,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -6368,7 +6373,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, "requires": { "loose-envify": "^1.1.0" } @@ -6408,7 +6412,6 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -6461,6 +6464,12 @@ "warning": "^4.0.2" } }, + "react-resizable-panels": { + "version": "0.0.48", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.48.tgz", + "integrity": "sha512-fJa3itmJ3HLLmVG7y8tka80wFW63N6ai76q7MGwU8nSXeA0qkX36vnmPyXm34lvtsGjn1Cgi5IPhPQnf42SVpA==", + "requires": {} + }, "react-resize-detector": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.0.4.tgz", @@ -6637,7 +6646,6 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, "requires": { "loose-envify": "^1.1.0" } diff --git a/packages/@dcl/inspector/package.json b/packages/@dcl/inspector/package.json index 42aa1b7a6..aaeefbbf7 100644 --- a/packages/@dcl/inspector/package.json +++ b/packages/@dcl/inspector/package.json @@ -48,5 +48,8 @@ "start": "node ./build.js --watch" }, "types": "dist/tooling-entrypoint.d.ts", - "typings": "dist/tooling-entrypoint.d.ts" + "typings": "dist/tooling-entrypoint.d.ts", + "dependencies": { + "react-resizable-panels": "^0.0.48" + } } diff --git a/packages/@dcl/inspector/src/components/App/App.css b/packages/@dcl/inspector/src/components/App/App.css index 01b3f95d7..6f423a0f4 100644 --- a/packages/@dcl/inspector/src/components/App/App.css +++ b/packages/@dcl/inspector/src/components/App/App.css @@ -34,7 +34,7 @@ code { display: flex; flex-direction: column; width: 100%; - height: 100vh; + height: 100%; background: var(--main-bg-color); } @@ -54,3 +54,16 @@ code { top: -30px; position: absolute !important; } + +.App { + width: 100%; + height: 100%; +} + +.App .horizontal-handle { + width: 4px; +} + +.App .vertical-handle { + height: 4px; +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/App/App.tsx b/packages/@dcl/inspector/src/components/App/App.tsx index 3a8eed2de..bf197e127 100644 --- a/packages/@dcl/inspector/src/components/App/App.tsx +++ b/packages/@dcl/inspector/src/components/App/App.tsx @@ -1,37 +1,62 @@ -import React from 'react' +import React, { useRef } from 'react' import { EntityInspector } from '../EntityInspector' import { Hierarchy } from '../Hierarchy' import { Renderer } from '../Renderer' import { Box } from '../Box' import { Toolbar } from '../Toolbar' +import { PanelGroup, Panel, PanelResizeHandle, ImperativePanelHandle } from 'react-resizable-panels' import './App.css' -import { Resizable } from '../Resizable' import Assets from '../Assets' +import { useSelectedEntity } from '../../hooks/sdk/useSelectedEntity' +import { useWindowSize } from '../../hooks/useWindowSize' const App = () => { + const selectedEntity = useSelectedEntity() + + const { height } = useWindowSize() + + // Footer's height is 36 pixels, so we need to calculate the percentage of the screen that it takes to pass as the minSize prop for the Panel + const footerMin = (50 / height!) * 100 + + console.log(footerMin) + return ( - - -
- - - - -
-
-
- - - - - -
-
+
+ + + + + + + + + + + + + + + + + {selectedEntity !== null && ( + + + + + + )} + + + + + + + + + +
) } diff --git a/packages/@dcl/inspector/src/components/Assets/Assets.css b/packages/@dcl/inspector/src/components/Assets/Assets.css index 3e64efdea..9e74def7b 100644 --- a/packages/@dcl/inspector/src/components/Assets/Assets.css +++ b/packages/@dcl/inspector/src/components/Assets/Assets.css @@ -2,6 +2,9 @@ display: flex; flex-direction: column; flex: none; + width: 100%; + height: 100%; + overflow: hidden; } .Assets .Assets-buttons span { @@ -62,5 +65,5 @@ .Assets .Assets-content { background: var(--tree-bg-color) !important; - height: 250px !important; -} + height: calc(100% - 36px); +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/Assets/Assets.tsx b/packages/@dcl/inspector/src/components/Assets/Assets.tsx index 3f3d0d03a..91a3b25f0 100644 --- a/packages/@dcl/inspector/src/components/Assets/Assets.tsx +++ b/packages/@dcl/inspector/src/components/Assets/Assets.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useState } from 'react' import cx from 'classnames' import { AssetsTab } from './types' -import { Box } from '../Box' import { FolderOpen } from '../Icons/Folder' import { MdImageSearch } from 'react-icons/md' import { HiOutlinePlus } from 'react-icons/hi' @@ -14,11 +13,11 @@ import ImportAsset from '../ImportAsset' import './Assets.css' function Assets() { - const [tab, setTab] = useState(undefined) + const [tab, setTab] = useState(AssetsTab.FileSystem) const handleTabClick = useCallback( (value: AssetsTab) => () => { - setTab(tab === value ? undefined : value) + setTab(value) }, [tab] ) @@ -29,7 +28,7 @@ function Assets() { }, []) return ( - +
@@ -49,14 +48,12 @@ function Assets() {
- {tab && ( -
- {tab === AssetsTab.AssetsPack && } - {tab === AssetsTab.FileSystem && } - {tab === AssetsTab.Import && } -
- )} - +
+ {tab === AssetsTab.AssetsPack && } + {tab === AssetsTab.FileSystem && } + {tab === AssetsTab.Import && } +
+
) } diff --git a/packages/@dcl/inspector/src/components/Box/Box.css b/packages/@dcl/inspector/src/components/Box/Box.css index decb4f622..30227741e 100644 --- a/packages/@dcl/inspector/src/components/Box/Box.css +++ b/packages/@dcl/inspector/src/components/Box/Box.css @@ -1,3 +1,7 @@ +.Box { + height: calc(100% - 4.8px); +} + .Box.with-border { margin: 2.4px; padding: 3.2px; @@ -14,4 +18,5 @@ background: var(--main-bg-color); width: 100%; height: 100%; + position: relative; } \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/Container/Container.css b/packages/@dcl/inspector/src/components/Container/Container.css index 19df7df32..1f29cd521 100644 --- a/packages/@dcl/inspector/src/components/Container/Container.css +++ b/packages/@dcl/inspector/src/components/Container/Container.css @@ -2,11 +2,9 @@ display: flex; flex-direction: column; padding: 12px; - background-color: var(--modal); - border-radius: 8px; - margin: 12px; height: 100%; overflow-y: auto; + border-bottom: 1px solid var(--border-gray); } .Container.hover { @@ -27,10 +25,10 @@ margin-right: 4px; } -.Container > span { +.Container>span { margin: 0px 0px 4px 0px; color: var(--text); font-size: 14px; font-weight: 600; cursor: pointer; -} +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.css b/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.css new file mode 100644 index 000000000..fe98461de --- /dev/null +++ b/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.css @@ -0,0 +1,6 @@ +.EntityInspector { + position: absolute; + width: 100%; + height: 100%; + background-color: var(--tree-bg-color); +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx b/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx index 0f607c743..a544c229c 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/EntityInspector.tsx @@ -3,6 +3,7 @@ import { useSelectedEntity } from '../../hooks/sdk/useSelectedEntity' import { SceneInspector } from './SceneInspector' import { TransformInspector } from './TransformInspector' import { GltfInspector } from './GltfInspector' +import './EntityInspector.css' export const EntityInspector: React.FC = () => { const entity = useSelectedEntity() diff --git a/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.css b/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.css new file mode 100644 index 000000000..3ba42ecfb --- /dev/null +++ b/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.css @@ -0,0 +1,4 @@ +.Hierarchy { + height: 100%; + background: var(--tree-bg-color); +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.tsx b/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.tsx index 93ef462fb..311a900e0 100644 --- a/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.tsx +++ b/packages/@dcl/inspector/src/components/Hierarchy/Hierarchy.tsx @@ -6,9 +6,9 @@ import { FiHexagon } from 'react-icons/fi' import { ROOT } from '../../lib/sdk/tree' import { useSelectedEntity } from '../../hooks/sdk/useSelectedEntity' import { useTree } from '../../hooks/sdk/useTree' -import { Container } from '../Container' import { Tree } from '../Tree' import { ContextMenu } from './ContextMenu' +import './Hierarchy.css' function HierarchyIcon({ value, hasChildrens, isOpen }: { value: Entity; hasChildrens: boolean; isOpen: boolean }) { if (value === ROOT) { @@ -45,7 +45,7 @@ const Hierarchy: React.FC = () => { [selectedEntity] ) return ( - +
{ canRename={canRename} canRemove={canRemove} /> - +
) } diff --git a/packages/@dcl/inspector/src/components/Resizable/Resizable.css b/packages/@dcl/inspector/src/components/Resizable/Resizable.css deleted file mode 100644 index 2f9bf544e..000000000 --- a/packages/@dcl/inspector/src/components/Resizable/Resizable.css +++ /dev/null @@ -1,30 +0,0 @@ -.Resizable { - display: flex; - width: 100%; -} - -.Resizable .resize-handle { - width: 3px; - height: 100%; - cursor: col-resize; -} - -.Resizable.vertical { - display: flex; - flex-direction: column; -} - -.Resizable.vertical .resize-handle { - width: 100%; - height: 10px; - cursor: row-resize; -} - -.Resizable.vertical > div:first-child { - display: flex; - flex-direction: column; -} - -.Resizable.vertical > div:nth-child(3) { - overflow-y: auto; -} diff --git a/packages/@dcl/inspector/src/components/Resizable/Resizable.tsx b/packages/@dcl/inspector/src/components/Resizable/Resizable.tsx deleted file mode 100644 index 6b5a56e8a..000000000 --- a/packages/@dcl/inspector/src/components/Resizable/Resizable.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' - -import { PropTypes, TypeProps, HORIZONTAL_PROPS, VERTICAL_PROPS } from './types' - -import './Resizable.css' - -const getProperties = (type: PropTypes['type']): TypeProps => type === 'horizontal' ? HORIZONTAL_PROPS : VERTICAL_PROPS -const capitalize = (value: string) => value.charAt(0).toUpperCase() + value.slice(1) - -function Resizable(props: React.PropsWithChildren) { - const ref = useRef(null) - const [value, setValue] = useState<[number | null, number | null]>([props.initial ?? null, null]) - const [minValue, setMinValue] = useState(0) - const [dragging, setDragging] = useState(false) - const children = React.Children.toArray(props.children) - const { offsetValue, eventClientValue, css } = getProperties(props.type) - - if (!children.length) return null - if (children.length !== 2) return <>{props.children} - - useLayoutEffect(() => { - if (!ref.current) return - setValue([ref.current[offsetValue], getParentOffset() - ref.current[offsetValue]]) - }, []) - - useEffect(() => { - if (props.min === 'initial') { - setMinValue(ref.current![offsetValue]) - } else if (props.min) { - setMinValue(props.min) - } - }, [props.min]) - - const getParentOffset = useCallback(() => { - const parent = ref.current?.parentElement?.parentElement ?? undefined - return parent ? parent[offsetValue] : 0 - }, [value]) - - const handleDrag = (event: React.MouseEvent) => { - if (!dragging) return - resize(event[eventClientValue]) - } - - const resize = (value: number) => { - const diff = getParentOffset() - value - if (value <= minValue || value > (props.max || Infinity)) return - setValue([value, diff]) - if (props.onChange) props.onChange([value, diff]) - } - - const handleMouseUp = useCallback(() => setDragging(false), []) - - useEffect(() => { - document.addEventListener('mouseup', handleMouseUp) - return () => { - document.removeEventListener('mouseup', handleMouseUp) - } - }, []) - - const styles = { - [css.childs]: value[0] ?? 'auto', - [`min${capitalize(css.childs)}`]: minValue - } - - return ( -
-
{children[0]}
-
setDragging(true)} /> -
{children[1]}
-
- ) -} - -export default Resizable diff --git a/packages/@dcl/inspector/src/components/Resizable/index.ts b/packages/@dcl/inspector/src/components/Resizable/index.ts deleted file mode 100644 index e57490f72..000000000 --- a/packages/@dcl/inspector/src/components/Resizable/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import Resizable from './Resizable' -export { Resizable } diff --git a/packages/@dcl/inspector/src/components/Resizable/types.ts b/packages/@dcl/inspector/src/components/Resizable/types.ts deleted file mode 100644 index e06cf9103..000000000 --- a/packages/@dcl/inspector/src/components/Resizable/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface PropTypes { - type: 'horizontal' | 'vertical' - min?: 'initial' | number - initial?: number - max?: number - onChange?(value: [number, number]): void -} - -export interface TypeProps { - offsetValue: 'offsetWidth' | 'offsetHeight' - eventClientValue: 'clientX' | 'clientY' - css: { - childs: 'width' | 'height' - handle: 'left' | 'top' - } -} - -export const HORIZONTAL_PROPS: TypeProps = { - offsetValue: 'offsetWidth', - eventClientValue: 'clientX', - css: { - childs: 'width', - handle: 'left' - } -} - -export const VERTICAL_PROPS: TypeProps = { - offsetValue: 'offsetHeight', - eventClientValue: 'clientY', - css: { - childs: 'height', - handle: 'top' - } -} diff --git a/packages/@dcl/inspector/src/hooks/useWindowSize.ts b/packages/@dcl/inspector/src/hooks/useWindowSize.ts new file mode 100644 index 000000000..af97fba35 --- /dev/null +++ b/packages/@dcl/inspector/src/hooks/useWindowSize.ts @@ -0,0 +1,33 @@ +import { useState, useEffect } from 'react' + +// Define general type for useWindowSize hook, which includes width and height +interface Size { + width: number | undefined + height: number | undefined +} + +export function useWindowSize(): Size { + // Initialize state with undefined width/height so server and client renders match + // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/ + const [windowSize, setWindowSize] = useState({ + width: undefined, + height: undefined + }) + useEffect(() => { + // Handler to call on window resize + function handleResize() { + // Set window width/height to state + setWindowSize({ + width: window.innerWidth, + height: window.innerHeight + }) + } + // Add event listener + window.addEventListener('resize', handleResize) + // Call handler right away so state gets updated with initial window size + handleResize() + // Remove event listener on cleanup + return () => window.removeEventListener('resize', handleResize) + }, []) // Empty array ensures that effect is only run on mount + return windowSize +} From 57f9113464b42fc4ca10fb90d0babb9d81e8507e Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 15:13:56 -0300 Subject: [PATCH 2/8] chore: added tests --- .../@dcl/inspector/src/components/App/App.tsx | 4 +- .../inspector/src/hooks/useWindowSize.spec.ts | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 packages/@dcl/inspector/src/hooks/useWindowSize.spec.ts diff --git a/packages/@dcl/inspector/src/components/App/App.tsx b/packages/@dcl/inspector/src/components/App/App.tsx index bf197e127..13d960cbc 100644 --- a/packages/@dcl/inspector/src/components/App/App.tsx +++ b/packages/@dcl/inspector/src/components/App/App.tsx @@ -18,9 +18,7 @@ const App = () => { const { height } = useWindowSize() // Footer's height is 36 pixels, so we need to calculate the percentage of the screen that it takes to pass as the minSize prop for the Panel - const footerMin = (50 / height!) * 100 - - console.log(footerMin) + const footerMin = (48 / height!) * 100 return (
diff --git a/packages/@dcl/inspector/src/hooks/useWindowSize.spec.ts b/packages/@dcl/inspector/src/hooks/useWindowSize.spec.ts new file mode 100644 index 000000000..ba0bf9556 --- /dev/null +++ b/packages/@dcl/inspector/src/hooks/useWindowSize.spec.ts @@ -0,0 +1,40 @@ +import { act, renderHook } from '@testing-library/react' +import { useWindowSize } from './useWindowSize' + +const originalWidth = global.innerWidth +const originalHeight = global.innerHeight + +describe('useWindowSize', () => { + describe('When the hook is rendered', () => { + beforeEach(() => { + global.innerWidth = 1024 + global.innerHeight = 1024 + }) + afterEach(() => { + global.innerWidth = originalWidth + global.innerHeight = originalHeight + }) + it('should return the width and height of the window', () => { + const { result } = renderHook(() => useWindowSize()) + const { width, height } = result.current + expect(width).toBe(1024) + expect(height).toBe(1024) + }) + describe('and the window is resized', () => { + it('should return the new size of the window', () => { + const { result } = renderHook(() => useWindowSize()) + const { width, height } = result.current + expect(width).toBe(1024) + expect(height).toBe(1024) + act(() => { + global.innerWidth = 800 + global.innerHeight = 600 + global.dispatchEvent(new Event('resize')) + }) + const { width: newWidth, height: newHeight } = result.current + expect(newWidth).toBe(800) + expect(newHeight).toBe(600) + }) + }) + }) +}) From 201d0e3315bf810c9c7ecd0f6ce5ebc94820b8ee Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 16:25:13 -0300 Subject: [PATCH 3/8] chore: move to dev dependency --- packages/@dcl/inspector/package-lock.json | 20 +++++++++++++++----- packages/@dcl/inspector/package.json | 6 ++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/@dcl/inspector/package-lock.json b/packages/@dcl/inspector/package-lock.json index 4b58ea45f..9592f6772 100644 --- a/packages/@dcl/inspector/package-lock.json +++ b/packages/@dcl/inspector/package-lock.json @@ -7,9 +7,6 @@ "": { "name": "@dcl/inspector", "version": "0.1.0", - "dependencies": { - "react-resizable-panels": "^0.0.48" - }, "devDependencies": { "@babylonjs/core": "^5.48.0", "@babylonjs/gui": "^5.48.0", @@ -41,6 +38,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-modal": "^3.16.1", + "react-resizable-panels": "^0.0.48", "typescript": "^5.0.2" } }, @@ -2715,7 +2713,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/jsdom": { "version": "20.0.3", @@ -2809,6 +2808,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -3154,6 +3154,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -3217,6 +3218,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -3290,6 +3292,7 @@ "version": "0.0.48", "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.48.tgz", "integrity": "sha512-fJa3itmJ3HLLmVG7y8tka80wFW63N6ai76q7MGwU8nSXeA0qkX36vnmPyXm34lvtsGjn1Cgi5IPhPQnf42SVpA==", + "dev": true, "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" @@ -3520,6 +3523,7 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -6017,7 +6021,8 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "jsdom": { "version": "20.0.3", @@ -6097,6 +6102,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -6373,6 +6379,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, "requires": { "loose-envify": "^1.1.0" } @@ -6412,6 +6419,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -6468,6 +6476,7 @@ "version": "0.0.48", "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-0.0.48.tgz", "integrity": "sha512-fJa3itmJ3HLLmVG7y8tka80wFW63N6ai76q7MGwU8nSXeA0qkX36vnmPyXm34lvtsGjn1Cgi5IPhPQnf42SVpA==", + "dev": true, "requires": {} }, "react-resize-detector": { @@ -6646,6 +6655,7 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, "requires": { "loose-envify": "^1.1.0" } diff --git a/packages/@dcl/inspector/package.json b/packages/@dcl/inspector/package.json index aaeefbbf7..65af0d01f 100644 --- a/packages/@dcl/inspector/package.json +++ b/packages/@dcl/inspector/package.json @@ -32,6 +32,7 @@ "react-dom": "^18.2.0", "react-icons": "^4.7.1", "react-modal": "^3.16.1", + "react-resizable-panels": "^0.0.48", "typescript": "^5.0.2" }, "files": [ @@ -48,8 +49,5 @@ "start": "node ./build.js --watch" }, "types": "dist/tooling-entrypoint.d.ts", - "typings": "dist/tooling-entrypoint.d.ts", - "dependencies": { - "react-resizable-panels": "^0.0.48" - } + "typings": "dist/tooling-entrypoint.d.ts" } From e9384ed4545aa959646b2c4490f57356b33fa989 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 16:25:49 -0300 Subject: [PATCH 4/8] chore: fix import order and unused import --- packages/@dcl/inspector/src/components/App/App.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@dcl/inspector/src/components/App/App.tsx b/packages/@dcl/inspector/src/components/App/App.tsx index 13d960cbc..a681cd519 100644 --- a/packages/@dcl/inspector/src/components/App/App.tsx +++ b/packages/@dcl/inspector/src/components/App/App.tsx @@ -1,11 +1,11 @@ -import React, { useRef } from 'react' +import React from 'react' +import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels' import { EntityInspector } from '../EntityInspector' import { Hierarchy } from '../Hierarchy' import { Renderer } from '../Renderer' import { Box } from '../Box' import { Toolbar } from '../Toolbar' -import { PanelGroup, Panel, PanelResizeHandle, ImperativePanelHandle } from 'react-resizable-panels' import './App.css' import Assets from '../Assets' From c7f84af9448c0c4355737fb19cb9a7f9d8434574 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 16:26:31 -0300 Subject: [PATCH 5/8] chore: fix comment --- packages/@dcl/inspector/src/components/App/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@dcl/inspector/src/components/App/App.tsx b/packages/@dcl/inspector/src/components/App/App.tsx index a681cd519..308f75522 100644 --- a/packages/@dcl/inspector/src/components/App/App.tsx +++ b/packages/@dcl/inspector/src/components/App/App.tsx @@ -17,7 +17,7 @@ const App = () => { const { height } = useWindowSize() - // Footer's height is 36 pixels, so we need to calculate the percentage of the screen that it takes to pass as the minSize prop for the Panel + // Footer's height is 48 pixels, so we need to calculate the percentage of the screen that it takes to pass as the minSize prop for the Panel const footerMin = (48 / height!) * 100 return ( From cedb2bc8f0fc3f4a9c5b2e97426424f9aa7ab3b4 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 17:17:11 -0300 Subject: [PATCH 6/8] feat: improve styles --- .../src/components/EntityInspector/TextField/TextField.css | 4 ++-- packages/@dcl/inspector/src/components/Toolbar/Toolbar.css | 4 ++-- .../src/components/Toolbar/ToolbarButton/ToolbarButton.css | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/@dcl/inspector/src/components/EntityInspector/TextField/TextField.css b/packages/@dcl/inspector/src/components/EntityInspector/TextField/TextField.css index f88e796dd..ae4aceb3d 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/TextField/TextField.css +++ b/packages/@dcl/inspector/src/components/EntityInspector/TextField/TextField.css @@ -1,6 +1,6 @@ .TextField { padding: 4px 6px; - border: 1px solid var(--secondary-text); + border: 1px solid var(--border-gray); display: flex; margin-right: 8px; border-radius: 1.6px; @@ -28,4 +28,4 @@ .TextField:last-child { margin-right: 0px; -} +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/Toolbar/Toolbar.css b/packages/@dcl/inspector/src/components/Toolbar/Toolbar.css index 2a4066727..183c693fb 100644 --- a/packages/@dcl/inspector/src/components/Toolbar/Toolbar.css +++ b/packages/@dcl/inspector/src/components/Toolbar/Toolbar.css @@ -1,6 +1,6 @@ .Toolbar { display: flex; - background-color: #29262f; + background-color: var(--tree-bg-color); padding: 8px; width: 100%; } @@ -27,4 +27,4 @@ width: 18px; height: 18px; transform: translateY(2px); -} +} \ No newline at end of file diff --git a/packages/@dcl/inspector/src/components/Toolbar/ToolbarButton/ToolbarButton.css b/packages/@dcl/inspector/src/components/Toolbar/ToolbarButton/ToolbarButton.css index ed0b1d3f6..590be246c 100644 --- a/packages/@dcl/inspector/src/components/Toolbar/ToolbarButton/ToolbarButton.css +++ b/packages/@dcl/inspector/src/components/Toolbar/ToolbarButton/ToolbarButton.css @@ -3,7 +3,7 @@ padding: 0px 2px; margin: 0 8px; color: #fff; - background-color: #4b4852; + background-color: var(--tree-border-color); font-size: 16px; border-radius: 4px; cursor: pointer; @@ -11,7 +11,7 @@ } .ToolbarButton:hover:not(:disabled) { - background-color: #6c6974; + background-color: var(--background-gray); } .ToolbarButton:disabled { @@ -20,7 +20,7 @@ } .ToolbarButton.active { - background-color: #6c6974; + background-color: var(--vscode-accent-blue) !important; } .ToolbarButton svg { From 6634c353bddc5528e1cec98d0b4125ad0af153f5 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 17:22:43 -0300 Subject: [PATCH 7/8] chore: linted --- .../ProjectAssetExplorer/ProjectView.tsx | 183 ++++++++++++------ 1 file changed, 125 insertions(+), 58 deletions(-) diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx index 01071282f..9dbd6aa86 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx @@ -47,16 +47,16 @@ function ProjectView({ folders }: Props) { return `${node}/${children}` } const tree = new Map() - tree.set(ROOT, { children: folders.map(f => f.name), name: ROOT, type: 'folder', parent: null }) + tree.set(ROOT, { children: folders.map((f) => f.name), name: ROOT, type: 'folder', parent: null }) open.add(ROOT) - function hasMatch (name: string) { + function hasMatch(name: string) { return search && name.toLocaleLowerCase().includes(search.toLocaleLowerCase()) } function generateTree(node: AssetNodeFolder, parentName: string = ''): string[] { const namePath = getPath(parentName, node.name) - const childrens = node.children.map(c => `${namePath}/${c.name}`) + const childrens = node.children.map((c) => `${namePath}/${c.name}`) const matchesList: string[] = [] for (const children of node.children) { if (children.type === 'folder') { @@ -96,36 +96,45 @@ function ProjectView({ folders }: Props) { /** * Callbacks */ - const onToggle = useCallback((value: string, toggle: boolean) => { - setLastSelected(value) - if (toggle) { - open.add(value) - } else { - open.delete(value) - } - setOpen(new Set(open)) - }, [open, setOpen, setLastSelected]) - - const getEntitiesWithAsset = useCallback((value: string): Entity[] => { - if (!sdk || modal?.isOpen) return [] - const entitiesWithAsset: Entity[] = [] - const { GltfContainer } = sdk.components - for (const [entity, _value] of sdk.engine.getEntitiesWith(GltfContainer)) { - if (_value.src === value) { - entitiesWithAsset.push(entity) + const onToggle = useCallback( + (value: string, toggle: boolean) => { + setLastSelected(value) + if (toggle) { + open.add(value) + } else { + open.delete(value) } - } - return entitiesWithAsset - }, [sdk, modal]) - - const handleRemove = useCallback(async (value: string) => { - const path = getFullNodePath(tree.get(value)!).slice(1) - const entitiesWithAsset = getEntitiesWithAsset(path) - if (entitiesWithAsset.length) { - return setModal({ isOpen: true, value: path, entities: entitiesWithAsset }) - } - await removeAsset(path) - }, [open, setOpen]) + setOpen(new Set(open)) + }, + [open, setOpen, setLastSelected] + ) + + const getEntitiesWithAsset = useCallback( + (value: string): Entity[] => { + if (!sdk || modal?.isOpen) return [] + const entitiesWithAsset: Entity[] = [] + const { GltfContainer } = sdk.components + for (const [entity, _value] of sdk.engine.getEntitiesWith(GltfContainer)) { + if (_value.src === value) { + entitiesWithAsset.push(entity) + } + } + return entitiesWithAsset + }, + [sdk, modal] + ) + + const handleRemove = useCallback( + async (value: string) => { + const path = getFullNodePath(tree.get(value)!).slice(1) + const entitiesWithAsset = getEntitiesWithAsset(path) + if (entitiesWithAsset.length) { + return setModal({ isOpen: true, value: path, entities: entitiesWithAsset }) + } + await removeAsset(path) + }, + [open, setOpen] + ) const removeAsset = useCallback(async (path: string, _: Entity[] = []) => { if (!sdk) return @@ -141,37 +150,52 @@ function ProjectView({ folders }: Props) { }, [modal, setModal]) const handleModalClose = useCallback(() => setModal(undefined), []) - const handleClickFolder = useCallback((val: string) => () => { - if (lastSelected === val) return - open.add(val) - setLastSelected(val) - }, [setLastSelected]) + const handleClickFolder = useCallback( + (val: string) => () => { + if (lastSelected === val) return + open.add(val) + setLastSelected(val) + }, + [setLastSelected] + ) const handleDragContext = useCallback(() => ({ tree }), [tree]) const isOpen = useCallback((val: string) => open.has(val), [open]) - const getChildren = useCallback((val: string) => { - const value = tree.get(val) - if (!value?.children?.length) return [] - if (!search.length) return value.children + const getChildren = useCallback( + (val: string) => { + const value = tree.get(val) + if (!value?.children?.length) return [] + if (!search.length) return value.children - return value.children.filter(($) => { - const childrenValue = tree.get($) - return !!childrenValue?.matches?.length - }) - }, [tree, search]) + return value.children.filter(($) => { + const childrenValue = tree.get($) + return !!childrenValue?.matches?.length + }) + }, + [tree, search] + ) return ( <>

⚠️ Removing this asset will break some components

- - + +
- setSearch('')} /> + setSearch('')} + /> value.toString()} getChildren={getChildren} - getLabel={(val: string) => {tree.get(val)?.name ?? val}} + getLabel={(val: string) => {tree.get(val)?.name ?? val}} isOpen={isOpen} isSelected={(val: string) => lastSelected === val} canRename={() => false} @@ -197,18 +221,46 @@ function ProjectView({ folders }: Props) {
{selectedTreeNode?.type === 'folder' - ? selectedTreeNode?.children?.map($ => ) - : !!selectedTreeNode && lastSelected && - } + ? selectedTreeNode?.children?.map(($) => ( + + )) + : !!selectedTreeNode && + lastSelected && ( + + )}
) } -function NodeView({ valueId, value, onSelect, getDragContext }: { value?: TreeNode, onSelect: () => void, getDragContext: () => unknown, valueId: string }) { +function NodeView({ + valueId, + value, + onSelect, + getDragContext +}: { + value?: TreeNode + onSelect: () => void + getDragContext: () => unknown + valueId: string +}) { if (!value) return null - const [, drag] = useDrag(() => ({ type: DRAG_N_DROP_ASSET_KEY, item: { value: valueId, context: getDragContext() } }), [valueId]) + const [, drag] = useDrag( + () => ({ type: DRAG_N_DROP_ASSET_KEY, item: { value: valueId, context: getDragContext() } }), + [valueId] + ) return (
{value.type === 'folder' ? : } @@ -217,12 +269,27 @@ function NodeView({ valueId, value, onSelect, getDragContext }: { value?: TreeNo ) } -function NodeIcon({ value, isOpen }: { value?: TreeNode, isOpen: boolean }) { +function NodeIcon({ value, isOpen }: { value?: TreeNode; isOpen: boolean }) { if (!value) return null if (value.type === 'folder') { - return isOpen ? <> : <> + return isOpen ? ( + <> + + + + ) : ( + <> + + + + ) } - return <> + return ( + <> + + + + ) } export default React.memo(ProjectView) From bc74c7b892d7ef5e9332f4bdd8ff2e3548061499 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Thu, 1 Jun 2023 17:23:27 -0300 Subject: [PATCH 8/8] fix: remove asset --- .../ProjectAssetExplorer/ProjectView.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx index 9dbd6aa86..5122e7c89 100644 --- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx +++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx @@ -136,12 +136,15 @@ function ProjectView({ folders }: Props) { [open, setOpen] ) - const removeAsset = useCallback(async (path: string, _: Entity[] = []) => { - if (!sdk) return - const { dataLayer } = sdk - fileSystemEvent.emit('change') - await dataLayer.removeAsset({ path }) - }, []) + const removeAsset = useCallback( + async (path: string, _: Entity[] = []) => { + if (!sdk) return + const { dataLayer } = sdk + fileSystemEvent.emit('change') + await dataLayer.removeAsset({ path }) + }, + [sdk] + ) const handleConfirm = useCallback(async () => { if (!modal) return