├── .DS_Store ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .gitignore 2 ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── LoginForm.tsx ├── README.md ├── Schema.md ├── __tests__ └── AppTests.js ├── assets ├── favicon.ico └── formabull_logo_whitebg.png ├── client ├── components │ ├── AddTab.tsx │ ├── CSSTab.tsx │ ├── CardCreator.tsx │ ├── CodeTab.tsx │ ├── EditTab.tsx │ ├── LeftSideBar.tsx │ ├── MainCanvas.tsx │ ├── MenuDrawer.tsx │ ├── NavBar.tsx │ ├── RightSideBar.tsx │ ├── SelectTheme.tsx │ └── StyleTab.tsx ├── containers │ ├── App.tsx │ ├── Contact.tsx │ ├── Drafts.tsx │ ├── Help.tsx │ ├── Landing.tsx │ ├── Login.tsx │ ├── SavedForms.tsx │ ├── SignUp.tsx │ └── TakeTour.tsx └── utils │ └── items.js ├── index.html ├── main.ts ├── package.json ├── pull_request_template.md ├── routes └── Routes.tsx ├── src ├── ContextProvider.tsx └── index.tsx ├── style.scss ├── tsconfig.json ├── webpack.electron.config.js └── webpack.react.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/formaBull/88501887627ad53756eae238944e6ed75148717e/.DS_Store -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | 'plugin:react/recommended', 9 | 'airbnb', 10 | ], 11 | parser: '@typescript-eslint/parser', 12 | parserOptions: { 13 | ecmaFeatures: { 14 | jsx: true, 15 | }, 16 | ecmaVersion: 12, 17 | sourceType: 'module', 18 | }, 19 | plugins: [ 20 | 'react', 21 | '@typescript-eslint', 22 | ], 23 | rules: { 24 | 'linebreak-style': 'off', 25 | 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.tsx'] }], 26 | 'import/extensions': ['error', 'never', {}], 27 | 'prefer-destructuring': ['error', { object: false, array: false }], 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | package-lock.json 4 | build 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.gitignore 2: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | bundle.js -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.detectIndentation": false 4 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at formaBull@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | Hello, and thank you for your interest in contributing! At team formaBull, we love working on our projects, and we get just as excited when new volunteers show interest. 4 | 5 | ## Tests 6 | 7 | We write our tests with Jest and Enzyme, and ask that you also choose one of those two frameworks. 8 | 9 | ## Submitting Pull Requests 10 | 11 | Our [pull requests](https://github.com/oslabs-beta/formaBull/pulls) usually include the following block of markdown code: 12 | ``` markdown 13 | # Types of changes 14 | - [ ] New feature (adds functionality) 15 | - [ ] Refactor (changes the codebase without affecting its external behavior) 16 | - [ ] Functional Change (fix or feature that affects codebase) 17 | # Purpose 18 | - 19 | # Approach 20 | - 21 | 22 | 23 | Co-authored-by: Your Friend 24 | ``` 25 | Please try to minimize your pull requests to only check one box at a time. If you want to offer some refactoring, we ask that you do so separately from adding new features or significant modifications. Co-author credit isn't necessary, but the two empty lines between the final approach and Co-authored-by ***are*** necessary for proper GitHub credit. 26 | 27 | ## Coding Standards 28 | 29 | We follow the [Airbnb](https://github.com/airbnb/javascript) style guide for best practices. 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 formaBull 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LoginForm.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export interface Props { 4 | shouldRemember: boolean; 5 | onUsernameChange: (username: string) => void; 6 | onPasswordChange: (password: string) => void; 7 | onRememberChange: (remember: boolean) => void; 8 | onSubmit: (username: string, password: string) => void; 9 | } 10 | 11 | function LoginForm(props: Props) { 12 | const [username, setUsername] = React.useState(""); 13 | const [password, setPassword] = React.useState(""); 14 | const [remember, setRemember] = React.useState(props.shouldRemember); 15 | 16 | const handleUsernameChange = (e: React.ChangeEvent) => { 17 | const { value } = e.target; 18 | setUsername(value); 19 | props.onUsernameChange(value); 20 | }; 21 | 22 | const handlePasswordChange = (e: React.ChangeEvent) => { 23 | const { value } = e.target; 24 | setPassword(value); 25 | props.onPasswordChange(value); 26 | }; 27 | 28 | const handleRememberChange = (e: React.ChangeEvent) => { 29 | const { checked } = e.target; 30 | setRemember(checked); 31 | props.onRememberChange(checked); 32 | }; 33 | 34 | const handleSubmit = (e: React.FormEvent) => { 35 | e.preventDefault(); 36 | props.onSubmit(username, password); 37 | }; 38 | 39 | return ( 40 |
41 | 42 | 49 | 50 | 51 | 58 | 59 | 69 | 70 | 73 |
74 | ); 75 | } 76 | 77 | export default LoginForm; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

formaBull

2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 | 10 |

11 | 12 | ![formaBull logo](./assets/formabull_logo_whitebg.png) 13 | 14 |

Forms with React and Hooks simplified:
Click » Drag » Drop » Done.

15 | 16 |

Description

17 |

formaBull is an Electron application for React Developers who want to quickly generate forms. You can create the shape of your forms by dragging elements in, and style them in-app by adjusting sliders in the Style tab. Forms will be saved to your account, and you can switch among them as needed. 18 |
19 | As you build up your form on the main canvas, you'll see the

