diff --git a/app/src/components/left/DragDropPanel.tsx b/app/src/components/left/DragDropPanel.tsx index 256e14fd..6bb98eac 100644 --- a/app/src/components/left/DragDropPanel.tsx +++ b/app/src/components/left/DragDropPanel.tsx @@ -1,6 +1,7 @@ import { useDispatch, useSelector } from 'react-redux'; import Grid from '@mui/material/Grid'; import HTMLItem from './HTMLItem'; +import MUIItem from './MUIItem'; import React from 'react'; import { RootState } from '../../redux/store'; import { deleteElement } from '../../redux/reducers/slice/appStateSlice'; @@ -10,18 +11,17 @@ import AccordionSummary from '@mui/material/AccordionSummary'; import AccordionDetails from '@mui/material/AccordionDetails'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { makeStyles } from '@mui/styles'; -import ComponentDrag from './ComponentDrag'; - +import ComponentDrag from './ComponentDrag'; const useStyles = makeStyles({ accordion: { backgroundColor: '#000000', // Set the background color to gray - color: '#ffffff', // Set the text color to white + color: '#ffffff' // Set the text color to white }, accordionSummary: { backgroundColor: '#000000', // Set the background color of the summary to gray - color: '#ffffff', // Set the text color of the summary to white - }, + color: '#ffffff' // Set the text color of the summary to white + } }); const DragDropPanel = (props): JSX.Element => { @@ -46,9 +46,14 @@ const DragDropPanel = (props): JSX.Element => { (type) => type.name !== 'separator' ); + const muiTypesToRender = state.MUITypes.filter( + (type) => type.name !== 'separator' + ); + return (
+ {/* Root Components */} } @@ -62,6 +67,8 @@ const DragDropPanel = (props): JSX.Element => { + + {/* HTML Components */} } @@ -93,7 +100,7 @@ const DragDropPanel = (props): JSX.Element => { - + {/* MUI Components */} { - {htmlTypesToRender.map((option) => { - if (option.name === 'MUI') { - return ( - - ); - } + {muiTypesToRender.map((option) => { + return ( + + ); })} - + {/* React Router */} { - + {/* Next.js */} {state.projectType === 'Next.js' ? (

Next.js

@@ -183,4 +188,3 @@ const DragDropPanel = (props): JSX.Element => { }; export default DragDropPanel; - diff --git a/app/src/components/left/HTMLItem.tsx b/app/src/components/left/HTMLItem.tsx index 028ab8cd..cbe65eef 100644 --- a/app/src/components/left/HTMLItem.tsx +++ b/app/src/components/left/HTMLItem.tsx @@ -12,12 +12,14 @@ import * as Icons from '@mui/icons-material'; import { useDispatch, useSelector } from 'react-redux'; import { addChild } from '../../redux/reducers/slice/appStateSlice'; import { emitEvent } from '../../helperFunctions/socket'; +import { RootState } from '../../redux/store'; const useStyles = makeStyles({ HTMLPanelItem: { height: 'auto', width: 'auto', fontSize: 'small', + alignItems: 'center', display: 'flex', flexDirection: 'row', justifyContent: 'space-evenly', @@ -40,10 +42,8 @@ const HTMLItem: React.FC<{ }> = ({ name, id, icon, handleDelete }) => { const IconComponent = Icons[icon]; - const roomCode = useSelector((store: RootState) => store.roomSlice.roomCode); // current roomCode - const classes = useStyles(); const [modal, setModal] = useState(null); const [{ isDragging }, drag] = useDrag({ @@ -113,7 +113,6 @@ const HTMLItem: React.FC<{ ); }; - const dispatch = useDispatch(); const handleClick = () => { @@ -140,7 +139,10 @@ const HTMLItem: React.FC<{ {id <= 20 && (
{ @@ -183,4 +185,3 @@ const HTMLItem: React.FC<{ }; export default HTMLItem; - diff --git a/app/src/components/left/MUIItem.tsx b/app/src/components/left/MUIItem.tsx new file mode 100644 index 00000000..b91a59f7 --- /dev/null +++ b/app/src/components/left/MUIItem.tsx @@ -0,0 +1,195 @@ +import React, { useState } from 'react'; +import Grid from '@mui/material/Grid'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemText from '@mui/material/ListItemText'; +import makeStyles from '@mui/styles/makeStyles'; +import { useDrag } from 'react-dnd'; + +import { ItemTypes } from '../../constants/ItemTypes'; +import { RootState } from '../../redux/store'; +import * as Icons from '@mui/icons-material'; // Assuming a collection of MUI icons +import CodeIcon from '@mui/icons-material/Code'; // Default icon if specific icon not provided +import { useDispatch, useSelector } from 'react-redux'; +import { addChild } from '../../redux/reducers/slice/appStateSlice'; +import createModal from '../right/createModal'; // Modal creation utility +import { emitEvent } from '../../helperFunctions/socket'; // Event emission utility + +// Define component styles using MUI styling solutions +const useStyles = makeStyles({ + MUIPanelItem: { + height: 'auto', + width: 'auto', + fontSize: 'small', + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-evenly', + alignItems: 'center', + textAlign: 'center', + cursor: 'grab' + }, + lightThemeFontColor: { + color: '#8F8F8F' + }, + darkThemeFontColor: { + color: '#8F8F8F' + } +}); + +const MUIItem: React.FC<{ + name: string; + id: number; + icon: any; + handleDelete: (id: number) => void; +}> = ({ name, id, icon, handleDelete }) => { + const IconComponent = Icons[icon]; + + const roomCode = useSelector((store: RootState) => store.roomSlice.roomCode); // current roomCode + + // Use drag and drop functionality + const classes = useStyles(); + const [modal, setModal] = useState(null); + + const item = { + type: ItemTypes.INSTANCE, + newInstance: true, + instanceType: 'MUI Component', // MUI Element? - we should carefully consider what we call this + name, + icon, + instanceTypeId: id + }; + + // console.log('draggable item', item); + + const [{ isDragging }, drag] = useDrag({ + item, + collect: (monitor: any) => ({ + isDragging: !!monitor.isDragging() + }) + }); + + const closeModal = () => setModal(null); + const deleteAllInstances = (deleteID: number) => { + const children = ( + + handleDelete(deleteID)} + style={{ + border: '1px solid #C6C6C6', + marginBottom: '2%', + marginTop: '5%' + }} + > + + + + + + + ); + setModal( + createModal({ + closeModal, + children, + message: + 'Deleting this element will delete all instances of this element within the application.\nDo you still wish to proceed?', + primBtnLabel: null, + primBtnAction: null, + secBtnAction: null, + secBtnLabel: null, + open: true + }) + ); + }; + + const dispatch = useDispatch(); + + const handleClick = () => { + const childData = { + type: 'MUI Component', + typeId: id, + childId: null, + contextParam: { + allContext: [] + } + }; + + dispatch(addChild(childData)); + if (roomCode) { + // Emit 'addChildAction' event to the server + emitEvent('addChildAction', roomCode, childData); + } + }; + + // id over/under 20 logic + // html-g{name} - html grid name = item + return ( + + {id >= 20 && ( +
{ + handleClick(); + }} + > + {typeof IconComponent !== 'undefined' && ( + + )} + {name} +
+ )} + + {id < 20 && ( +
{ + handleClick(); + }} + > + {typeof CodeIcon !== 'undefined' && ( + + )} + {name} + +
+ )} + {modal} +
+ ); +}; + +export default MUIItem; diff --git a/app/src/components/main/Canvas.tsx b/app/src/components/main/Canvas.tsx index c491dfd3..3936c3d2 100644 --- a/app/src/components/main/Canvas.tsx +++ b/app/src/components/main/Canvas.tsx @@ -211,6 +211,7 @@ const Canvas = forwardRef(({ zoom }, ref) => { } // if item dropped is going to be a new instance (i.e. it came from the left panel), then create a new child component if (item.newInstance && item.instanceType !== 'Component') { + console.log('inside not component check', item); dispatch( //update state addChild({ diff --git a/app/src/components/main/DirectChildMUI.tsx b/app/src/components/main/DirectChildMUI.tsx new file mode 100644 index 00000000..934ef48a --- /dev/null +++ b/app/src/components/main/DirectChildMUI.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { ChildElement, MUIType } from '../../interfaces/Interfaces'; +import { useDrag } from 'react-dnd'; +import { ItemTypes } from '../../constants/ItemTypes'; +import { combineStyles } from '../../helperFunctions/combineStyles'; +import globalDefaultStyle from '../../public/styles/globalDefaultStyles'; +import DeleteButton from './DeleteButton'; +import { useDispatch, useSelector } from 'react-redux'; +import { changeFocus } from '../../redux/reducers/slice/appStateSlice'; +import { RootState } from '../../redux/store'; +import { emitEvent } from '../../helperFunctions/socket'; + +function DirectChildMUI({ childId, name, type, typeId, style }: ChildElement) { + const state = useSelector((store: RootState) => store.appState); + + const roomCode = useSelector((store: RootState) => store.roomSlice.roomCode); + + const dispatch = useDispatch(); + + // find the MUI element corresponding with this instance of an MUI element + // find the current component to render on the canvas + const MUIType: MUIType = state.MUITypes.find( + (type: MUIType) => type.id === typeId + ); + // hook that allows component to be draggable + const [{ isDragging }, drag] = useDrag({ + // setting item attributes to be referenced when updating state with new instance of dragged item + item: { + type: ItemTypes.INSTANCE, + newInstance: false, + childId: childId, + instanceType: type, + instanceTypeId: typeId + }, + collect: (monitor: any) => ({ + isDragging: !!monitor.isDragging() + }) + }); + + const changeFocusFunction = (componentId: number, childId: number | null) => { + dispatch(changeFocus({ componentId, childId })); + if (roomCode) { + emitEvent('changeFocusAction', roomCode, { + componentId: componentId, + childId: childId + }); + // console.log('emit focus event from DirectChildMUI'); + } + }; + + // onClickHandler is responsible for changing the focused component and child component + function onClickHandler(event) { + event.stopPropagation(); + changeFocusFunction(state.canvasFocus.componentId, childId); + } + + // combine all styles so that higher priority style specifications overrule lower priority style specifications + // priority order is 1) style directly set for this child (style), 2) style of the referenced HTML element, and 3) default styling + const interactiveStyle = { + border: + state.canvasFocus.childId === childId + ? '2px solid #0671e3' + : '1px solid #31343A' + }; + + const combinedStyle = combineStyles( + combineStyles(combineStyles(globalDefaultStyle, MUIType.style), style), + interactiveStyle + ); + + return ( +
+ + {MUIType.placeHolderShort} + + +
+ ); +} + +export default DirectChildMUI; diff --git a/app/src/components/main/DirectChildMUINestable.tsx b/app/src/components/main/DirectChildMUINestable.tsx new file mode 100644 index 00000000..f30848cb --- /dev/null +++ b/app/src/components/main/DirectChildMUINestable.tsx @@ -0,0 +1,240 @@ +import React, { useRef } from 'react'; +import { ChildElement, MUIType } from '../../interfaces/Interfaces'; +import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd'; +import { ItemTypes } from '../../constants/ItemTypes'; +import { combineStyles } from '../../helperFunctions/combineStyles'; +import globalDefaultStyle from '../../public/styles/globalDefaultStyles'; +import renderChildren from '../../helperFunctions/renderChildren'; +import DeleteButton from './DeleteButton'; +import validateNewParent from '../../helperFunctions/changePositionValidation'; +import componentNest from '../../helperFunctions/componentNestValidation'; +import AddRoute from './AddRoute'; +import AddLink from './AddLink'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState } from '../../redux/store'; +import { emitEvent } from '../../helperFunctions/socket'; + +import { + changeFocus, + changePosition, + addChild, + snapShotAction +} from '../../redux/reducers/slice/appStateSlice'; + +function DirectChildMUINestable({ + childId, + type, + typeId, + style, + children, + name, + attributes +}: ChildElement): React.JSX.Element { + const state = useSelector((store: RootState) => store.appState); + const contextParam = useSelector((store: RootState) => store.contextSlice); + + const dispatch = useDispatch(); + const roomCode = useSelector((store: RootState) => store.roomSlice.roomCode); + + const ref = useRef(null); + + // takes a snapshot of state to be used in UNDO and REDO cases. snapShotFunc is also invoked in Canvas.tsx + const snapShotFunc = () => { + //makes a deep clone of state + const deepCopiedState = JSON.parse(JSON.stringify(state)); + const focusIndex = state.canvasFocus.componentId - 1; + //pushes the last user action on the canvas into the past array of Component + dispatch( + snapShotAction({ + focusIndex: focusIndex, + deepCopiedState: deepCopiedState + }) + ); + }; + + // find the MUI element corresponding with this instance of an MUI element + // find the current component to render on the canvas + const MUIType: MUIType = state.MUITypes.find( + (type: MUIType) => type.id === typeId + ); + + // hook that allows component to be draggable + const [{ isDragging }, drag] = useDrag({ + // setting item attributes to be referenced when updating state with new instance of dragged item + item: { + type: ItemTypes.INSTANCE, + newInstance: false, + childId: childId, + instanceType: type, + instanceTypeId: typeId, + name: name + }, + canDrag: MUIType.id !== 1000, // dragging not permitted if element is separator + collect: (monitor: any) => { + return { + isDragging: !!monitor.isDragging() + }; + } + }); + + // both useDrop and useDrag used here to allow canvas components to be both a drop target and drag source + const [{ isOver }, drop] = useDrop({ + accept: ItemTypes.INSTANCE, + // triggered on drop + drop: (item: any, monitor: DropTargetMonitor) => { + const didDrop = monitor.didDrop(); + // takes a snapshot of state to be used in UNDO and REDO cases + snapShotFunc(); + if (didDrop) { + return; + } + // updates state with new instance + // if item dropped is going to be a new instance (i.e. it came from the left panel), then create a new child component + if (item.newInstance) { + if ( + (item.instanceType === 'Component' && + componentNest( + state.components[item.instanceTypeId - 1].children, + childId + )) || + item.instanceType !== 'Component' + ) { + dispatch( + addChild({ + type: item.instanceType, + typeId: item.instanceTypeId, + childId: childId, + contextParam: contextParam + }) + ); + if (roomCode) { + emitEvent('addChildAction', roomCode, { + type: item.instanceType, + typeId: item.instanceTypeId, + childId: childId, + contextParam: contextParam + }); + + // console.log( + // 'emit addChildAction event is triggered in DirectChildMUINestable' + // ); + } + } + } + // if item is not a new instance, change position of element dragged inside div so that the div is the new parent + else { + // check to see if the selected child is trying to nest within itself + if (validateNewParent(state, item.childId, childId) === true) { + dispatch( + changePosition({ + currentChildId: item.childId, + newParentChildId: childId, + contextParam: contextParam + }) + ); + if (roomCode) { + emitEvent('changePositionAction', roomCode, { + currentChildId: item.childId, + newParentChildId: childId, + contextParam: contextParam + }); + + // console.log( + // 'emit changePosition event is triggered in DirectChildMUINestable' + // ); + } + } + } + }, + + collect: (monitor: any) => { + return { + isOver: !!monitor.isOver({ shallow: true }) + }; + } + }); + + const changeFocusFunction = (componentId: number, childId: number | null) => { + dispatch(changeFocus({ componentId, childId })); + if (roomCode) { + emitEvent('changeFocusAction', roomCode, { + componentId: componentId, + childId: childId + }); + // console.log('emit focus event from DirectChildMUINestable'); + } + }; + + // onClickHandler is responsible for changing the focused component and child component + function onClickHandler(event) { + event.stopPropagation(); + changeFocusFunction(state.canvasFocus.componentId, childId); + } + + // combine all styles so that higher priority style specifications overrule lower priority style specifications + // priority order is 1) style directly set for this child (style), 2) style of the referenced MUI element, and 3) default styling + const defaultNestableStyle = { ...globalDefaultStyle }; + const interactiveStyle = { + border: + state.canvasFocus.childId === childId + ? '2px solid #0671e3' + : '1px solid #31343A' + }; + + // interactive style to change color when nested element is hovered over + if (isOver) defaultNestableStyle['#3c59ba']; + defaultNestableStyle['backgroundColor'] = isOver + ? '#3c59ba' + : defaultNestableStyle['backgroundColor']; + + const combinedStyle = combineStyles( + combineStyles(combineStyles(defaultNestableStyle, MUIType.style), style), + interactiveStyle + ); + + drag(drop(ref)); + + const routeButton = []; + if (typeId === 17) { + routeButton.push(); + } + if (typeId === 19) { + routeButton.push( + + ); + } + + return ( +
+ + {MUIType.placeHolderShort} + + {attributes && attributes.compLink ? ` ${attributes.compLink}` : ''} + + {routeButton} + + + {renderChildren(children)} +
+ ); +} + +export default DirectChildMUINestable; diff --git a/app/src/components/main/SeparatorChild.tsx b/app/src/components/main/SeparatorChild.tsx index 8d9388ec..f2910ebb 100644 --- a/app/src/components/main/SeparatorChild.tsx +++ b/app/src/components/main/SeparatorChild.tsx @@ -1,5 +1,10 @@ import React, { useRef } from 'react'; -import { ChildElement, HTMLType, DragItem } from '../../interfaces/Interfaces'; +import { + ChildElement, + HTMLType, + MUIType, + DragItem +} from '../../interfaces/Interfaces'; import { useDrag, useDrop, DropTargetMonitor } from 'react-dnd'; import { ItemTypes } from '../../constants/ItemTypes'; import { combineStyles } from '../../helperFunctions/combineStyles'; @@ -37,6 +42,9 @@ function SeparatorChild({ (type: HTMLType) => type.id === typeId ); + const MUIType: MUIType = state.MUITypes.find( + (type: MUIType) => type.id === typeId + ); // hook that allows component to be draggable const [{ isDragging }, drag] = useDrag({ // setting item attributes to be referenced when updating state with new instance of dragged item @@ -47,7 +55,7 @@ function SeparatorChild({ instanceType: type, instanceTypeId: typeId }, - canDrag: HTMLType.id !== 1000, // dragging not permitted if element is separator + canDrag: HTMLType.id !== 1000 || MUIType.id !== 1000, // dragging not permitted if element is separator collect: (monitor: any) => { return { isDragging: !!monitor.isDragging() @@ -144,7 +152,7 @@ function SeparatorChild({ // priority order is 1) style directly set for this child (style), 2) style of the referenced HTML element, and 3) default styling const defaultNestableStyle = { ...globalDefaultStyle }; const separatorStyle = { - padding: isOver ? '40px 10px' : '2px 10px', + padding: isOver ? '40px 10px' : '2px 10px', margin: '1px 10px', transition: 'padding 1s ease-out' }; diff --git a/app/src/helperFunctions/generateCode.ts b/app/src/helperFunctions/generateCode.ts index bd9b1e9e..35bbf93e 100644 --- a/app/src/helperFunctions/generateCode.ts +++ b/app/src/helperFunctions/generateCode.ts @@ -2,6 +2,7 @@ import { Component, ChildElement, HTMLType, + MUIType, ChildStyle, StateProp } from '../interfaces/Interfaces'; @@ -17,6 +18,7 @@ const generateCode = ( rootComponents: number[], projectType: string, HTMLTypes: HTMLType[], + MUITypes: MUIType[], tailwind: boolean, contextParam: any ) => { @@ -26,6 +28,7 @@ const generateCode = ( rootComponents, projectType, HTMLTypes, + MUITypes, tailwind, contextParam ); @@ -39,6 +42,7 @@ const generateUnformattedCode = ( rootComponents: number[], projectType: string, HTMLTypes: HTMLType[], + MUITypes: MUIType[], tailwind: boolean, contextParam: any ) => { @@ -105,6 +109,30 @@ const generateUnformattedCode = ( else if (referencedHTML.tag === 'Link') links = true; if (referencedHTML.tag === 'Image') images = true; return child; + } else if (child.type === 'MUI Component') { + const referencedMUI = MUITypes.find((elem) => elem.id === child.typeId); + child['tag'] = referencedMUI.tag; + if ( + referencedMUI.tag === 'button' || + referencedMUI.tag === 'card' || + referencedMUI.tag === 'typography' || + referencedMUI.tag === 'input' || + referencedMUI.tag === 'Link' || + referencedMUI.tag === 'Switch' || + referencedMUI.tag === 'Route' + ) { + child.children = getEnrichedChildren(child); + } + + // when we see a Switch or LinkTo, import React Router + if ( + (referencedMUI.tag === 'Switch' || referencedMUI.tag === 'Route') && + projectType === 'Classic React' + ) + importReactRouter = true; + else if (referencedMUI.tag === 'Link') links = true; + if (referencedMUI.tag === 'Image') images = true; + return child; } else if (child.type === 'Route Link') { links = true; child.name = components.find( @@ -499,6 +527,15 @@ const generateUnformattedCode = ( if (child.children.length !== 0) importStr += createEventHandler(child.children); } + if (child.type === 'MUI Component') { + if (child.events) { + for (const [event, funcName] of Object.entries(child.events)) { + importStr += `\tconst ${funcName} = () => {};\n`; + } + } + if (child.children.length !== 0) + importStr += createEventHandler(child.children); + } }); return importStr; diff --git a/app/src/helperFunctions/renderChildren.tsx b/app/src/helperFunctions/renderChildren.tsx index bbae5960..9a19901a 100644 --- a/app/src/helperFunctions/renderChildren.tsx +++ b/app/src/helperFunctions/renderChildren.tsx @@ -3,6 +3,8 @@ import { ChildElement } from '../interfaces/Interfaces'; import DirectChildComponent from '../components/main/DirectChildComponent'; import DirectChildHTML from '../components/main/DirectChildHTML'; import DirectChildHTMLNestable from '../components/main/DirectChildHTMLNestable'; +import DirectChildMUI from '../components/main/DirectChildMUI'; +import DirectChildMUINestable from '../components/main/DirectChildMUINestable'; import SeparatorChild from '../components/main/SeparatorChild'; import RouteLink from '../components/main/RouteLink'; import { useSelector } from 'react-redux'; @@ -17,7 +19,12 @@ const renderChildren = (children: ChildElement[]) => { return children.map((child: ChildElement, i: number) => { const { type, style, childId, children, attributes, name } = child; + // console.log('state components', state.components); + // console.log('state MUI type', state.MUITypes); let { typeId } = child; + // console.log('typeID', typeId); + // console.log('name', name); + // console.log('child.name', state.components[typeId - 1].name); if (name === '') child.name = state.components[typeId - 1].name; // A DirectChildComponent is an instance of a top level component // This component will render IndirectChild components (div/components rendered inside a child component) @@ -39,7 +46,8 @@ const renderChildren = (children: ChildElement[]) => { /> ); } - // child is a non-nestable type of HTML element (aka NOT divs, forms, OrderedLists, UnorderedLists, menus) + // child is a non-nestable type of HTML element + // nestable = false -> input(10), img(12), image(20) else if ( type === 'HTML Element' && typeId !== 11 && @@ -119,6 +127,63 @@ const renderChildren = (children: ChildElement[]) => { passedInProps={[]} /> ); + } else if (type === 'MUI Component' && typeId === 31) { + return ( + + ); + } + // child is a nestable type of HTML element (divs, forms, OrderedLists, UnorderedLists, menus) + else if ( + type === 'MUI Component' && + (typeId === 21 || typeId === 41 || typeId === 51) + ) { + return ( + + ); + } else if (type === 'MUI Component' && typeId === 1000) { + return ( + + ); } else if (type === 'HTML Element' && typeId === 1000) { return ( { // if no match is found return false return; }; +function createHTMLElement(data) { + return { + ...data, + isHTMLElement: true + }; +} + // update all ids and typeIds to match one another const updateAllIds = (comp: Component[] | ChildElement[]) => { // put components' names and ids into an obj @@ -272,6 +282,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -280,7 +291,6 @@ const appStateSlice = createSlice({ }, addChild: (state, action) => { - let parentComponentId: number; const { type, typeId, @@ -288,60 +298,57 @@ const appStateSlice = createSlice({ }: { type: string; typeId: number; childId: any } = action.payload; // determine the parent component id - if (action.payload.copyId) { - parentComponentId = action.payload.copyId; - } else { - parentComponentId = state.canvasFocus.componentId; - } + const parentComponentId = + action.payload.copyId || state.canvasFocus.componentId; // make a copy of the components from state const components = [...state.components]; // find the parent component const parentComponent = findComponent(components, parentComponentId); + if (!parentComponent) return state; // ensure that parent component exists // if type is 'Component', loop through components to find componentName and componentChildren let componentName: string = ''; let componentChildren: ChildElement[] = []; - if (type === 'Component') { - components.forEach((comp) => { - if (comp.id === typeId) { - componentName = comp.name; - componentChildren = comp.children; - } - }); - } + if (type === 'Component') { const originalComponent = findComponent(state.components, typeId); if (originalComponent) { + componentName = originalComponent.name; + componentChildren = originalComponent.children; if ( childTypeExists('Component', parentComponentId, originalComponent) ) return state; } - } - - // create a new name based on the type of element - let newName = state.HTMLTypes.reduce((name, el) => { - if (typeId === el.id) { - name = type === 'Component' ? componentName : el.tag; + } else if (type === 'Route Link') { + const routeLinkComponent = components.find( + (comp) => comp.id === typeId + ); + if (routeLinkComponent) { + componentName = routeLinkComponent.name; } - return name; - }, ''); - - if (type === 'Route Link') { - components.find((comp) => { - if (comp.id === typeId) { - newName = comp.name; - return; + } else if (type === 'HTML Element') { + componentName = state.HTMLTypes.reduce((name, el) => { + if (typeId === el.id) { + name = el.tag; } - }); + return name; + }, ''); + } else { + componentName = state.MUITypes.reduce((name, el) => { + if (typeId === el.id) { + name = el.tag; + } + return name; + }, ''); } const newChild: ChildElement = { type, typeId, - name: newName, + name: componentName, childId: state.nextChildId, style: {}, attributes: {}, @@ -369,26 +376,20 @@ const appStateSlice = createSlice({ // if the newChild Element is an input or img type, delete the children key/value pair // if (newChild.name === 'input' && newChild.name === 'img') // delete newChild.children; - let directParent: HTMLElement | any; + // let directParent: HTMLElement | any; if (childId === null) { - if (parentComponent) { - parentComponent.children.push(topSeparator); - parentComponent.children.push(newChild); - } - } - // if there is a childId (childId here references the direct parent of the new child) find that child and add new child to its children array - else { - if (parentComponent) { - directParent = findChild(parentComponent, childId); - //disable nesting a component inside a HTML element + parentComponent.children.push(topSeparator, newChild); + } else { + const directParent = findChild(parentComponent, childId); + if (directParent) { if (directParent.type === 'HTML Element' && type === 'HTML Element') { - directParent.children.push(topSeparator); - directParent.children.push(newChild); + directParent.children.push(topSeparator, newChild); } else { return { ...state }; } } } + // update canvasFocus to the new child const canvasFocus = { ...state.canvasFocus, @@ -396,38 +397,34 @@ const appStateSlice = createSlice({ childId: newChild.childId }; - const nextChildId = state.nextChildId + 1; - let nextTopSeparatorId = state.nextTopSeparatorId + 1; - let addChildArray = components[canvasFocus.componentId - 1].children; - // merge separator needs interface and type - addChildArray = manageSeparators.mergeSeparator(addChildArray, 1); - if (directParent && directParent.name === 'separator') - nextTopSeparatorId = manageSeparators.handleSeparators( - // handle separator needs interface and type - addChildArray, - 'add' - ); - - components[canvasFocus.componentId - 1].children = addChildArray; + // Increment IDs + state.nextChildId += 1; + state.nextTopSeparatorId += 1; - // generate code - if (parentComponent) { - parentComponent.code = generateCode( - components, - parentComponentId, - [...state.rootComponents], - state.projectType, - state.HTMLTypes, - state.tailwind, - action.payload.contextParam + // Update the components array and potentially other parts of the state + components[parentComponentId - 1].children = + manageSeparators.mergeSeparator( + components[parentComponentId - 1].children, + 1 ); - } - // update the state with the new values + // Generate code for the component + parentComponent.code = generateCode( + components, + parentComponentId, + [...state.rootComponents], + state.projectType, + state.HTMLTypes, + state.MUITypes, + state.tailwind, + action.payload.contextParam + ); + + // Update state state.components = components; - state.nextChildId = nextChildId; state.canvasFocus = canvasFocus; - state.nextTopSeparatorId = nextTopSeparatorId; + + return state; }, changeTailwind: (state, action) => { @@ -465,6 +462,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -489,6 +487,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -556,6 +555,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -582,6 +582,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -610,6 +611,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -639,6 +641,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -665,6 +668,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -723,6 +727,7 @@ const appStateSlice = createSlice({ rootComponents, state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -753,6 +758,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -781,6 +787,7 @@ const appStateSlice = createSlice({ const stylesheet = ''; const resetHTMLTypes = HTMLTypes; + const resetMUITypes = MUITypes; return { ...state, @@ -791,7 +798,8 @@ const appStateSlice = createSlice({ components, canvasFocus, stylesheet, - HTMLTypes: resetHTMLTypes + HTMLTypes: resetHTMLTypes, + MUITypes: resetMUITypes }; }, updateProjectName: (state, action) => { @@ -814,6 +822,12 @@ const appStateSlice = createSlice({ } return el.id !== action.payload.id; }); + // const MUITypes: MUIType[] = [...state.MUITypes].filter((el) => { + // if (el.id === action.payload.id) { + // name = el.tag; + // } + // return el.id !== action.payload.id; + // }); const components: Component[] = deleteById( action.payload.id, name, @@ -828,6 +842,7 @@ const appStateSlice = createSlice({ rootComponents, state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -835,6 +850,7 @@ const appStateSlice = createSlice({ state.canvasFocus = canvasFocus; state.HTMLTypes = HTMLTypes; + // state.MUITypes = MUITypes; }, deleteChild: (state, action) => { @@ -880,6 +896,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -931,6 +948,7 @@ const appStateSlice = createSlice({ state.rootComponents, state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -959,6 +977,7 @@ const appStateSlice = createSlice({ state.rootComponents, state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -982,6 +1001,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1035,6 +1055,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1045,6 +1066,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1101,6 +1123,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1125,6 +1148,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1148,6 +1172,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1162,6 +1187,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1266,6 +1292,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam ); @@ -1278,6 +1305,7 @@ const appStateSlice = createSlice({ [...state.rootComponents], state.projectType, state.HTMLTypes, + state.MUITypes, state.tailwind, action.payload.contextParam );