├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── screens ├── output.gif └── screen1.png ├── src ├── _test_ │ └── function.test.js ├── components │ ├── droppablePlace │ │ ├── droppable.css │ │ └── index.js │ ├── renderPlace │ │ ├── index.js │ │ └── renderPlace.css │ └── sidebar │ │ ├── index.js │ │ └── sidebar.css ├── helpers │ ├── generateUiElements.js │ ├── reOrderFn.js │ └── styleFn.js ├── index.css ├── index.js ├── menu.svg ├── serviceWorker.js └── setupTests.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | 13 | ### `About` 14 | 15 | This project is a GUI builder created using react and react-beautiful-dnd. The main goal is to play with drag and drop feature and also to generate the html elements based on the user clicks. 16 | 17 | ### Screens 18 | ![Screen](https://github.com/karthick3018/react-gui/blob/master/screens/screen1.png) 19 | 20 | ![Preview](https://github.com/karthick3018/react-gui/blob/master/screens/output.gif) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-gui-builder", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": " https://karthick3018.github.io/react-gui/", 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^4.2.4", 8 | "@testing-library/react": "^9.3.2", 9 | "@testing-library/user-event": "^7.1.2", 10 | "gh-pages": "^3.1.0", 11 | "react": "^16.13.1", 12 | "react-beautiful-dnd": "^13.0.0", 13 | "react-dom": "^16.13.1", 14 | "react-scripts": "3.4.3" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject", 21 | "deploy": "gh-pages -d build" 22 | }, 23 | "eslintConfig": { 24 | "extends": "react-app" 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthick3018/react-gui/817540aac73f835e39e6f81a60a44bc4ba97feba/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React GUI 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthick3018/react-gui/817540aac73f835e39e6f81a60a44bc4ba97feba/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthick3018/react-gui/817540aac73f835e39e6f81a60a44bc4ba97feba/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /screens/output.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthick3018/react-gui/817540aac73f835e39e6f81a60a44bc4ba97feba/screens/output.gif -------------------------------------------------------------------------------- /screens/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karthick3018/react-gui/817540aac73f835e39e6f81a60a44bc4ba97feba/screens/screen1.png -------------------------------------------------------------------------------- /src/_test_/function.test.js: -------------------------------------------------------------------------------- 1 | import {render} from '@testing-library/react'; 2 | import {generateElement,returnRespectiveHtmlElement} from '../helpers/generateUiElements'; 3 | 4 | 5 | test('check generateElement function',()=>{ 6 | let result,temp={id:'302',element:'button'}; 7 | result = generateElement('button','302'); 8 | expect(temp).toEqual(expect.objectContaining(result)); 9 | }) 10 | 11 | it('checks returnRespectiveHtmlElement function',()=>{ 12 | let JsxElement = returnRespectiveHtmlElement('button'); 13 | const { getByText } = render(JsxElement); 14 | const createdElement = getByText(/i'm button/i); 15 | expect(createdElement).toBeInTheDocument(); 16 | }) -------------------------------------------------------------------------------- /src/components/droppablePlace/droppable.css: -------------------------------------------------------------------------------- 1 | .droppable-wrapper{ 2 | width: 100%; 3 | border: 1px solid; 4 | overflow-y: scroll; 5 | background-image: linear-gradient(to right, rgb(217, 226, 233) 1px, transparent 1px), linear-gradient(rgb(217, 226, 233) 1px, transparent 1px); 6 | background-size: 20px 20px; 7 | background-color: rgb(237, 242, 246); 8 | } 9 | 10 | .droppable-element{ 11 | width: 100%; 12 | display: flex; 13 | } 14 | 15 | .help-text{ 16 | display: grid; 17 | justify-content: center; 18 | opacity: 0.3; 19 | } -------------------------------------------------------------------------------- /src/components/droppablePlace/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Droppable, Draggable } from 'react-beautiful-dnd'; 3 | import { getDraggableItemStyle } from '../../helpers/styleFn.js'; 4 | import {returnRespectiveHtmlElement} from '../../helpers/generateUiElements'; 5 | import './droppable.css' 6 | 7 | const DroppablePlace = ({ droppedElements }) => { 8 | return ( 9 | 10 | {(provided, snapshot) => ( 11 |
15 | {droppedElements?.length ? 16 | droppedElements?.map((item, index) => ( 17 | 21 | {(provided, snapshot) => ( 22 |
31 | {returnRespectiveHtmlElement(item.element)} 32 |
33 | )} 34 |
35 | )):

*Drag & Drop some component to start

} 36 |
37 | )} 38 |
39 | ) 40 | } 41 | 42 | export default DroppablePlace; -------------------------------------------------------------------------------- /src/components/renderPlace/index.js: -------------------------------------------------------------------------------- 1 | import React, { Suspense, useEffect, useState } from 'react'; 2 | import { DragDropContext } from 'react-beautiful-dnd'; 3 | import SideBarRender from '../sidebar'; 4 | import { reOrderWithInSameArea, reOrderWithOtherArea } from '../../helpers/reOrderFn'; 5 | import { generateElement } from '../../helpers/generateUiElements'; 6 | import './renderPlace.css' 7 | const DroppablePlace = React.lazy(() => import('../droppablePlace')); 8 | 9 | 10 | const initialElements = [ 11 | { id: 'button', element: "Button" }, 12 | { id: 'input', element: "Input Box" }, 13 | { id: 'textarea', element: "Textarea" }, 14 | { id: 'box', element: "Box" }, 15 | { id: 'heading', element: "Heading" } 16 | ] 17 | 18 | const RenderPlace = () => { 19 | const [sidebarElements, setSideBarElements] = useState(initialElements); 20 | const [droppedElements, setDroppedElements] = useState([]); 21 | 22 | useEffect(() => { 23 | const previousElements = JSON.parse(localStorage.getItem("movedElements")); 24 | if (previousElements && previousElements?.length) { 25 | setDroppedElements(previousElements); 26 | } 27 | }, []) 28 | 29 | const onDragEnd = result => { 30 | const { source, destination, draggableId: selectedElementType } = result; 31 | 32 | if (!destination) return; 33 | 34 | if (source.droppableId === destination.droppableId) { 35 | let reOrderedValue; 36 | if (source.droppableId === 'sidebar') { 37 | //handle drop inside sidebar 38 | reOrderedValue = reOrderWithInSameArea(sidebarElements, source.index, destination.index) 39 | setSideBarElements(reOrderedValue); 40 | } 41 | else { 42 | reOrderedValue = reOrderWithInSameArea(droppedElements, source.index, destination.index) 43 | setDroppedElements(reOrderedValue); 44 | } 45 | 46 | } else { 47 | //handle drop into droppable area 48 | const newHtmlElement = generateElement(selectedElementType, droppedElements.length); 49 | let valueAfterElementInsertion = reOrderWithOtherArea(droppedElements, destination.index, newHtmlElement); 50 | setDroppedElements(valueAfterElementInsertion); 51 | 52 | }; 53 | } 54 | 55 | const handleSave = () => { 56 | if(droppedElements?.length){ 57 | localStorage.setItem('movedElements', JSON.stringify(droppedElements)) 58 | alert('Elements Saved!') 59 | } 60 | 61 | } 62 | 63 | const handleClear = () => { 64 | setDroppedElements([]); 65 | localStorage.removeItem('movedElements') 66 | } 67 | 68 | return ( 69 | 70 |
71 | 72 | DroppablePlace loading...
}> 73 | 74 | 75 | 76 | 77 |
78 | 79 | 80 |
81 | 82 |
83 | ); 84 | } 85 | 86 | export default RenderPlace 87 | -------------------------------------------------------------------------------- /src/components/renderPlace/renderPlace.css: -------------------------------------------------------------------------------- 1 | .render-wrapper{ 2 | display: flex; 3 | padding: 20px; 4 | max-width: 62.5em; 5 | width: 100%; 6 | margin: 0 auto; 7 | height: 100vh; 8 | } 9 | 10 | .btn-wrapper{ 11 | position: absolute; 12 | top: 0; 13 | right: 0; 14 | margin: 35px; 15 | display:flex; 16 | flex-direction: column; 17 | } 18 | .save-btn{ 19 | margin-bottom: 20px; 20 | } 21 | 22 | .save-btn:hover, .clear-btn:hover{ 23 | cursor: pointer; 24 | box-shadow: 3px 3px 3px 3px #B2F5EA; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/components/sidebar/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Droppable, Draggable } from 'react-beautiful-dnd'; 3 | import { getSidebarListStyle, getSidebarItemStyle } from '../../helpers/styleFn.js'; 4 | import {ReactComponent as Menu} from '../../menu.svg'; 5 | import './sidebar.css'; 6 | 7 | const SideBarRender = ({ 8 | sidebarElements 9 | }) => { 10 | return ( 11 |
12 | 13 | {(provided, snapshot) => ( 14 |
17 | {sidebarElements?.map((item, index) => ( 18 | 22 | {(provided, snapshot) => ( 23 |
32 | 33 | {item.element} 34 |
35 | )} 36 |
37 | ))} 38 | {provided.placeholder} 39 |
40 | )} 41 |
42 |
43 | ) 44 | } 45 | 46 | export default SideBarRender; -------------------------------------------------------------------------------- /src/components/sidebar/sidebar.css: -------------------------------------------------------------------------------- 1 | .sidebar-wrapper{ 2 | background-color: #2E3648; 3 | } 4 | .sidebar-element{ 5 | transition: margin 200ms; 6 | margin-left: 1em; 7 | margin-top: 20px; 8 | margin-bottom: 20px; 9 | color: white; 10 | font-size: 20px; 11 | display: flex; 12 | align-items: center; 13 | } 14 | 15 | .sidebar-element:hover{ 16 | margin-left: -0.25em; 17 | margin-right: 0.25em; 18 | color:gray; 19 | background-color: #B2F5EA; 20 | box-shadow: 0 1px 3px 0 rgba(0,0,0,0.1), 0 1px 2px 0 rgba(0,0,0,0.06); 21 | cursor: move; 22 | } 23 | 24 | svg{ 25 | width: 20px; 26 | height: 15px; 27 | margin-right: 10px; 28 | opacity: 0.5; 29 | } -------------------------------------------------------------------------------- /src/helpers/generateUiElements.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const returnRespectiveHtmlElement = (type) => { 4 | switch (type) { 5 | case "button": 6 | return 7 | case "input": 8 | return 9 | case "textarea": 10 | return