20 | 21 | ## Technologies Used 22 | React, Typescript, Electron, VS Code, Material-UI 23 | 24 | ## Directions 25 | To run this app, do the following: 26 | * Fork this repository, copy the code from your repo clone, open your command line in VS Code and navigate to the parent folder you want to install, 27 | * run `git clone https://github.com//formaBull.git`, 28 | * navigate into formaBull folder `cd formaBull`, 29 | * run `npm install`, 30 | * run `npm run build`, 31 | 32 | If you just want to use the app: 33 | * run `npm run pack`. 34 | 35 | If you want to run as a developer: 36 | * open two terminals 37 | * run `npm run dev:react` in one terminal, 38 | * and run `npm run dev:electron` in the other terminal. 39 | 40 | ### What Needs Work? 41 | Currently, we are serving a default form. Customization to come.
42 | Authentication is not required. Using the app takes you directly to the Landing page. 43 | 44 | ## Developers: 45 | * Courtney Doss [@catalyst-777](https://github.com/catalyst-777) 46 | * Mario Eldin [@Sector88](https://github.com/Sector88) 47 | * Max Nikitin [@nikitinmax2300](https://github.com/nikitinmax2300) 48 | * Windu Sayles [@windusayles](https://github.com/windusayles) 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Schema.md: -------------------------------------------------------------------------------- 1 | # FRONT END COMPONENT SCHEMA 2 | 3 | - App 4 | - Landing - MVP 5 | - NavBar 6 | - CreateFrom 7 | - SavedFormsBtn 8 | - Drafts 9 | - TakeATour 10 | - Settings 11 | - Help 12 | - LeftSideBar 13 | - Edit 14 | - Add 15 | - DefaultForm(Drag&Drop) - MVP 16 | - Input 17 | - Button 18 | - Checkbox 19 | - Dropdown 20 | - Email 21 | - ... 22 | - Style 23 | - MainCanvas 24 | - RightSideBar 25 | - Code 26 | - CodeCanvas 27 | - CustomCSSTab 28 | - ExpandCollapseTab 29 | - ExportBtn 30 | - SaveBtn 31 | - SavedForms 32 | - Login 33 | - SignUp 34 | 35 | -------------------------------------------------------------------------------- /__tests__/AppTests.js: -------------------------------------------------------------------------------- 1 | const Application = require("spectron").Application; 2 | const electronPath = require("electron"); 3 | const path = require("path"); 4 | 5 | let app; 6 | 7 | beforeAll(() => { 8 | app = new Application({ 9 | path: electronPath, 10 | 11 | args: [path.join(__dirname, "..")] 12 | }); 13 | return app.start(); 14 | }, 50000); 15 | 16 | 17 | afterAll(function () { 18 | if (app && app.isRunning()) { 19 | // console.log(app) 20 | return app.stop(); 21 | } 22 | }); 23 | 24 | test("Displays App window", async function () { 25 | let windowCount = await app.client.getWindowCount(); 26 | 27 | expect(windowCount).toBe(2); 28 | }); 29 | 30 | test("should launch app", async () => { 31 | const devTitle = await app.client.getTitle(); 32 | console.log(app) 33 | expect(devTitle).toBe('DevTools'); 34 | }); -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/formaBull/88501887627ad53756eae238944e6ed75148717e/assets/favicon.ico -------------------------------------------------------------------------------- /assets/formabull_logo_whitebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/formaBull/88501887627ad53756eae238944e6ed75148717e/assets/formabull_logo_whitebg.png -------------------------------------------------------------------------------- /client/components/AddTab.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { AppContext } from '../../src/'; 3 | import { CardCreator } from '../components/CardCreator'; 4 | 5 | 6 | export const AddTab = (props:any) => { 7 | type ArrayOfDraggables = Array<{ 8 | id: number; 9 | status: string; 10 | title: string; 11 | data: string; 12 | }>; 13 | interface DraggableElementsArray { 14 | }; 15 | 16 | const { listOfDraggableElements }: ArrayOfDraggables = useContext(AppContext); 17 | 18 | return ( 19 |
20 | {listOfDraggableElements 21 | .map((draggableElement: any, i: any) => ( 22 | 29 | )) 30 | } 31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /client/components/CSSTab.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from 'react'; 2 | import { makeStyles, withStyles, Theme, createStyles } from '@material-ui/core/styles'; 3 | import { Tabs, Tab, Box, Typography } from '@material-ui/core'; 4 | import { CopyBlock, dracula, nord, monokai, irBlack, a11yDark, a11yLight, anOldHope, androidstudio, arta, atomOneDark, github, monoBlue, obsidian, ocean, rainbow } from 'react-code-blocks'; 5 | import { Resizable } from 're-resizable'; 6 | import { AppContext } from '../../src/'; 7 | import { SelectTheme } from './SelectTheme'; 8 | 9 | const defaultCSS = `html { 10 | height: 100%; 11 | } 12 | 13 | body { 14 | margin:0; 15 | padding:0; 16 | font-family: sans-serif; 17 | background: linear-gradient(#141e30, #243b55); 18 | color: white 19 | } 20 | 21 | form { 22 | position: absolute; 23 | top: 50%; 24 | left: 50%; 25 | width: 400px; 26 | padding: 40px; 27 | transform: translate(-50%, -50%); 28 | background: rgba(0,0,0,.5); 29 | box-sizing: border-box; 30 | box-shadow: 0 15px 25px rgba(0,0,0,.6); 31 | border-radius: 10px; 32 | display: flex; 33 | flex-direction: column; 34 | align-items: center; 35 | } 36 | 37 | input { 38 | width: 100%; 39 | padding: 10px 0; 40 | font-size: 16px; 41 | color: #fff; 42 | margin-bottom: 20px; 43 | border: none; 44 | border-bottom: 1px solid #fff; 45 | outline: none; 46 | background: transparent; 47 | } 48 | 49 | input[type="submit"] { 50 | background: #ec5990; 51 | color: white; 52 | text-transform: uppercase; 53 | border: none; 54 | margin-top: 20px; 55 | padding: 20px; 56 | font-size: 16px; 57 | font-weight: 100; 58 | letter-spacing: 10px; 59 | display: block; 60 | appearance: none; 61 | border-radius: 4px; 62 | width: 100%; 63 | font-weight: lighter; 64 | } 65 | input[type="submit"] { 66 | padding: 5px; 67 | background: #516391; 68 | color: white; 69 | letter-spacing: 0px; 70 | text-transform: none; 71 | padding: 10px; 72 | letter-spacing: 2px; 73 | } 74 | 75 | input[type="submit"]:hover { 76 | background: #ec5990; 77 | color: white; 78 | } 79 | `; 80 | 81 | export const CSSTab = (props:any) => { 82 | const {theme, setTheme }:any = useContext(AppContext); 83 | 84 | const galleryOfThemes:any = {'dracula': dracula, 'monokai': monokai, 'irBlack': irBlack, 'nord': nord, 'a11yDark': a11yDark, 'a11yLight': a11yLight, 'anOldHope': anOldHope, 'androidstudio': androidstudio, 'arta': arta, 'atomOneDark': atomOneDark, 'github': github, 'monoBlue': monoBlue, 'obsidian': obsidian, 'ocean': ocean, 'rainbow': rainbow }; 85 | 86 | const selectedTheme = galleryOfThemes[theme]; 87 | 88 | return ( 89 |
90 | 91 | 97 |
98 | 110 |
111 |
112 |
113 | ); 114 | }; 115 | -------------------------------------------------------------------------------- /client/components/CardCreator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDrag } from 'react-dnd'; 3 | import { makeStyles, Theme, Box, Typography } from '@material-ui/core'; 4 | import { ItemTypes } from '../utils/items'; 5 | import DragIndicatorIcon from '@material-ui/icons/DragIndicator'; 6 | 7 | 8 | const useStyles = makeStyles((theme: Theme) => ({ 9 | root: { 10 | flexGrow: 1, 11 | padding: theme.spacing(3), 12 | backgroundColor: '#202020', 13 | width: '300px', 14 | margin: 'auto', 15 | height: '8px', 16 | marginBottom: '15px', 17 | marginTop: '8px', 18 | borderStyle: 'solid', 19 | borderWidth: '1.5px', 20 | borderColor: '#F94EB4', 21 | borderRadius: '3px', 22 | display: 'flex', 23 | }, 24 | dragIndicator: { 25 | padding: '0px', 26 | fontSize: '35px', 27 | position: 'relative', 28 | bottom: '13px', 29 | display: 'inline-block' 30 | }, 31 | title: { 32 | padding: '0px', 33 | fontSize: '18px', 34 | position: 'relative', 35 | bottom: '10px', 36 | marginLeft: 'auto', 37 | color: 'white', 38 | display: 'inline-block' 39 | } 40 | })); 41 | 42 | interface CardCreatorProps { 43 | id: number; 44 | category: string; 45 | title: string; 46 | }; 47 | 48 | export const CardCreator = (props: CardCreatorProps) => { 49 | const classes = useStyles(); 50 | 51 | const[{ isDragging }, drag] = useDrag({ 52 | item: { 53 | type: ItemTypes.CARD, 54 | id: props.id, 55 | }, 56 | end: (item, monitor) => { 57 | const dropResult = monitor.getDropResult(); 58 | if (item && dropResult) { 59 | } 60 | }, 61 | collect: monitor => ({ 62 | isDragging: !!monitor.isDragging(), 63 | }) 64 | }); 65 | 66 | return ( 67 |
68 | 69 | 70 | {props.title} 71 | 72 |
73 | ) 74 | }; 75 | -------------------------------------------------------------------------------- /client/components/CodeTab.tsx: -------------------------------------------------------------------------------- 1 | import React, {useContext, useState} from 'react'; 2 | import { CopyBlock, dracula, nord, monokai, irBlack, a11yDark, a11yLight, anOldHope, androidstudio, arta, atomOneDark, github, monoBlue, obsidian, ocean, rainbow } from 'react-code-blocks'; 3 | import { Resizable } from 're-resizable'; 4 | import { AppContext } from '../../src/'; 5 | import { SelectTheme } from './SelectTheme'; 6 | 7 | 8 | export const CodeTab = () => { 9 | const { listOfDroppedElements }: any = useContext(AppContext); 10 | 11 | const {theme, setTheme }:any = useContext(AppContext); 12 | 13 | const galleryOfThemes: any = {'dracula': dracula, 'monokai': monokai, 'irBlack': irBlack, 'nord': nord, 'a11yDark': a11yDark, 'a11yLight': a11yLight, 'anOldHope': anOldHope, 'androidstudio': androidstudio, 'arta': arta, 'atomOneDark': atomOneDark, 'github': github, 'monoBlue': monoBlue, 'obsidian': obsidian, 'ocean': ocean, 'rainbow': rainbow }; 14 | 15 | const selectedTheme: object = galleryOfThemes[theme]; 16 | 17 | const injectFunc = (listofDraggableElements: any) => { 18 | const result: string[] = []; 19 | const linkedListCode = listOfDroppedElements[0]; 20 | let cur = linkedListCode.head; 21 | while (cur) { 22 | result.push(cur.val.data); 23 | cur = cur.next; 24 | }; 25 | let cleanedUpResults = ``; 26 | for (const each of result) cleanedUpResults += `\n\t` + each; 27 | return cleanedUpResults; 28 | }; 29 | 30 | const parse = `import React from "react"; 31 | import { useForm } from "react-hook-form"; 32 | 33 | 34 | export default function App() { 35 | const { register, handleSubmit, watch, errors } = useForm(); 36 | const onSubmit = data => console.log(data); 37 | 38 | return ( 39 |
${injectFunc(listOfDroppedElements)} 40 |
41 | ); 42 | };`; 43 | 44 | return ( 45 |
46 | 47 | 53 |
54 | 66 |
67 |
68 |
69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /client/components/EditTab.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { Box } from '@material-ui/core'; 3 | import { AppContext } from '../../src/'; 4 | import { CardCreator } from '../components/CardCreator'; 5 | 6 | 7 | export const EditTab = (props:any) => { 8 | 9 | return ( 10 |
11 | EDIT TAB 12 |
13 | ) 14 | }; -------------------------------------------------------------------------------- /client/components/LeftSideBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles, withStyles, Theme, createStyles } from '@material-ui/core/styles'; 3 | import { Tabs, Tab, Box, Typography, Container } from '@material-ui/core'; 4 | import { AddTab } from '../components/AddTab'; 5 | import { EditTab } from '../components/EditTab'; 6 | import { StyleTab } from '../components/StyleTab'; 7 | 8 | 9 | interface StyledTabsProps { 10 | value: number; 11 | onChange: (event: React.ChangeEvent<{}>, newValue: number) => void; 12 | }; 13 | 14 | // Use withStyles for styling class-based components 15 | const StyledTabs = withStyles({ 16 | indicator: { 17 | display: 'flex', 18 | justifyContent: 'center', 19 | backgroundColor: 'transparent', 20 | '& > span': { 21 | maxWidth: 40, 22 | width: '100%', 23 | backgroundColor: '#000000', 24 | }, 25 | }, 26 | })((props: StyledTabsProps) => }} />); 27 | 28 | interface StyledTabProps { 29 | label: string; 30 | index: number; 31 | }; 32 | 33 | // Use withStyles for styling class-based components 34 | const StyledTab = withStyles((theme: Theme) => 35 | createStyles({ 36 | root: { 37 | textTransform: 'none', 38 | color: '#fff', 39 | fontWeight: theme.typography.fontWeightRegular, 40 | fontSize: theme.typography.pxToRem(17), 41 | marginRight: theme.spacing(-3), 42 | marginLeft: theme.spacing(-3), 43 | '&:focus': { 44 | opacity: 1, 45 | }, 46 | }, 47 | }), 48 | )((props: StyledTabProps) => ); 49 | 50 | const useStyles = makeStyles((theme: Theme) => ({ 51 | root: { 52 | flexGrow: 0.03, 53 | }, 54 | padding: { 55 | padding: theme.spacing(1), 56 | }, 57 | containerPadding: { 58 | padding: '3px' 59 | }, 60 | backgroundColor: { 61 | backgroundColor: '#F94EB4', 62 | }, 63 | })); 64 | 65 | export const LeftSideBar = (props:any) => { 66 | const classes = useStyles(); 67 | 68 | const [value, setValue] = React.useState(0); 69 | 70 | const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { 71 | setValue(newValue); 72 | }; 73 | 74 | interface TabPanelProps { 75 | children?: React.ReactNode; 76 | index: any; 77 | value: any; 78 | } 79 | 80 | function TabPanel(props: TabPanelProps) { 81 | const { children, value, index, ...other } = props; 82 | 83 | return ( 84 | 99 | ); 100 | }; 101 | 102 | return ( 103 |
104 |
105 | 106 | 107 | 108 | 109 | 110 | 111 |
112 |
113 | 114 | 115 | 116 |
117 |
118 | 119 | 120 | 121 |
122 |
123 | 124 | 125 | 126 |
127 |
128 | ); 129 | }; 130 | -------------------------------------------------------------------------------- /client/components/MainCanvas.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { useDrop } from 'react-dnd'; 3 | import { createStyles, Theme, makeStyles } from '@material-ui/core/styles'; 4 | import { ItemTypes } from '../utils/items'; 5 | import { CardCreator } from './CardCreator'; 6 | import { AppContext } from '../../src/'; 7 | 8 | 9 | const useStyles = makeStyles((theme: Theme) => 10 | createStyles({ 11 | root: { 12 | flexGrow: 1, 13 | boxShadow: '0 3px 5px 7px rgba(255, 105, 135, .3)', 14 | background: '#fff', 15 | border: 0, 16 | borderRadius: 3, 17 | color: 'black', 18 | height: '89%', 19 | width: 'auto', 20 | marginLeft: '50px', 21 | marginRight: '50px', 22 | padding: '0 30px', 23 | textAlign: 'center', 24 | overflowY: 'scroll', 25 | minWidth: 400 26 | }, 27 | }) 28 | ); 29 | 30 | export const MainCanvas = (props:any) => { 31 | const classes = useStyles(); 32 | 33 | const { elementDropped }: any = useContext(AppContext); 34 | 35 | const { listOfDroppedElements }: any = useContext(AppContext); 36 | 37 | const [{ isOver, canDrop }, drop] = useDrop({ 38 | accept: ItemTypes.CARD, 39 | drop: (item: any, monitor) => { 40 | 41 | elementDropped(item.id) 42 | }, 43 | collect: (monitor) => ({ 44 | isOver: !!monitor.isOver(), 45 | canDrop: !!monitor.canDrop(), 46 | }) 47 | }); 48 | 49 | const isActive = canDrop && isOver; 50 | 51 | const droppedLinkedList = () => { 52 | const createDroppedItem = (draggableElement: any, i: any) => ( 53 |
54 | 59 |
60 | ); 61 | let cur = listOfDroppedElements[0].head; 62 | const arrayOfReactLL = []; 63 | if (!cur) return 'Drop Here!'; 64 | while (cur) { 65 | arrayOfReactLL.push(createDroppedItem(cur.val, cur.val.id)); 66 | cur = cur.next; 67 | }; 68 | return arrayOfReactLL; 69 | }; 70 | 71 | return ( 72 |
76 | {/* TODO this console log pops up a lot, does this mean all of maincanvas is being re-rendered every time I see this log? */} 77 | {console.log('list of dropped: ', listOfDroppedElements[0].head)} 78 | {droppedLinkedList()} 79 |
80 | ) 81 | }; 82 | -------------------------------------------------------------------------------- /client/components/MenuDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; 4 | import { Drawer, List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core/'; 5 | import CodeIcon from '@material-ui/icons/Code'; 6 | import BookmarkIcon from '@material-ui/icons/Bookmark'; 7 | import MenuIcon from '@material-ui/icons/Menu'; 8 | import IconButton from '@material-ui/icons/Menu'; 9 | import{ navLinks } from '../../routes/Routes' 10 | 11 | 12 | const useStyles = makeStyles((theme: Theme) => createStyles({ 13 | root: { 14 | flexGrow: 1, 15 | }, 16 | list: { 17 | width: 350, 18 | }, 19 | fullList: { 20 | width: 'auto', 21 | }, 22 | menuButton: { 23 | marginRight: theme.spacing(2), 24 | }, 25 | })); 26 | 27 | type Anchor = 'left'; 28 | 29 | export const MenuDrawer = (props:any) => { 30 | const classes = useStyles(); 31 | const [state, setState] = React.useState({ 32 | left: false, 33 | }); 34 | 35 | const toggleDrawer = (anchor: Anchor, open: boolean) => ( 36 | event: React.KeyboardEvent | React.MouseEvent, 37 | ) => { 38 | if ( 39 | event.type === 'keydown' && 40 | ((event as React.KeyboardEvent).key === 'Tab' || 41 | (event as React.KeyboardEvent).key === 'Shift') 42 | ) { 43 | return; 44 | } 45 | setState({ ...state, [anchor]: open }); 46 | }; 47 | 48 | const list = (anchor: Anchor) => ( 49 |
55 | 56 | {navLinks.map(({ title, path }, index) => ( 57 | 58 | 59 | {index % 2 === 0 ? : } 60 | 61 | 62 | 63 | ))} 64 | 65 |
66 | ); 67 | 68 | return ( 69 |
70 | {(['left'] as Anchor[]).map((anchor) => ( 71 | 72 | 78 | {anchor} 79 | 80 | 81 | 82 | {list(anchor)} 83 | 84 | 85 | ))} 86 |
87 | ); 88 | }; 89 | -------------------------------------------------------------------------------- /client/components/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { AppBar, Toolbar, Typography, InputBase } from '@material-ui/core/'; 3 | import { createStyles, fade, Theme, makeStyles } from '@material-ui/core/styles'; 4 | import SearchIcon from '@material-ui/icons/Search'; 5 | import { MenuDrawer } from './MenuDrawer' 6 | 7 | 8 | const useStyles = makeStyles((theme: Theme) => 9 | createStyles({ 10 | root: { 11 | flexGrow: 1, 12 | }, 13 | backgroundColor: { 14 | backgroundColor: '#F94EB4', 15 | }, 16 | menuButton: { 17 | marginRight: theme.spacing(2), 18 | }, 19 | list: { 20 | width: 250, 21 | }, 22 | fullList: { 23 | width: 'auto', 24 | }, 25 | title: { 26 | flexGrow: 1, 27 | display: 'none', 28 | [theme.breakpoints.up('sm')]: { 29 | display: 'block', 30 | }, 31 | }, 32 | search: { 33 | position: 'relative', 34 | borderRadius: theme.shape.borderRadius, 35 | backgroundColor: fade(theme.palette.common.white, 0.15), 36 | '&:hover': { 37 | backgroundColor: fade(theme.palette.common.white, 0.25), 38 | }, 39 | marginLeft: 0, 40 | width: '100%', 41 | [theme.breakpoints.up('sm')]: { 42 | marginLeft: theme.spacing(1), 43 | width: 'auto', 44 | }, 45 | }, 46 | searchIcon: { 47 | padding: theme.spacing(0, 2), 48 | height: '100%', 49 | position: 'absolute', 50 | pointerEvents: 'none', 51 | display: 'flex', 52 | alignItems: 'center', 53 | justifyContent: 'center', 54 | }, 55 | inputRoot: { 56 | color: 'inherit', 57 | }, 58 | inputInput: { 59 | padding: theme.spacing(1, 1, 1, 0), 60 | // vertical padding + font size from searchIcon 61 | paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, 62 | transition: theme.transitions.create('width'), 63 | width: '100%', 64 | [theme.breakpoints.up('sm')]: { 65 | width: '12ch', 66 | '&:focus': { 67 | width: '20ch', 68 | }, 69 | }, 70 | }, 71 | }), 72 | ); 73 | 74 | export const NavBar = (props:any) => { 75 | const classes = useStyles(); 76 | 77 | return ( 78 |
79 | 80 | 81 | 82 | 83 | formaBull 84 | 85 |
86 |
87 | 88 |
89 | 97 |
98 |
99 |
100 |
101 | ); 102 | }; 103 | -------------------------------------------------------------------------------- /client/components/RightSideBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles, withStyles, Theme, createStyles } from '@material-ui/core/styles'; 3 | import { Tabs, Tab, Typography, Box, Container } from '@material-ui/core/'; 4 | import { CodeTab } from './CodeTab'; 5 | import { CSSTab } from './CSSTab'; 6 | 7 | 8 | interface StyledTabsProps { 9 | value: number; 10 | onChange: (event: React.ChangeEvent<{}>, newValue: number) => void; 11 | }; 12 | 13 | // Use withStyles for styling class-based components 14 | const StyledTabs = withStyles({ 15 | indicator: { 16 | display: 'flex', 17 | position: 'absolute', 18 | justifyContent: 'center', 19 | backgroundColor: 'transparent', 20 | '& > span': { 21 | maxWidth: 40, 22 | width: '100%', 23 | backgroundColor: '#000000', 24 | }, 25 | }, 26 | })((props: StyledTabsProps) => }} />); 27 | 28 | interface StyledTabProps { 29 | label: string; 30 | index: number; 31 | }; 32 | 33 | // Use withStyles for styling class-based components 34 | const StyledTab = withStyles((theme: Theme) => 35 | createStyles({ 36 | root: { 37 | flexGrow: 1, 38 | textTransform: 'none', 39 | color: '#fff', 40 | fontWeight: theme.typography.fontWeightRegular, 41 | fontSize: theme.typography.pxToRem(17), 42 | marginRight: theme.spacing(1), 43 | marginLeft: theme.spacing(2), 44 | '&:focus': { 45 | opacity: 1, 46 | }, 47 | }, 48 | }), 49 | )((props: StyledTabProps) => ); 50 | 51 | // makeStyle/useStyle is used at function components 52 | const useStyles = makeStyles((theme: Theme) => ({ 53 | root: { 54 | flexGrow: 1, 55 | border: '10px', 56 | }, 57 | padding: { 58 | padding: theme.spacing(1), 59 | }, 60 | backgroundColor: { 61 | backgroundColor: '#F94EB4', 62 | }, 63 | })); 64 | 65 | 66 | export const RightSideBar = (props:any) => { 67 | const classes = useStyles(); 68 | 69 | const [value, setValue] = React.useState(0); 70 | 71 | const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { 72 | setValue(newValue); 73 | }; 74 | 75 | interface TabPanelProps { 76 | children?: React.ReactNode; 77 | index: any; 78 | value: any; 79 | }; 80 | 81 | function TabPanel(props: TabPanelProps) { 82 | const { children, value, index, ...other } = props; 83 | return ( 84 | 97 | ); 98 | }; 99 | 100 | return ( 101 |
102 |
103 |
104 | 105 | 106 | 107 | 108 | 109 |
110 |
111 | 112 | 113 | 114 |
115 | 116 | 117 | 118 |
119 |
120 | ); 121 | }; -------------------------------------------------------------------------------- /client/components/SelectTheme.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; 3 | import { dracula, nord, monokai, irBlack, a11yDark, a11yLight, anOldHope, androidstudio, arta, atomOneDark, github, monoBlue, obsidian, ocean, rainbow } from 'react-code-blocks'; 4 | import InputLabel from '@material-ui/core/InputLabel'; 5 | import MenuItem from '@material-ui/core/MenuItem'; 6 | import FormControl from '@material-ui/core/FormControl'; 7 | import Select from '@material-ui/core/Select'; 8 | import { AppContext } from '../../src/'; 9 | 10 | 11 | const useStyles = makeStyles((theme: Theme) => 12 | createStyles({ 13 | formControl: { 14 | margin: theme.spacing(0), 15 | minWidth: 370, 16 | display: 'flex', 17 | }, 18 | selectEmpty: { 19 | marginTop: theme.spacing(6), 20 | }, 21 | }), 22 | ); 23 | 24 | const galleryOfThemes = {'dracula': dracula, 'monokai': monokai, 'irBlack': irBlack, 'nord': nord, 'a11yDark': a11yDark, 'a11yLight': a11yLight, 'anOldHope': anOldHope, 'androidstudio': androidstudio, 'arta': arta, 'atomOneDark': atomOneDark, 'github': github, 'monoBlue': monoBlue, 'obsidian': obsidian, 'ocean': ocean, 'rainbow': rainbow }; 25 | 26 | export const SelectTheme = () => { 27 | const classes = useStyles(); 28 | const { theme, setTheme }: any = useContext(AppContext) 29 | 30 | const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { 31 | setTheme(event.target.value as string); 32 | }; 33 | 34 | return ( 35 |
36 | 37 | Select theme 38 | 62 | 63 |
64 | ) 65 | }; 66 | -------------------------------------------------------------------------------- /client/components/StyleTab.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { Box } from '@material-ui/core'; 3 | import { AppContext } from '../../src/'; 4 | import { CardCreator } from '../components/CardCreator'; 5 | 6 | 7 | export const StyleTab = (props:any) => { 8 | return ( 9 |
10 | STYLE TAB 11 |
12 | ) 13 | }; -------------------------------------------------------------------------------- /client/containers/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter, Switch, Route } from 'react-router-dom'; 3 | import Landing from './Landing'; 4 | import { NavBar } from '../components/NavBar'; 5 | import SavedForms from './SavedForms'; 6 | import Drafts from './Drafts'; 7 | import Contact from './Contact'; 8 | import Help from './Help'; 9 | import TakeTour from './TakeTour'; 10 | import '../../style.scss'; 11 | 12 | 13 | export default function App(props: any) { 14 | return ( 15 |
16 |
17 | 18 |
19 | 20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 |
45 |
46 | ) 47 | }; 48 | -------------------------------------------------------------------------------- /client/containers/Contact.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export default function Contact () { 5 | return( 6 |
7 |

Contact

8 |
9 | ) 10 | }; -------------------------------------------------------------------------------- /client/containers/Drafts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export default function Drafts () { 5 | return( 6 |
7 |

Drafts

8 |
9 | ) 10 | }; -------------------------------------------------------------------------------- /client/containers/Help.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export default function Help () { 5 | return( 6 |
7 |

Help

8 |
9 | ) 10 | }; -------------------------------------------------------------------------------- /client/containers/Landing.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { LeftSideBar } from '../components/LeftSideBar'; 3 | import { RightSideBar } from '../components/RightSideBar'; 4 | import { MainCanvas } from '../components/MainCanvas'; 5 | 6 | 7 | export default function Landing (props: any) { 8 | return ( 9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 | ) 17 | }; -------------------------------------------------------------------------------- /client/containers/Login.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/formaBull/88501887627ad53756eae238944e6ed75148717e/client/containers/Login.tsx -------------------------------------------------------------------------------- /client/containers/SavedForms.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export default function SavedForms () { 5 | return( 6 |
7 |

Saved Forms

8 |
9 | ) 10 | }; -------------------------------------------------------------------------------- /client/containers/SignUp.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/formaBull/88501887627ad53756eae238944e6ed75148717e/client/containers/SignUp.tsx -------------------------------------------------------------------------------- /client/containers/TakeTour.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export default function TakeTour () { 5 | return( 6 |
7 |

Take a Tour

8 |
9 | ) 10 | }; -------------------------------------------------------------------------------- /client/utils/items.js: -------------------------------------------------------------------------------- 1 | export const ItemTypes = { 2 | CARD: "card," 3 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | formaBull | Hook 'em by the Horns 5 | 6 | 7 | 8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow } = require('electron'); 2 | import * as path from "path"; 3 | import * as url from "url"; 4 | 5 | app.on('ready', () => { 6 | const window = new BrowserWindow({ 7 | width: 1024, 8 | height: 768, 9 | webPreferences: { 10 | nodeIntegration: true, 11 | contextIsolation: false, 12 | webSecurity: false, 13 | } 14 | }); 15 | 16 | window.setMenuBarVisibility(false); 17 | 18 | window.loadFile('index.html'); 19 | window.loadURL(`http://localhost:4000`); 20 | window.webContents.openDevTools(); 21 | }); 22 | 23 | // Quit when all windows are closed, except on macOS. There, it's common 24 | // for applications and their menu bar to stay active until the user quits 25 | // explicitly with Cmd + Q. 26 | app.on('window-all-closed', () => { 27 | if (process.platform !== 'darwin') { 28 | app.quit(); 29 | } 30 | }); 31 | 32 | // app.on('activate', () => { 33 | // // On OS X it's common to re-create a window in the app when the 34 | // // dock icon is clicked and there are no other windows open. 35 | // if (BrowserWindow.getAllWindows().length === 0) { 36 | // app.on('ready', () => { 37 | // // once electron has started up, create a window. 38 | // const window = new BrowserWindow({ 39 | // width: 1024, 40 | // height: 768, 41 | // webPreferences: { 42 | // nodeIntegration: true, 43 | // contextIsolation: false,//THIS HAS CONFLICT WITH TYPESCRIPT 44 | // webSecurity: false, 45 | // } 46 | // }); 47 | 48 | // // hide the default menu bar that comes with the browser window 49 | // window.setMenuBarVisibility(false); //NOTE changed from null to false 50 | 51 | // // load a website to display 52 | // window.loadFile('index.html'); //This is just for production, not used for development 53 | // window.loadURL(`http://localhost:4000`); // development 54 | // // if(process.env.NODE_env === 'production') 55 | // window.webContents.openDevTools(); 56 | // }); 57 | // } 58 | // }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "production", 3 | "version": "1.0.0", 4 | "description": "Forms with React and Hooks simplified. Click » Drag » Drop » Done", 5 | "main": "dist/main.js", 6 | "scripts": { 7 | "dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .", 8 | "dev:react": "NODE_ENV=development webpack serve --config webpack.react.config.js --mode development", 9 | "build:electron": "NODE_ENV=production webpack --config webpack.electron.config.js --mode production", 10 | "build:react": "NODE_ENV=production webpack --config webpack.react.config.js --mode production", 11 | "build": "npm run build:electron && npm run build:react", 12 | "pack": "electron-builder --dir", 13 | "dist": "electron-builder", 14 | "test": "jest", 15 | "postinstall": "electron-builder install-app-deps", 16 | "dist:mac": "npm run build:react && electron-builder --mac", 17 | "dist:win": "npm run build:react && electron-builder --windows", 18 | "dist:linux": "npm run build:react && electron-builder --linux" 19 | }, 20 | "author": "", 21 | "license": "MIT", 22 | "build": { 23 | "files": [ 24 | "dist/", 25 | "node_modules/", 26 | "package.json" 27 | ], 28 | "productName": "formaBull", 29 | "appId": "com.electron.formaBull", 30 | "directories": { 31 | "output": "build" 32 | }, 33 | "mac": { 34 | "category": "public.app-category.developer-tools" 35 | }, 36 | "win": { 37 | "target": "NSIS" 38 | }, 39 | "linux": { 40 | "target": [ 41 | "AppImage", 42 | "deb" 43 | ] 44 | } 45 | }, 46 | "dependencies": { 47 | "@material-ui/core": "^4.11.2", 48 | "@material-ui/icons": "^4.11.2", 49 | "@types/react": "^17.0.0", 50 | "@types/react-dom": "^17.0.0", 51 | "fix-path": "^3.0.0", 52 | "fontsource-roboto": "^4.0.0", 53 | "jsx-to-string": "^1.4.0", 54 | "pretty-format": "^26.6.2", 55 | "re-resizable": "^6.9.0", 56 | "react": "^17.0.1", 57 | "react-code-blocks": "0.0.8", 58 | "react-dnd": "^11.1.3", 59 | "react-dnd-html5-backend": "^11.1.3", 60 | "react-dom": "^17.0.1", 61 | "react-element-to-jsx-string": "^14.3.2", 62 | "react-hook-form": "^6.14.2", 63 | "react-router-dom": "^5.2.0", 64 | "react-scroll-to-bottom": "^4.1.0", 65 | "react-stringify": "^1.0.0", 66 | "uninstall": "0.0.0" 67 | }, 68 | "devDependencies": { 69 | "@babel/core": "^7.12.10", 70 | "@babel/plugin-syntax-jsx": "^7.12.13", 71 | "@babel/preset-env": "^7.12.11", 72 | "@babel/preset-react": "^7.12.10", 73 | "@babel/preset-typescript": "^7.12.7", 74 | "@types/electron-devtools-installer": "^2.2.0", 75 | "@types/jest": "^26.0.21", 76 | "@types/react-router-dom": "^5.1.7", 77 | "@typescript-eslint/eslint-plugin": "^4.14.2", 78 | "@typescript-eslint/parser": "^4.14.2", 79 | "babel-loader": "^8.2.2", 80 | "css-loader": "^5.0.1", 81 | "electron": "^12.0.1", 82 | "electron-builder": "^22.9.1", 83 | "electron-chromedriver": "^11.0.0", 84 | "electron-devtools-installer": "^3.1.1", 85 | "electron-react-devtools": "^0.5.3", 86 | "eslint": "^7.19.0", 87 | "eslint-config-airbnb": "^18.2.1", 88 | "eslint-plugin-import": "^2.22.1", 89 | "eslint-plugin-jsx-a11y": "^6.4.1", 90 | "eslint-plugin-react": "^7.22.0", 91 | "eslint-plugin-react-hooks": "^4.2.0", 92 | "html-webpack-plugin": "^4.5.1", 93 | "jest": "^26.6.3", 94 | "sass": "^1.32.4", 95 | "sass-loader": "^10.1.1", 96 | "spectron": "^14.0.0", 97 | "style-loader": "^2.0.0", 98 | "typescript": "^4.1.3", 99 | "webpack": "^4.42.1", 100 | "webpack-cli": "^4.3.1", 101 | "webpack-dev-server": "^3.11.2" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Types of changes 2 | - [ ] New feature (adds functionality) 3 | - [ ] Refactor (changes the codebase without affecting its external behavior) 4 | - [ ] Functional Change (fix or feature that affects codebase) 5 | # Purpose 6 | - 7 | # Approach 8 | - 9 | 10 | 11 | Co-authored-by: Your Friend 12 | -------------------------------------------------------------------------------- /routes/Routes.tsx: -------------------------------------------------------------------------------- 1 | export const navLinks = [ 2 | { title: 'Create Form', path: '/' }, 3 | { title: 'Saved Forms', path: '/savedforms' }, 4 | { title: 'Drafts', path: '/drafts' }, 5 | { title: 'Contact', path: '/contact' }, 6 | { title: 'Help', path: '/help' }, 7 | { title: 'Take a Tour', path: '/take-a-tour' } 8 | ]; 9 | -------------------------------------------------------------------------------- /src/ContextProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { AppContext } from './index'; 3 | 4 | export const ContextProvider = ({ children }: React) => { 5 | console.log('context provider children:', children) 6 | const elementDropped = (id: any) => { 7 | //check for existing elements to avoid duplicating 8 | let cur = listOfDroppedElements[0].head; 9 | while (cur) { 10 | if (cur.val.id === id) return; 11 | cur = cur.next; 12 | }; 13 | //function to deeply clone an object 14 | const deepCopyFunction = (inObject: any): any => { 15 | if (typeof inObject !== "object" || inObject === null) { 16 | return inObject; 17 | }; 18 | const outObject: object = Array.isArray(inObject) ? [] : {}; 19 | for (const property in inObject) { 20 | let value = inObject[property]; 21 | outObject[property] = deepCopyFunction(value); 22 | }; 23 | return outObject; 24 | }; 25 | 26 | //iterate the draggable elements until we find the item we want, then set that onto list of dropped elements 27 | for (let i = 0; i < listOfDraggableElements.length; i++) { 28 | if (listOfDraggableElements[i].id === id) { 29 | const elementToInsert = deepCopyFunction(listOfDraggableElements.slice(i, i+1)); 30 | console.log('element to insert', elementToInsert) 31 | elementToInsert[0].status = "dropped"; 32 | listOfDroppedElements[0].add(elementToInsert[0]); 33 | setListOfDroppedElements((listOfDroppedElements: any): any => { 34 | return [...listOfDroppedElements]; 35 | }); 36 | } 37 | }; 38 | }; 39 | 40 | // class/function to create linked list on main 41 | class ComponentLinkedList { 42 | head: any; 43 | tail: any; 44 | add: any; 45 | remove: any; 46 | constructor() { 47 | this.head = null; 48 | this.tail = null; 49 | }; 50 | }; 51 | 52 | // class/function to create a node on linked list 53 | class Node { 54 | val: object; 55 | next: any; 56 | prev: any; 57 | constructor(val: object) { 58 | this.val = val; 59 | this.next = null; 60 | this.prev = null; 61 | }; 62 | }; 63 | 64 | // function to add to the end of linked list 65 | ComponentLinkedList.prototype.add = function (val: object) { 66 | const newNode = new Node(val); 67 | if (!this.tail) { 68 | this.head = newNode; 69 | this.tail = newNode; 70 | return; 71 | } 72 | const oldTail = this.tail; 73 | newNode.prev = oldTail; 74 | this.tail.next = newNode; 75 | this.tail = newNode; 76 | }; 77 | 78 | // function to remove a node passed in 79 | ComponentLinkedList.prototype.remove = function (val: object) { 80 | let curr = this.head; 81 | let prev = curr.prev; 82 | let next = curr.next; 83 | while (curr.val !== val) { 84 | next = next.next; 85 | curr = curr.next; 86 | prev = prev.next; 87 | } 88 | if (!curr) return; 89 | if (!prev && !next) { 90 | this.head = null; 91 | this.tail = null; 92 | return; 93 | } 94 | if (!prev) { 95 | next.prev = null; 96 | this.head = curr.next; 97 | return; 98 | } 99 | if (!next) { 100 | prev.next = null 101 | this.tail = prev; 102 | return; 103 | } 104 | prev.next = next; 105 | next.prev = prev; 106 | } 107 | 108 | const stateAsLinkedList = new ComponentLinkedList; 109 | 110 | const [listOfDraggableElements, setListOfDraggableElements] = useState([ 111 | { 112 | id: 1, 113 | status: 'not-dropped', 114 | title: 'First Name', 115 | data: ` 116 | 117 | {errors.firstName && "First name is required"}` 118 | }, 119 | { 120 | id: 2, 121 | status: 'not-dropped', 122 | title: 'Last Name', 123 | data: ` 124 | 125 | {errors.lastName && "Last name is required"}` 126 | }, 127 | { 128 | id: 3, 129 | status: 'not-dropped', 130 | title: 'Password', 131 | data: ` 132 | 133 | {errors.password && "Password should contain at least 1 number, lowercase letter, uppercase letter, special character between 8 to 16 characters long"}` 134 | }, 135 | { 136 | id: 4, 137 | status: 'not-dropped', 138 | title: 'Radio', 139 | data: ` 140 |
141 | ` 142 | }, 143 | { 144 | id: 5, 145 | status: 'not-dropped', 146 | title: 'Check Box', 147 | data: ` 148 |
149 | ` 150 | }, 151 | { 152 | id: 6, 153 | status: 'not-dropped', 154 | title: 'Submit', 155 | data: `` 156 | }, 157 | { 158 | id: 7, 159 | status: 'not-dropped', 160 | title: 'Gender', 161 | data: ` 162 | ` 167 | }, 168 | { 169 | id: 8, 170 | status: 'not-dropped', 171 | title: 'Age', 172 | data: ` 173 | 174 | {errors.age && "age must be between 18 and 99"}` 175 | }, 176 | { 177 | id: 9, 178 | status: 'not-dropped', 179 | title: 'Email', 180 | data: `` 181 | }, 182 | { 183 | id: 10, 184 | status: 'not-dropped', 185 | title: 'Text Area', 186 | data: ` 187 |