diff --git a/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.css b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.css new file mode 100644 index 000000000..e5702c2f5 --- /dev/null +++ b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.css @@ -0,0 +1,3 @@ +.Container.Scene .content svg { + cursor: pointer; +} diff --git a/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.tsx b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.tsx index a093c4008..d28e60d89 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.tsx +++ b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/SceneInspector.tsx @@ -1,3 +1,6 @@ +import { useCallback, useState } from 'react' +import { RxBorderAll } from 'react-icons/rx' + import { useComponentInput } from '../../../hooks/sdk/useComponentInput' import { useHasComponent } from '../../../hooks/sdk/useHasComponent' import { withSdk } from '../../../hoc/withSdk' @@ -5,13 +8,27 @@ import { Block } from '../../Block' import { Container } from '../../Container' import { TextField } from '../TextField' import { Props } from './types' -import { fromScene, toScene, isValidInput } from './utils' +import { fromScene, toScene, toSceneAuto, getInputValidation } from './utils' + +import './SceneInspector.css' export default withSdk(({ sdk, entity }) => { + const [auto, setAuto] = useState(false) const { Scene } = sdk.components const hasScene = useHasComponent(entity, Scene) - const { getInputProps } = useComponentInput(entity, Scene, fromScene, toScene, isValidInput) + const { getInputProps } = useComponentInput( + entity, + Scene, + fromScene, + auto ? toSceneAuto : toScene, + getInputValidation(auto) + ) + const parcelsProps = getInputProps('layout.parcels') + + const handleClick = useCallback(() => { + setAuto(!auto) + }, [auto]) if (!hasScene) { return null @@ -20,7 +37,8 @@ export default withSdk(({ sdk, entity }) => { return ( - + + ) diff --git a/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.spec.ts b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.spec.ts new file mode 100644 index 000000000..c9ea429df --- /dev/null +++ b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.spec.ts @@ -0,0 +1,178 @@ +import { EditorComponentsTypes } from '../../../lib/sdk/components' +import { Coords } from '../../../lib/utils/layout' +import { SceneInput } from './types' +import { fromScene, getCoordinatesBetweenPoints, getInputValidation, parseParcels, toScene, toSceneAuto } from './utils' + +describe('SceneInspector/utils', () => { + describe('fromScene', () => { + it('should convert a Scene to a SceneInput', () => { + const scene: EditorComponentsTypes['Scene'] = { + layout: { + base: { x: 1, y: 1 }, + parcels: [ + { x: 1, y: 1 }, + { x: 2, y: 2 } + ] + } + } + + const result = fromScene(scene) + + expect(result).toEqual({ + layout: { + parcels: '1,1 2,2' + } + }) + }) + }) + + describe('toScene', () => { + it('should convert a SceneInput to a Scene', () => { + const input: SceneInput = { + layout: { + parcels: '1,1 2,2' + } + } + + const result = toScene(input) + + expect(result).toEqual({ + layout: { + base: { x: 1, y: 1 }, + parcels: [ + { x: 1, y: 1 }, + { x: 2, y: 2 } + ] + } + }) + }) + }) + + describe('toSceneAuto', () => { + it('should convert a SceneInput to a Scene with auto-generated base and parcels', () => { + const input: SceneInput = { + layout: { + parcels: '1,1 2,2' + } + } + + const result = toSceneAuto(input) + + expect(result).toEqual({ + layout: { + base: { x: 1, y: 1 }, + parcels: [ + { x: 1, y: 1 }, + { x: 1, y: 2 }, + { x: 2, y: 1 }, + { x: 2, y: 2 } + ] + } + }) + }) + }) + + describe('parseParcels', () => { + it('should parse a string of parcels into Coords array', () => { + const input = '1,1 2,2 3,3' + + const result = parseParcels(input) + + expect(result).toEqual([ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 } + ]) + }) + + it('should return an empty array for invalid input', () => { + const input = '1,1 2,2 invalid 3,3' + + const result = parseParcels(input) + + expect(result).toEqual([]) + }) + }) + + describe('getInputValidation', () => { + it('should return a validation function that checks for connected parcels', () => { + const inputValidation = getInputValidation(false) + const validInput: SceneInput = { + layout: { + parcels: '1,1 1,2' + } + } + const invalidInput: SceneInput = { + layout: { + parcels: '1,1 2,2' + } + } + + const isValidValidInput = inputValidation(validInput) + const isValidInvalidInput = inputValidation(invalidInput) + + expect(isValidValidInput).toBe(true) + expect(isValidInvalidInput).toBe(false) + }) + + it('should return a validation function that checks for auto-generated parcels', () => { + const inputValidation = getInputValidation(true) + const validInput: SceneInput = { + layout: { + parcels: '1,1 2,2' + } + } + const invalidInput: SceneInput = { + layout: { + parcels: '1,1 1,2 1,3' + } + } + + const isValidValidInput = inputValidation(validInput) + const isValidInvalidInput = inputValidation(invalidInput) + + expect(isValidValidInput).toBe(true) + expect(isValidInvalidInput).toBe(false) + }) + }) + + describe('getCoordinatesBetweenPoints', () => { + it('should return an array of coordinates between two points', () => { + const pointA: Coords = { x: 0, y: 0 } + const pointB: Coords = { x: 2, y: 2 } + + const result = getCoordinatesBetweenPoints(pointA, pointB) + + expect(result).toEqual([ + { x: 0, y: 0 }, + { x: 0, y: 1 }, + { x: 0, y: 2 }, + { x: 1, y: 0 }, + { x: 1, y: 1 }, + { x: 1, y: 2 }, + { x: 2, y: 0 }, + { x: 2, y: 1 }, + { x: 2, y: 2 } + ]) + }) + + it('should return an array with a single coordinate when both points are the same', () => { + const pointA: Coords = { x: 3, y: 3 } + const pointB: Coords = { x: 3, y: 3 } + + const result = getCoordinatesBetweenPoints(pointA, pointB) + + expect(result).toEqual([{ x: 3, y: 3 }]) + }) + + it('should return the bottom-left/top-right parcel as the first & second coords', () => { + const pointA: Coords = { x: 9, y: 7 } + const pointB: Coords = { x: 5, y: 3 } + + const result = getCoordinatesBetweenPoints(pointA, pointB) + + expect(result[0]).toEqual({ x: 5, y: 3 }) + expect(result[result.length - 1]).toEqual({ x: 9, y: 7 }) + }) + }) +}) diff --git a/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.ts b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.ts index 7d30d10c1..cd0b8d9b5 100644 --- a/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.ts +++ b/packages/@dcl/inspector/src/components/EntityInspector/SceneInspector/utils.ts @@ -34,17 +34,55 @@ export function toScene(inputs: SceneInput): EditorComponentsTypes['Scene'] { } } -export function isValidInput(input: SceneInput): boolean { - const parcels = input.layout.parcels.split(' ') +export function toSceneAuto(inputs: SceneInput): EditorComponentsTypes['Scene'] { + const parcels = parseParcels(inputs.layout.parcels) + const points = getCoordinatesBetweenPoints(parcels[0], parcels[1]) + return { + layout: { + base: points[0], + parcels: points + } + } +} + +export function parseParcels(value: string): Coords[] { + const parcels = value.split(' ') const coordsList: Coords[] = [] for (const parcel of parcels) { const coords = parcel.split(',') const x = parseInt(coords[0]) const y = parseInt(coords[1]) - if (coords.length !== 2 || isNaN(x) || isNaN(y)) return false + if (coords.length !== 2 || isNaN(x) || isNaN(y)) return [] coordsList.push({ x, y }) } - return areConnected(coordsList) + return coordsList +} + +export function getInputValidation(auto?: boolean) { + return function isValidInput(input: SceneInput): boolean { + const parcels = parseParcels(input.layout.parcels) + return auto ? parcels.length === 2 : areConnected(parcels) + } +} + +export function getCoordinatesBetweenPoints(pointA: Coords, pointB: Coords): Coords[] { + const coordinates: Coords[] = [] + + // ensure pointA is the bottom-left coord + if (pointA.x > pointB.x) { + ;[pointA.x, pointB.x] = [pointB.x, pointA.x] + } + if (pointA.y > pointB.y) { + ;[pointA.y, pointB.y] = [pointB.y, pointA.y] + } + + for (let x = pointA.x; x <= pointB.x; x++) { + for (let y = pointA.y; y <= pointB.y; y++) { + coordinates.push({ x, y }) + } + } + + return coordinates } diff --git a/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx b/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx index 11ee46cd1..1b5151916 100644 --- a/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx +++ b/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx @@ -78,8 +78,8 @@ async function validateAsset(extension: string, data: ArrayBuffer): Promise