├── .envexample
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── README.md
├── craco.config.js
├── jsconfig.json
├── package.json
├── public
├── _redirects
├── favicon.svg
├── index.html
├── logo192.png
├── manifest.json
└── robots.txt
├── src
├── App.css
├── App.js
├── App.test.js
├── assets
│ ├── add.svg
│ ├── add_icon_only.svg
│ ├── avatar.png
│ ├── basic_shapes.png
│ ├── blue_star.svg
│ ├── blur_craftbase_screenshot.jpg
│ ├── button.png
│ ├── buttonWithIcon.png
│ ├── buttonWithIcon.svg
│ ├── checkbox.png
│ ├── circle.png
│ ├── copy.svg
│ ├── craftbase_board_screenshot.png
│ ├── craftbase_frontend_architecture.png
│ ├── cursor.svg
│ ├── customize.svg
│ ├── divider.png
│ ├── dropdown.png
│ ├── github_logo.svg
│ ├── home_shape_toolbar.png
│ ├── imageCard.png
│ ├── layers_toolbar_icon.svg
│ ├── link_white.svg
│ ├── linkwithicon.png
│ ├── no_signup.svg
│ ├── ooorganize.svg
│ ├── overlay.png
│ ├── pan.svg
│ ├── radio_check.png
│ ├── radio_white.svg
│ ├── radiobox.png
│ ├── rectangle.png
│ ├── refresh.svg
│ ├── right_arrow.svg
│ ├── right_arrow_white.svg
│ ├── sticker.svg
│ ├── subtract.svg
│ ├── text.png
│ ├── textinput.png
│ ├── toggle.png
│ ├── toggle.svg
│ └── twitter_logo.svg
├── common.css
├── components
│ ├── ProgressiveImageLoader
│ │ ├── image.css
│ │ ├── image.js
│ │ ├── index.css
│ │ └── loader.js
│ ├── common
│ │ ├── button.js
│ │ ├── index.css
│ │ ├── modal.js
│ │ ├── modalContainer.js
│ │ ├── portal.js
│ │ ├── spinner.js
│ │ └── spinnerWithSize.js
│ ├── elements
│ │ ├── arrowLine.js
│ │ ├── avatar.js
│ │ ├── button.js
│ │ ├── buttonWithIcon.js
│ │ ├── checkbox.js
│ │ ├── circle.js
│ │ ├── divider.js
│ │ ├── dropdown.js
│ │ ├── frame.js
│ │ ├── groupobject.js
│ │ ├── imageCard.js
│ │ ├── linkWithIcon.js
│ │ ├── logo512.png
│ │ ├── overlay.js
│ │ ├── pencil.js
│ │ ├── radiobox.js
│ │ ├── rectangle.js
│ │ ├── template.js
│ │ ├── text.js
│ │ ├── textarea.js
│ │ ├── textinput.js
│ │ ├── toggle.js
│ │ └── tooltip.js
│ ├── floatingToolbar.js
│ ├── oldToolbar.js
│ ├── sidebar
│ │ ├── elementsDropdown.js
│ │ ├── primary.js
│ │ ├── shareLinkPopup.js
│ │ ├── sidebar.css
│ │ ├── tempPopup.js
│ │ └── userDetailsPopup.js
│ └── utils
│ │ ├── borderStyleBox.js
│ │ ├── colorPicker.js
│ │ ├── dragger.js
│ │ ├── editWrapper.js
│ │ ├── loader.js
│ │ ├── objectSelector.js
│ │ ├── opacitySlider.js
│ │ ├── toolbarConnector.js
│ │ └── zoomer.js
├── constants
│ ├── elementSchema.js
│ ├── exportHooks.js
│ ├── misc.js
│ └── properties.js
├── factory
│ ├── arrowLine.js
│ ├── avatar.js
│ ├── button.js
│ ├── buttonwithicon.js
│ ├── circle.js
│ ├── divider.js
│ ├── dropdown.js
│ ├── frame.js
│ ├── imagecard.js
│ ├── linkwithicon.js
│ ├── main.js
│ ├── newArrowLine.js
│ ├── overlay.js
│ ├── pencil.js
│ ├── rectangle.js
│ ├── text.js
│ ├── textarea.js
│ ├── textinput.js
│ └── toggle.js
├── hooks
│ └── intersectionObserver.js
├── icons
│ ├── icon.js
│ ├── icons.js
│ └── image-gallery-landscape-square-potrait-pic-interface-ui-3 (2).svg
├── index.css
├── index.js
├── logo.svg
├── newCanvas.js
├── routes.js
├── scalingRotate.js
├── schema
│ ├── mutations
│ │ └── index.js
│ ├── queries
│ │ └── index.js
│ └── subscriptions
│ │ └── index.js
├── serviceWorker.js
├── setupTests.js
├── store
│ ├── actions
│ │ └── main.js
│ ├── reducers
│ │ ├── index.js
│ │ └── main.js
│ └── types.js
├── styles
│ └── tailwind.css
├── utils
│ ├── constants.js
│ ├── dragCodeClone.js
│ ├── index.js
│ ├── misc.js
│ └── updateVertices.js
├── views
│ ├── Board
│ │ ├── board.js
│ │ ├── errorBoundary.js
│ │ ├── index.css
│ │ └── index.js
│ └── Home
│ │ ├── errorBoundary.js
│ │ ├── home.js
│ │ ├── index.css
│ │ └── index.js
└── wireframeAssets
│ ├── avatar.svg
│ ├── btn.svg
│ ├── btnWithIcon.svg
│ ├── checkbox.svg
│ ├── circle.svg
│ ├── cursor.svg
│ ├── divider.svg
│ ├── frame.svg
│ ├── imageCard.svg
│ ├── miniAvatar.svg
│ ├── pencil.svg
│ ├── radiobox.svg
│ ├── rectangle.svg
│ ├── text.svg
│ └── toggle.svg
├── tailwind.config.js
├── temp.js
├── temp.json
└── yarn.lock
/.envexample:
--------------------------------------------------------------------------------
1 | REACT_APP_HASURA_ADMIN_SECRET=secret
2 | REACT_APP_WS_GRAPHQL_ENDPOINT=ws://localhost:8080/v1/graphql
3 | REACT_APP_GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql
4 | REACT_APP_GRAPHQL_ERROR_POLICY=all
--------------------------------------------------------------------------------
/.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 |
25 | .env
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 4,
4 | "semi": false,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Welcome
2 |
3 | Thanks for reading this README of craftbase app.
4 |
5 | Craftbase is online whiteboarding tool where user(s) can brainstorm using various objects such as shapes, pencil and to name a few, with themselves as well share it with other users.
6 |
7 | Currently, the live sharing feature is internally disabled but I am working on adding feature flag for it. Once it's enabled, you can live share it with your other colleagues/peers/friends and collaborate together.
8 |
9 | My attempt is to make it as simple to spin up a board and start whiteboarding right away. No signup/signin required.
10 |
11 | Please expect some bugs and glitches as I am working on it (mostly on weekends). Feel free to raise GH issue whether is feature, suggestion or bug related to craftbase.
12 |
13 | ## Craftbase and its architecture
14 |
15 | 
16 |
17 | Craftbase underneath uses [two.js](https://github.com/jonobr1/two.js) library to render scene graph which is developed by awesome [Jono brandel](https://github.com/jonobr1)
18 |
19 | The craftbase has "Board" as it's main primary concept which holds the specific responsiblity of rendering the canvas, components and utilities. The heirarchy looks like : Board -> Canvas -> ElementRenderer -> Component Element -> Component Factory
20 |
21 | Board represents the Canvas, primary sidebar and floating toolbar (soon to be). Canvas is where the logic of rendering the components in 2d space is present. Each component has it's factory from which the template code is generated as output and provided to "component element" which hooks with react functional component and it attaches some event listeners to specific component.
22 |
23 | The user interaction controls such as mouse , drag , zoom and pan are part of Canvas. All these interactions are able to make the board lively as if user was whiteboarding on it.
24 |
25 | ## Run/develop locally
26 |
27 | This craftbase repo is containing the frontend code and the other repo under same org is containing the backend code (craftbase-hasura)
28 |
29 | You need to setup both the projects (frontend and backend) locally in order to successfully run craftbase application server in your machine.
30 |
31 | ### Backend
32 |
33 | Head over to the readme.md of the [craftbase-hasura](https://github.com/craftbase-org/craftbase-hasura) and follow the instructions to setup backend of the craftbase locally.
34 |
35 | ### Frontend
36 |
37 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
38 |
39 | So you'll need to clone the repository and run `yarn` first to install the dependencies. You can also run `npm install` depending on your choice since it will have it's own lock file for peer and core dependencies.
40 |
41 | Then, create `.env` file in root of the project directory and copy the contents from .envexample into that.
42 |
43 | Finally, to run the frontend server, you can run `yarn start` which will start the server on default port of 3000.
44 |
45 | #### `yarn start`
46 |
47 | Runs the app in the development mode.
48 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
49 |
50 | The page will reload if you make edits.
51 | You will also see any lint errors in the console.
52 |
53 | #### `yarn build`
54 |
55 | Builds the app for production to the `build` folder.
56 | It correctly bundles React in production mode and optimizes the build for the best performance.
57 |
58 | The build is minified and the filenames include the hashes.
59 | Your app is ready to be deployed!
60 |
61 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
62 |
--------------------------------------------------------------------------------
/craco.config.js:
--------------------------------------------------------------------------------
1 | const CracoLessPlugin = require('craco-less')
2 |
3 | module.exports = {
4 | style: {},
5 | plugins: [
6 | // {
7 | // plugin: CracoLessPlugin,
8 | // options: {
9 | // lessLoaderOptions: {
10 | // lessOptions: {
11 | // modifyVars: {
12 | // '@primary-color': '#FF4747',
13 | // },
14 | // javascriptEnabled: true,
15 | // },
16 | // },
17 | // },
18 | // },
19 | ],
20 | }
21 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src"
4 | },
5 | "include": ["src"]
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "craftbase",
3 | "version": "0.2.0",
4 | "private": true,
5 | "dependencies": {
6 | "@apollo/client": "^3.10.4",
7 | "@craco/craco": "^7.1.0",
8 | "@tanstack/react-ranger": "^0.0.4",
9 | "@testing-library/jest-dom": "^6.4.5",
10 | "@testing-library/react": "^16.0.0",
11 | "@testing-library/user-event": "^14.5.2",
12 | "classnames": "^2.5.1",
13 | "craco-less": "^3.0.1",
14 | "framer-motion": "^11.2.10",
15 | "graphql": "^16.8.1",
16 | "idx": "^3.0.3",
17 | "immer": "^10.1.1",
18 | "interactjs": "^1.10.27",
19 | "prop-types": "^15.8.1",
20 | "react": "^18.3.1",
21 | "react-dom": "^18.3.1",
22 | "react-loadable": "^5.5.0",
23 | "react-redux": "^9.1.2",
24 | "react-responsive": "10.0.0",
25 | "react-router": "^6.23.1",
26 | "react-router-dom": "^6.23.1",
27 | "react-scripts": "5.0.1",
28 | "redux": "^5.0.1",
29 | "redux-logger": "^3.0.6",
30 | "redux-thunk": "^3.1.0",
31 | "styled-components": "^6.1.11",
32 | "subscriptions-transport-ws": "^0.11.0",
33 | "two.js": "^0.8.13",
34 | "use-immer": "^0.9.0"
35 | },
36 | "scripts": {
37 | "start": "craco start",
38 | "build": "CI= craco build",
39 | "test": "craco test",
40 | "eject": "react-scripts eject"
41 | },
42 | "resolutions": {
43 | "react-scripts/**/react-error-overlay": "6.0.9"
44 | },
45 | "eslintConfig": {
46 | "extends": "react-app"
47 | },
48 | "browserslist": {
49 | "production": [
50 | ">0.2%",
51 | "not dead",
52 | "not op_mini all"
53 | ],
54 | "development": [
55 | "last 1 chrome version",
56 | "last 1 firefox version",
57 | "last 1 safari version"
58 | ]
59 | },
60 | "devDependencies": {
61 | "@babel/plugin-proposal-private-property-in-object": "^7.14.5",
62 | "tailwindcss": "^3.4.4"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
2 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
21 |
27 |
31 |
35 |
44 | Craftbase
45 |
46 |
50 |
59 |
60 |
80 |
81 |
82 |
83 |
84 |
94 |
95 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/public/logo192.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Craftbase",
3 | "name": "Craftbase | Wireframe tool",
4 | "icons": [
5 | {
6 | "src": "favicon.svg",
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 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Routes, Route, BrowserRouter } from 'react-router-dom'
3 |
4 | import {
5 | ApolloClient,
6 | ApolloProvider,
7 | InMemoryCache,
8 | HttpLink,
9 | split,
10 | } from '@apollo/client'
11 | import { getMainDefinition } from '@apollo/client/utilities'
12 | import { WebSocketLink } from '@apollo/client/link/ws'
13 |
14 | import BoardViewContainer from 'views/Board'
15 | import HomePageViewContainer from 'views/Home'
16 |
17 | import routes from './routes'
18 |
19 | import './App.css'
20 |
21 | import './common.css'
22 |
23 | function getApolloClient() {
24 | const accessToken = localStorage.getItem('access_token')
25 | // console.log('access_token in client', accessToken)
26 | // const httpLink = new HttpLink({
27 | // uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
28 | // headers: {
29 | // authorization: `Bearer ${accessToken}`,
30 | // 'content-type': 'application/json',
31 | // },
32 | // })
33 | const httpLink = new HttpLink({
34 | uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
35 | headers: {
36 | // authorization: `Bearer ${accessToken}`,
37 | 'content-type': 'application/json',
38 | 'x-hasura-admin-secret': process.env.REACT_APP_HASURA_ADMIN_SECRET,
39 | },
40 | })
41 |
42 | const wsLink = new WebSocketLink({
43 | uri: process.env.REACT_APP_WS_GRAPHQL_ENDPOINT,
44 |
45 | options: {
46 | reconnect: true,
47 | connectionParams: {
48 | headers: {
49 | // authorization: `Bearer ${accessToken}`,
50 | 'content-type': 'application/json',
51 | 'x-hasura-admin-secret':
52 | process.env.REACT_APP_HASURA_ADMIN_SECRET,
53 | },
54 | },
55 | },
56 | })
57 |
58 | // The split function takes three parameters:
59 | //
60 | // * A function that's called for each operation to execute
61 | // * The Link to use for an operation if the function returns a "truthy" value
62 | // * The Link to use for an operation if the function returns a "falsy" value
63 | const splitLink = split(
64 | ({ query }) => {
65 | const definition = getMainDefinition(query)
66 | return (
67 | definition.kind === 'OperationDefinition' &&
68 | definition.operation === 'subscription'
69 | )
70 | },
71 | wsLink,
72 | httpLink
73 | )
74 |
75 | const client = new ApolloClient({
76 | link: splitLink,
77 | cache: new InMemoryCache(),
78 | })
79 | return client
80 | }
81 |
82 | class App extends Component {
83 | constructor(props) {
84 | super(props)
85 | }
86 |
87 | render() {
88 | const apolloClient = getApolloClient()
89 | return (
90 |
91 |
92 |
93 |
94 | }
97 | />
98 | }
101 | />
102 |
103 |
104 |
105 |
106 | )
107 | }
108 | }
109 |
110 | export default App
111 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render();
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/src/assets/add.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/add_icon_only.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/avatar.png
--------------------------------------------------------------------------------
/src/assets/basic_shapes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/basic_shapes.png
--------------------------------------------------------------------------------
/src/assets/blue_star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/blur_craftbase_screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/blur_craftbase_screenshot.jpg
--------------------------------------------------------------------------------
/src/assets/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/button.png
--------------------------------------------------------------------------------
/src/assets/buttonWithIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/buttonWithIcon.png
--------------------------------------------------------------------------------
/src/assets/buttonWithIcon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/checkbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/checkbox.png
--------------------------------------------------------------------------------
/src/assets/circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/circle.png
--------------------------------------------------------------------------------
/src/assets/copy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/craftbase_board_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/craftbase_board_screenshot.png
--------------------------------------------------------------------------------
/src/assets/craftbase_frontend_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/craftbase_frontend_architecture.png
--------------------------------------------------------------------------------
/src/assets/cursor.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/customize.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/divider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/divider.png
--------------------------------------------------------------------------------
/src/assets/dropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/dropdown.png
--------------------------------------------------------------------------------
/src/assets/github_logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/home_shape_toolbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/home_shape_toolbar.png
--------------------------------------------------------------------------------
/src/assets/imageCard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/imageCard.png
--------------------------------------------------------------------------------
/src/assets/layers_toolbar_icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/link_white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/linkwithicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/linkwithicon.png
--------------------------------------------------------------------------------
/src/assets/no_signup.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/overlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/overlay.png
--------------------------------------------------------------------------------
/src/assets/pan.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/radio_check.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/radio_check.png
--------------------------------------------------------------------------------
/src/assets/radio_white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/radiobox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/radiobox.png
--------------------------------------------------------------------------------
/src/assets/rectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/rectangle.png
--------------------------------------------------------------------------------
/src/assets/refresh.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/right_arrow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/right_arrow_white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/sticker.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/subtract.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/text.png
--------------------------------------------------------------------------------
/src/assets/textinput.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/textinput.png
--------------------------------------------------------------------------------
/src/assets/toggle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/assets/toggle.png
--------------------------------------------------------------------------------
/src/assets/toggle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/twitter_logo.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/common.css:
--------------------------------------------------------------------------------
1 | .zoomer-container .zoom-in-selector {
2 | position: fixed;
3 | right: 60px;
4 | bottom: 20px;
5 | opacity: 1;
6 | z-index: 1;
7 | }
8 | .zoomer-container .zoom-out-selector {
9 | position: fixed;
10 | right: 15px;
11 | bottom: 20px;
12 | opacity: 1;
13 | z-index: 1;
14 | }
15 |
16 | .dragger-picker {
17 | cursor: pointer;
18 | }
19 |
20 | .rounded-50-percent {
21 | border-radius: 50%;
22 | }
23 |
24 | /* patch - to disable automatically appending iframe */
25 | iframe {
26 | z-index: -11111 !important;
27 | }
28 |
29 | /* css for text wireframe element's foreign object */
30 | .foreign-text-container-base {
31 | /* border-radius: 5px; */
32 | background: transparent;
33 | /* border: 2px solid #ccc; */
34 | height: 100%;
35 | display: flex;
36 | align-items: center;
37 | justify-content: center;
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/ProgressiveImageLoader/image.css:
--------------------------------------------------------------------------------
1 | .image {
2 | position: absolute;
3 | top: 0;
4 | left: 0;
5 | width: 100%;
6 | height: 100%;
7 | }
8 | .full {
9 | transition: opacity 400ms ease 0ms;
10 | }
11 | .thumb {
12 | filter: blur(20px);
13 | transform: scale(1.1);
14 | transition: visibility 0ms ease 400ms;
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/ProgressiveImageLoader/image.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import './image.css'
3 |
4 | const Image = (props) => {
5 | const [isLoaded, setIsLoaded] = React.useState(false)
6 | return (
7 |
8 |
14 |
{
16 | setIsLoaded(true)
17 | }}
18 | className="image full"
19 | style={{ opacity: isLoaded ? 1 : 0 }}
20 | alt={props.alt}
21 | src={props.src}
22 | />
23 |
24 | )
25 | }
26 | export default Image
27 |
--------------------------------------------------------------------------------
/src/components/ProgressiveImageLoader/index.css:
--------------------------------------------------------------------------------
1 | .image-container {
2 | position: relative;
3 | overflow: hidden;
4 | background: rgba(0, 0, 0, 0.05);
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/ProgressiveImageLoader/loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import useIntersectionObserver from 'hooks/intersectionObserver'
3 | import Image from './image'
4 | import './index.css'
5 |
6 | const ImageContainer = (props) => {
7 | const ref = React.useRef()
8 | const [isVisible, setIsVisible] = React.useState(false)
9 | useIntersectionObserver({
10 | target: ref,
11 | onIntersect: ([{ isIntersecting }], observerElement) => {
12 | if (isIntersecting) {
13 | setIsVisible(true)
14 | observerElement.unobserve(ref.current)
15 | }
16 | },
17 | })
18 | const aspectRatio = (props.height / props.width) * 100
19 | return (
20 |
25 | {isVisible && (
26 |
27 | )}
28 |
29 | )
30 | }
31 | export default ImageContainer
32 |
--------------------------------------------------------------------------------
/src/components/common/button.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Spinner from 'components/common/spinnerWithSize'
3 |
4 | const Button = (props) => {
5 | let baseClassNames =
6 | ' focus:outline-none rounded-md hover:shadow-lg flex items-center '
7 |
8 | switch (props.intent) {
9 | case 'primary': // red
10 | baseClassNames += ' bg-primary-blue text-white '
11 | break
12 | case 'info': // white
13 | baseClassNames += ' bg-neutrals-n500 text-neutrals-n40 '
14 | break
15 | case 'secondary': // white
16 | baseClassNames +=
17 | ' bg-white text-primary-blue border border-primary-blue'
18 | break
19 | default:
20 | baseClassNames +=
21 | ' bg-transparent text-white border border-neutral-gray-400'
22 | break
23 | }
24 |
25 | switch (props.size) {
26 | case 'large': // red
27 | baseClassNames +=
28 | ' px-4 py-2 2xl:px-6 2xl:py-3 text-sm xl:text-base 2xl:text-lg'
29 | break
30 | case 'small': // white
31 | baseClassNames += ' px-2 py-1 text-xs '
32 | break
33 | default:
34 | baseClassNames +=
35 | ' px-3 py-2 2xl:px-4 2xl:py-2 text-sm 2xl:text-base '
36 | break
37 | }
38 |
39 | if (props?.customClass) {
40 | baseClassNames = props.customClass
41 | }
42 |
43 | if (props?.disabled) {
44 | baseClassNames += ' cursor-not-allowed opacity-05 '
45 | }
46 |
47 | if (props?.extendClass) {
48 | baseClassNames = props.extendClass + ' ' + baseClassNames
49 | }
50 |
51 | return (
52 | <>
53 |
76 | >
77 | )
78 | }
79 |
80 | // Button.defaultProps = {
81 | // disabled: false,
82 | // loading: false,
83 | // onClick: () => {},
84 | // intent: '',
85 | // size: '',
86 | // label: '',
87 | // customClass: null,
88 | // extendClass: null,
89 | // }
90 |
91 | export default Button
92 |
--------------------------------------------------------------------------------
/src/components/common/index.css:
--------------------------------------------------------------------------------
1 | .loaders {
2 | display: flex;
3 | align-items: center;
4 | justify-content: center;
5 | width: 400px;
6 | height: 200px;
7 | border-radius: 20px;
8 | margin: 10% auto;
9 | background-color: #e0e0e0;
10 | }
11 | .loader {
12 | margin: 20px;
13 | width: 35px;
14 | height: 35px;
15 | border-radius: 35px;
16 | display: block;
17 | /* background-color:green; */
18 | border: 4px solid;
19 | animation: spinner 1s linear infinite;
20 | }
21 |
22 | .text-red {
23 | color: red;
24 | }
25 |
26 | .lg-loader {
27 | margin: 20px;
28 | display: block;
29 | /* background-color:green; */
30 | border: 4px solid;
31 | animation: spinner 0.5s linear infinite;
32 | width: 45px;
33 | height: 45px;
34 | border-radius: 45px;
35 | }
36 | .md-loader {
37 | margin: 20px;
38 | display: block;
39 | /* background-color:green; */
40 | border: 2.5px solid;
41 | animation: spinner 0.4s linear infinite;
42 | width: 20px;
43 | height: 20px;
44 | border-radius: 25px;
45 | }
46 | .sm-loader {
47 | margin: 20px;
48 | display: block;
49 | /* background-color:green; */
50 | border: 3px solid;
51 | animation: spinner 0.4s linear infinite;
52 | width: 18px;
53 | height: 18px;
54 | border-radius: 25px;
55 | }
56 | .xs-loader {
57 | margin: 20px;
58 | display: block;
59 | /* background-color:green; */
60 | border: 4px solid;
61 | animation: spinner 0.5s linear infinite;
62 | width: 15px;
63 | height: 15px;
64 | border-radius: 15px;
65 | }
66 |
67 | .loader-1 {
68 | border-color: #fff9c4;
69 | border-top-color: #9575cd;
70 | }
71 | .loader-2 {
72 | border-color: transparent;
73 | border-top-color: #81d4fa;
74 | border-bottom-color: #9575cd;
75 | }
76 | .loader-3 {
77 | border-color: transparent;
78 | border-bottom-color: #9575cd;
79 | }
80 |
81 | @keyframes spinner {
82 | 0% {
83 | transform: rotate(0deg);
84 | }
85 | 100% {
86 | transform: rotate(360deg);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/components/common/modal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 | // import "wicg-inert";
4 |
5 | import Portal from './portal'
6 |
7 | const Backdrop = styled.div`
8 | position: fixed;
9 | top: 0;
10 | right: 0;
11 | bottom: 0;
12 | left: 0;
13 | background-color: rgba(51, 51, 51, 0.3);
14 | backdrop-filter: blur(1px);
15 | opacity: 0;
16 | transition: all 100ms cubic-bezier(0.4, 0, 0.2, 1);
17 | transition-delay: 200ms;
18 | display: flex;
19 | align-items: center;
20 | justify-content: center;
21 |
22 | & .modal-content {
23 | transform: translateY(100px);
24 | transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
25 | opacity: 0;
26 | }
27 |
28 | &.active {
29 | transition-duration: 250ms;
30 | transition-delay: 0ms;
31 | opacity: 1;
32 |
33 | & .modal-content {
34 | transform: translateY(0);
35 | opacity: 1;
36 | transition-delay: 150ms;
37 | transition-duration: 350ms;
38 | }
39 | }
40 | `
41 |
42 | const Content = styled.div`
43 | position: relative;
44 | padding: 20px;
45 | box-sizing: border-box;
46 | min-height: 50px;
47 | min-width: 50px;
48 | max-height: 80%;
49 | max-width: 80%;
50 | box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
51 | background-color: white;
52 | border-radius: 2px;
53 | `
54 |
55 | export default function Modal(props) {
56 | const [active, setActive] = React.useState(false)
57 | const { open, onClose, locked } = props
58 | const backdrop = React.useRef(null)
59 |
60 | React.useEffect(() => {
61 | const { current } = backdrop
62 |
63 | const transitionEnd = () => setActive(open)
64 |
65 | const keyHandler = (e) =>
66 | !locked && [27].indexOf(e.which) >= 0 && onClose()
67 |
68 | const clickHandler = (e) => !locked && e.target === current && onClose()
69 |
70 | if (current) {
71 | current.addEventListener('transitionend', transitionEnd)
72 | current.addEventListener('click', clickHandler)
73 | window.addEventListener('keyup', keyHandler)
74 | }
75 |
76 | if (open) {
77 | window.setTimeout(() => {
78 | document.activeElement.blur()
79 | setActive(open)
80 | // document.querySelector("#root").setAttribute("inert", "true");
81 | }, 10)
82 | }
83 |
84 | return () => {
85 | if (current) {
86 | current.removeEventListener('transitionend', transitionEnd)
87 | current.removeEventListener('click', clickHandler)
88 | }
89 |
90 | // document.querySelector("#root").removeAttribute("inert");
91 | window.removeEventListener('keyup', keyHandler)
92 | }
93 | }, [open, locked, onClose])
94 |
95 | return (
96 |
97 | {(open || active) && (
98 |
99 |
103 |
104 | {props.children}
105 |
106 |
107 |
108 | )}
109 |
110 | )
111 | }
112 |
--------------------------------------------------------------------------------
/src/components/common/modalContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Modal from './modal'
4 |
5 | const ModalContainer = ({ closeModal, showModal, children }) => {
6 | return (
7 | <>
8 |
9 | {children}
10 |
11 | >
12 | )
13 | }
14 |
15 | export default ModalContainer
16 |
--------------------------------------------------------------------------------
/src/components/common/portal.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | export default function Portal({ children, parent, className }) {
5 | const el = React.useMemo(() => document.createElement('div'), [])
6 | React.useEffect(() => {
7 | const target = parent && parent.appendChild ? parent : document.body
8 | const classList = ['portal-container']
9 | if (className)
10 | className.split(' ').forEach((item) => classList.push(item))
11 | classList.forEach((item) => el.classList.add(item))
12 | target.appendChild(el)
13 | return () => {
14 | target.removeChild(el)
15 | }
16 | }, [el, parent, className])
17 | return ReactDOM.createPortal(children, el)
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/common/spinner.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import SpinnerWithSize from './spinnerWithSize'
3 | const Spinner = ({ displayText }) => {
4 | return (
5 | <>
6 |
7 | {displayText ? (
8 | displayText
9 | ) : (
10 |
18 | )}
19 |
20 | >
21 | )
22 | }
23 |
24 | export default Spinner
25 |
--------------------------------------------------------------------------------
/src/components/common/spinnerWithSize.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react'
2 | import './index.css'
3 |
4 | const SpinnerWithSize = (props) => {
5 | const { loaderSize, color, customStyles } = props
6 | const renderLoader = () => {
7 | switch (loaderSize) {
8 | case 'lg':
9 | return (
10 |
16 | )
17 | case 'md':
18 | return (
19 |
25 | )
26 | case 'sm':
27 | return (
28 |
34 | )
35 | case 'xs':
36 | return (
37 |
43 | )
44 | default:
45 | return
46 | }
47 | }
48 | return {renderLoader()}
49 | }
50 |
51 | // SpinnerWithSize.defaultProps = {
52 | // loaderSize: 'lg',
53 | // color: '#2F98D0',
54 | // customStyles: {},
55 | // }
56 |
57 | export default SpinnerWithSize
58 |
--------------------------------------------------------------------------------
/src/components/elements/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/craftbase-org/craftbase/235cbd840d18bdcf05ba96ffaca964f1c5748aa7/src/components/elements/logo512.png
--------------------------------------------------------------------------------
/src/components/elements/pencil.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import interact from 'interactjs'
3 | import { useImmer } from 'use-immer'
4 |
5 | import { elementOnBlurHandler } from 'utils/misc'
6 | import getEditComponents from 'components/utils/editWrapper'
7 | import PencilFactory from 'factory/pencil'
8 | import Toolbar from 'components/floatingToolbar'
9 |
10 | function Pencil(props) {
11 | const selectedComponents = []
12 |
13 | const [showToolbar, toggleToolbar] = useState(false)
14 | const [internalState, setInternalState] = useImmer({})
15 |
16 | const two = props.twoJSInstance
17 | let selectorInstance = null
18 | let toolbarInstance = null
19 | let groupObject = null
20 |
21 | // function onBlurHandler(e) {
22 | // elementOnBlurHandler(e, selectorInstance, two)
23 | // document.getElementById(`${groupObject.id}`) &&
24 | // document
25 | // .getElementById(`${groupObject.id}`)
26 | // .removeEventListener('keydown', handleKeyDown)
27 | // }
28 |
29 | // function handleKeyDown(e) {
30 | // if (e.keyCode === 8 || e.keyCode === 46) {
31 | // console.log('handle key down event', e)
32 | // // DELETE/BACKSPACE KEY WAS PRESSED
33 | // props.handleDeleteComponent &&
34 | // props.handleDeleteComponent(groupObject)
35 | // two.remove([groupObject])
36 | // two.update()
37 | // }
38 | // }
39 |
40 | // function onFocusHandler(e) {
41 | // document.getElementById(`${groupObject.id}`).style.outline = 0
42 | // document
43 | // .getElementById(`${groupObject.id}`)
44 | // .addEventListener('keydown', handleKeyDown)
45 | // }
46 |
47 | // Using unmount phase to remove event listeners
48 | useEffect(() => {
49 | // Calculate x and y through dividing width and height by 2 or vice versa
50 | // if x and y are given then multiply width and height into 2
51 | const offsetHeight = 0
52 |
53 | const prevX = props.x
54 | const prevY = props.y
55 |
56 | // Instantiate factory
57 | const elementFactory = new PencilFactory(two, prevX, prevY, {
58 | ...props,
59 | })
60 | // Get all instances of every sub child element
61 | const { group, path } = elementFactory.createElement()
62 | group.elementData = { ...props.itemData, ...props }
63 |
64 | if (props.parentGroup) {
65 | /** This element will be rendered and scoped in its parent group */
66 | console.log('properties of pencil', props)
67 | const parentGroup = props.parentGroup
68 | path.translation.x = props.properties.x
69 | path.translation.y = props.properties.y
70 | parentGroup.add(path)
71 | two.update()
72 | } else {
73 | /** This element will render by creating it's own group wrapper */
74 | groupObject = group
75 |
76 | const { selector } = getEditComponents(two, group, 4)
77 | selectorInstance = selector
78 |
79 | group.children.unshift(path)
80 | two.update()
81 |
82 | // document
83 | // .getElementById(group.id)
84 | // .setAttribute('class', 'dragger-picker')
85 |
86 | document
87 | .getElementById(group.id)
88 | .setAttribute('class', 'avoid-dragging')
89 |
90 | // setting database's id in html attribute of element
91 | document
92 | .getElementById(group.id)
93 | .setAttribute('data-component-id', props.id)
94 |
95 | // console.log('two circle', group.id)
96 | const initialSceneCoords = document
97 | .getElementById(two.scene.id)
98 | .getBoundingClientRect()
99 | console.log('initialSceneCoords', initialSceneCoords)
100 |
101 | setInternalState((draft) => {
102 | draft.element = {
103 | [path.id]: path,
104 | [group.id]: group,
105 | // [selector.id]: selector,
106 | }
107 | draft.group = {
108 | id: group.id,
109 | data: group,
110 | }
111 | draft.shape = {
112 | id: path.id,
113 | data: path,
114 | }
115 | draft.text = {
116 | data: {},
117 | }
118 | draft.icon = {
119 | data: {},
120 | }
121 | })
122 |
123 | const getGroupElementFromDOM = document.getElementById(
124 | `${group.id}`
125 | )
126 | // getGroupElementFromDOM.addEventListener('focus', onFocusHandler)
127 | // getGroupElementFromDOM.addEventListener('blur', onBlurHandler)
128 |
129 | // If component is in area of selection frame/tool, programmatically enable it's selector
130 | // if (selectedComponents.includes(props.id)) {
131 | // console.log('selectedComponents', selectedComponents)
132 |
133 | // // forcefully
134 | // // document.getElementById(`${group.id}`).focus();
135 |
136 | // selector.update(
137 | // circle.getBoundingClientRect(true).left - 10,
138 | // circle.getBoundingClientRect(true).right + 10,
139 | // circle.getBoundingClientRect(true).top - 10,
140 | // circle.getBoundingClientRect(true).bottom + 10
141 | // )
142 | // }
143 |
144 | // const { mousemove, mouseup } = handleDrag(two, group, 'Circle')
145 |
146 | // interact(`#${group.id}`).on('click', () => {
147 | // // two.scene.scale = 1
148 | // console.log('on click circle', group)
149 | // // selector.update(
150 | // // circle.getBoundingClientRect(true).left - 10,
151 | // // circle.getBoundingClientRect(true).right + 10,
152 | // // circle.getBoundingClientRect(true).top - 10,
153 | // // circle.getBoundingClientRect(true).bottom + 10
154 | // // )
155 | // // two.update()
156 | // toggleToolbar(true)
157 | // })
158 | }
159 |
160 | return () => {
161 | console.log('UNMOUNTING in Pencil', group)
162 | // clean garbage by removing instance
163 | // two.remove(group)
164 | }
165 | }, [])
166 |
167 | useEffect(() => {
168 | if (internalState?.group?.data) {
169 | let groupInstance = internalState.group.data
170 | groupInstance.translation.x = props.x
171 | groupInstance.translation.y = props.y
172 | two.update()
173 | }
174 | if (internalState?.shape?.data) {
175 | let shapeInstance = internalState.shape.data
176 |
177 | shapeInstance.width = props.width
178 | ? props.width
179 | : shapeInstance.width
180 | shapeInstance.height = props.height
181 | ? props.height
182 | : shapeInstance.height
183 | shapeInstance.fill = props.fill ? props.fill : shapeInstance.fill
184 |
185 | two.update()
186 | }
187 | }, [props.x, props.y, props.fill, props.width, props.height])
188 |
189 | function closeToolbar() {
190 | toggleToolbar(false)
191 | }
192 |
193 | return (
194 |
195 |
196 | {/* */}
197 | {showToolbar && (
198 | {
204 | two.update()
205 | }}
206 | />
207 | )}
208 |
209 | )
210 | }
211 |
212 | export default Pencil
213 |
--------------------------------------------------------------------------------
/src/components/elements/template.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import interact from 'interactjs';
3 | import { useDispatch, useSelector } from 'react-redux';
4 | import { useImmer } from 'use-immer';
5 |
6 | import getEditComponents from 'components/utils/editWrapper';
7 | import { elementOnBlurHandler } from 'utils/misc';
8 | import { setPeronsalInformation } from 'store/actions/main';
9 | import { UPDATE_ELEMENT_DATA } from 'store/types';
10 | import ElementFactory from 'factory/rectangle';
11 | import Toolbar from 'components/floatingToolbar';
12 |
13 | function Rectangle(props) {
14 | const selectedComponents = useSelector(
15 | (state) => state.main.selectedComponents
16 | );
17 | const [showToolbar, toggleToolbar] = useState(false);
18 | const [internalState, setInternalState] = useImmer({});
19 | const dispatch = useDispatch();
20 | const two = props.twoJSInstance;
21 | let selectorInstance = null;
22 | let groupObject = null;
23 |
24 | function onBlurHandler(e) {
25 | elementOnBlurHandler(e, selectorInstance, two);
26 | }
27 |
28 | function onFocusHandler(e) {
29 | document.getElementById(`${groupObject.id}`).style.outline = 0;
30 | }
31 |
32 | // Using unmount phase to remove event listeners
33 | useEffect(() => {
34 | // Calculate x and y through dividing width and height by 2 or vice versa
35 | // if x and y are given then multiply width and height into 2
36 | const offsetHeight = 0;
37 | const prevX = localStorage.getItem('rectangle_coordX');
38 | const prevY = localStorage.getItem('rectangle_coordY');
39 |
40 | // Instantiate factory
41 | const elementFactory = new ElementFactory(two, prevX, prevY, {});
42 | // Get all instances of every sub child element
43 | const { group, rectangle } = elementFactory.createElement();
44 |
45 | if (props.parentGroup) {
46 | /** This element will be rendered and scoped in its parent group */
47 | const parentGroup = props.parentGroup;
48 | parentGroup.add(rectangle);
49 | two.update();
50 | } else {
51 | /** This element will render by creating it's own group wrapper */
52 | groupObject = group;
53 |
54 | const { selector } = getEditComponents(two, group, 4);
55 | selectorInstance = selector;
56 | group.children.unshift(rectangle);
57 | two.update();
58 |
59 | setInternalState((draft) => {
60 | draft.element = {
61 | [rectangle.id]: rectangle,
62 | [group.id]: group,
63 | // [selector.id]: selector,
64 | };
65 | draft.group = {
66 | id: group.id,
67 | data: group,
68 | };
69 | draft.shape = {
70 | type: 'rectangle',
71 | id: rectangle.id,
72 | data: rectangle,
73 | };
74 | });
75 |
76 | const getGroupElementFromDOM = document.getElementById(`${group.id}`);
77 | getGroupElementFromDOM.addEventListener('focus', onFocusHandler);
78 | getGroupElementFromDOM.addEventListener('blur', onBlurHandler);
79 |
80 | // If component is in area of selection frame/tool, programmatically enable it's selector
81 | if (selectedComponents.includes(props.id)) {
82 | console.log('selectedComponents', selectedComponents);
83 |
84 | selector.update(
85 | rectangle.getBoundingClientRect(true).left - 10,
86 | rectangle.getBoundingClientRect(true).right + 10,
87 | rectangle.getBoundingClientRect(true).top - 10,
88 | rectangle.getBoundingClientRect(true).bottom + 10
89 | );
90 | }
91 |
92 | interact(`#${group.id}`).on('click', () => {
93 | console.log('on click ');
94 | selector.update(
95 | rectangle.getBoundingClientRect(true).left - 10,
96 | rectangle.getBoundingClientRect(true).right + 10,
97 | rectangle.getBoundingClientRect(true).top - 10,
98 | rectangle.getBoundingClientRect(true).bottom + 10
99 | );
100 | two.update();
101 |
102 | toggleToolbar(true);
103 | });
104 |
105 | // RESIZE SHAPE LOGIC
106 | interact(`#${group.id}`).resizable({
107 | edges: { right: true, left: true, top: true, bottom: true },
108 |
109 | listeners: {
110 | move(event) {
111 | const target = event.target;
112 | const rect = event.rect;
113 |
114 | const minRectHeight = parseInt(rect.height / 2);
115 | const minRectWidth = parseInt(rect.width / 2);
116 |
117 | if (minRectHeight > 20 && minRectWidth > 20) {
118 | rectangle.width = rect.width;
119 | rectangle.height = rect.height;
120 |
121 | selector.update(
122 | rectangle.getBoundingClientRect(true).left - 10,
123 | rectangle.getBoundingClientRect(true).right + 10,
124 | rectangle.getBoundingClientRect(true).top - 10,
125 | rectangle.getBoundingClientRect(true).bottom + 10
126 | );
127 | }
128 |
129 | two.update();
130 | },
131 | end(event) {
132 | console.log('the end');
133 | },
134 | },
135 | });
136 |
137 | // DRAG SHAPE LOGIC
138 | interact(`#${group.id}`).draggable({
139 | // enable inertial throwing
140 | inertia: false,
141 |
142 | listeners: {
143 | start(event) {
144 | // console.log(event.type, event.target);
145 | },
146 | move(event) {
147 | event.target.style.transform = `translate(${event.pageX}px, ${
148 | event.pageY - offsetHeight
149 | }px)`;
150 | },
151 | end(event) {
152 | console.log(
153 | 'event x',
154 | event.target.getBoundingClientRect(),
155 | event.rect.left,
156 | event.pageX,
157 | event.clientX
158 | );
159 | // alternate -> take event.rect.left for x
160 | localStorage.setItem('rectangle_coordX', parseInt(event.pageX));
161 | localStorage.setItem(
162 | 'rectangle_coordY',
163 | parseInt(event.pageY - offsetHeight)
164 | );
165 | group.translation.x = event.pageX;
166 | two.update();
167 | dispatch(
168 | setPeronsalInformation('COMPLETE', {
169 | data: {},
170 | shapeObj: { rectangle },
171 | fill: rectangle.fill,
172 | translationX: group.translation.x,
173 | translationY: group.translation.y,
174 | })
175 | );
176 | dispatch(
177 | setPeronsalInformation(UPDATE_ELEMENT_DATA, {
178 | data: {
179 | id: rectangle.id,
180 | property: 'x',
181 | value: group.translation.x,
182 | },
183 | })
184 | );
185 | dispatch(
186 | setPeronsalInformation(UPDATE_ELEMENT_DATA, {
187 | data: {
188 | id: rectangle.id,
189 | property: 'y',
190 | value: group.translation.y,
191 | },
192 | })
193 | );
194 | },
195 | },
196 | });
197 | }
198 |
199 | return () => {
200 | console.log('UNMOUNTING in Rectangle', group);
201 | // clean garbage by removing instance
202 | two.remove(group);
203 | };
204 | }, []);
205 |
206 | function closeToolbar() {
207 | toggleToolbar(false);
208 | }
209 |
210 | return (
211 |
212 |
213 | {showToolbar && }
214 | {/* */}
215 | {showToolbar ? (
216 | {
221 | two.update();
222 | }}
223 | />
224 | ) : null}
225 | {/* */}
226 |
227 | );
228 | }
229 |
230 | export default Rectangle;
231 |
--------------------------------------------------------------------------------
/src/components/sidebar/shareLinkPopup.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect } from 'react'
2 |
3 | import Button from 'components/common/button'
4 | import LinkIcon from 'assets/link_white.svg'
5 | import CopyIcon from 'assets/copy.svg'
6 |
7 | const ShareLinkPopup = ({}) => {
8 | const refNode = useRef(null)
9 | const [showLink, setShowLink] = useState(false)
10 |
11 | useEffect(() => {
12 | document.addEventListener('mousedown', handleClick, false)
13 |
14 | // when component will unmount
15 | return () => {
16 | document.removeEventListener('mousedown', handleClick, false)
17 | }
18 | }, [])
19 |
20 | const handleClick = (e) => {
21 | if (refNode && refNode.current.contains(e.target)) {
22 | return
23 | }
24 |
25 | setShowLink(false)
26 | }
27 |
28 | return (
29 | <>
30 |
83 | >
84 | )
85 | }
86 |
87 | export default ShareLinkPopup
88 |
--------------------------------------------------------------------------------
/src/components/sidebar/sidebar.css:
--------------------------------------------------------------------------------
1 | .secondary-sidebar-content .element-image-block {
2 | min-height: 100px;
3 | max-height: 100px;
4 | }
5 |
6 | .secondary-sidebar-content {
7 | font-weight: 600;
8 | }
9 |
10 | .tooltip-child {
11 | opacity: 0; /* define initial transition property */
12 | -webkit-transition: all 0.2s ease-in-out; /* define transitions */
13 | transition: all 0.2s ease-in-out;
14 | z-index: -1;
15 | }
16 |
17 | .tooltip-parent:hover .tooltip-child {
18 | opacity: 1;
19 | z-index: 1;
20 | }
21 |
--------------------------------------------------------------------------------
/src/components/sidebar/tempPopup.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect } from 'react'
2 |
3 | import Button from 'components/common/button'
4 | import LinkIcon from 'assets/link_white.svg'
5 | import CopyIcon from 'assets/copy.svg'
6 |
7 | const ShareLinkPopup = ({}) => {
8 | const refNode = useRef(null)
9 | const [showLink, setShowLink] = useState(false)
10 |
11 | useEffect(() => {
12 | document.addEventListener('mousedown', handleClick, false)
13 |
14 | // when component will unmount
15 | return () => {
16 | document.removeEventListener('mousedown', handleClick, false)
17 | }
18 | }, [])
19 |
20 | const handleClick = (e) => {
21 | if (refNode && refNode.current.contains(e.target)) {
22 | return
23 | }
24 |
25 | setShowLink(false)
26 | }
27 |
28 | return (
29 | <>
30 |
82 | >
83 | )
84 | }
85 |
86 | export default ShareLinkPopup
87 |
--------------------------------------------------------------------------------
/src/components/sidebar/userDetailsPopup.js:
--------------------------------------------------------------------------------
1 | import React, { useRef, useState, useEffect } from 'react'
2 |
3 | import Button from 'components/common/button'
4 | import LinkIcon from 'assets/link_white.svg'
5 | import CopyIcon from 'assets/copy.svg'
6 |
7 | const randomBgColors = [
8 | '#BF2600',
9 | '#FF8B00',
10 | '#006644',
11 | '#008DA6',
12 | '#0747A6',
13 | '#403294',
14 | '#091E42',
15 | '#FF5630',
16 | '#FFAB00',
17 | '#36B37E',
18 | '#00B8D9',
19 | '#0065FF',
20 | '#6554C0',
21 | ]
22 |
23 | const UserDetailsPopup = ({}) => {
24 | const refNode = useRef(null)
25 | const [showLink, setShowLink] = useState(false)
26 |
27 | useEffect(() => {
28 | document.addEventListener('mousedown', handleClick, false)
29 |
30 | // when component will unmount
31 | return () => {
32 | document.removeEventListener('mousedown', handleClick, false)
33 | }
34 | }, [])
35 |
36 | const handleClick = (e) => {
37 | if (refNode && refNode.current.contains(e.target)) {
38 | return
39 | }
40 |
41 | setShowLink(false)
42 | }
43 |
44 | return (
45 | <>
46 |
47 |
{
50 | e.stopPropagation()
51 | setShowLink(!showLink)
52 | }}
53 | >
54 |
60 | M
61 |
62 |
68 | A
69 |
70 |
71 |
72 |
79 |
85 |
86 |
92 | M
93 |
94 |
95 | Meet Zaveri (You)
96 |
97 |
98 |
99 |
100 |
106 | A
107 |
108 |
109 | Arnub G.
110 |
111 |
112 |
113 |
114 |
115 | >
116 | )
117 | }
118 |
119 | export default UserDetailsPopup
120 |
--------------------------------------------------------------------------------
/src/components/utils/borderStyleBox.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { allColorShades, essentialShades } from 'utils/constants'
5 |
6 | import { motion } from 'framer-motion'
7 | import ColorPicker from './colorPicker'
8 |
9 | const BorderStyleBoxContainer = styled.div`
10 | width: 250px;
11 | margin: 0 auto;
12 | height: auto;
13 | background: transparent;
14 | text-align: left;
15 | `
16 |
17 | const BorderStyleBox = ({
18 | currentType,
19 | currentWidth,
20 | currentColor,
21 | onChangeColor,
22 | onChangeBorderWidth,
23 | }) => {
24 | const renderBorderType = () => {
25 | const types = [
26 | { value: 'solid', display: '—' },
27 | { value: 'dashed', display: '- -' },
28 | ].map((type, index) => {
29 | return (
30 |
31 |
43 |
44 | )
45 | })
46 | return types
47 | }
48 |
49 | const renderBorderWidths = () => {
50 | const widths = [0, 1, 2, 4, 6].map((width, index) => {
51 | return (
52 |
53 |
63 |
64 | )
65 | })
66 | return widths
67 | }
68 |
69 | return (
70 |
71 |
72 |
73 |
78 | {renderBorderType()}
79 |
80 |
81 |
82 |
87 | {renderBorderWidths()}
88 |
89 |
90 | {
94 | onChangeColor(color)
95 | }}
96 | />
97 |
98 |
99 | )
100 | }
101 |
102 | // BorderStyleBox.defaultProps = {
103 | // currentType: 'solid',
104 | // currentWidth: '',
105 | // }
106 |
107 | export default BorderStyleBox
108 |
--------------------------------------------------------------------------------
/src/components/utils/colorPicker.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { allColorShades, essentialShades } from 'utils/constants'
5 | import Icon from 'icons/icon'
6 | import { motion, AnimatePresence } from 'framer-motion'
7 |
8 | const ColorPickerContainer = styled.div`
9 | width: 250px;
10 | margin: 0 auto;
11 | height: auto;
12 | background: transparent;
13 | text-align: left;
14 | `
15 |
16 | const ColorSelector = styled(motion.button)`
17 | border-radius: 50%;
18 | ${(props) =>
19 | props.colorpaint &&
20 | css`
21 | background: ${props.colorpaint};
22 | border: ${props.colorpaint === `#FFFFFF`
23 | ? `1px solid #000`
24 | : `1px solid transparent`};
25 | `}
26 | `
27 |
28 | const CurrentColorIndicator = styled.div`
29 | border-radius: 50%;
30 | ${(props) =>
31 | props.colorpaint &&
32 | css`
33 | background: ${props.colorpaint};
34 | `};
35 | `
36 |
37 | const ColorPicker = ({ title, onChangeComplete, currentColor }) => {
38 | const [showAllColors, ToggleShowAllColors] = useState(false)
39 |
40 | const renderColorBtns = () => {
41 | const colorsArr = showAllColors ? allColorShades : essentialShades
42 | const showColors = colorsArr.map((color, index) => {
43 | return (
44 |
45 |
46 |
53 | {
67 | onChangeComplete(color)
68 | }}
69 | />
70 |
71 |
72 |
73 | )
74 | })
75 | return showColors
76 | }
77 |
78 | return (
79 |
80 | {title && {title}
}
81 |
86 | {/*
87 |
88 | {' '}
89 | {
99 | // onChangeComplete(color);
100 | // }}
101 | />
102 |
103 |
116 |
*/}
117 |
118 |
119 |
120 | {renderColorBtns()}{' '}
121 |
129 |
130 |
131 |
132 |
133 | )
134 | }
135 |
136 | // ColorPicker.defaultProps = {
137 | // leftPos: 4,
138 | // currentColor: '#fff',
139 | // }
140 |
141 | ColorPicker.propTypes = {
142 | leftPos: PropTypes.number,
143 | currentColor: PropTypes.string,
144 | onChangeComplete: PropTypes.func,
145 | }
146 |
147 | export default ColorPicker
148 |
--------------------------------------------------------------------------------
/src/components/utils/dragger.js:
--------------------------------------------------------------------------------
1 | import Two from 'two.js'
2 | import { ZUI } from 'two.js/extras/jsm/zui'
3 |
4 | import { offsetHeight } from 'constants/misc'
5 |
6 | function handleDrag(twoJSInstance, group, el, cb) {
7 | console.log('group scene', group, twoJSInstance.scene)
8 | // zuifn()
9 |
10 | let domElement = group._renderer.elem
11 | let zui = new ZUI(group, domElement)
12 | document.getElementById(group.id).setAttribute('class', ' dragger-picker')
13 | let mouse = new Two.Vector(group.translation.x, group.translation.y)
14 | let touches = {}
15 | let distance = 0
16 | var dragging = false
17 |
18 | zui.addLimits(0.06, 8)
19 | twoJSInstance.update()
20 | // zui.addLimits(0.06, 8)
21 | console.log('mouse vector', mouse)
22 | domElement.addEventListener('mousedown', mousedown, false)
23 | // domElement.addEventListener('mousewheel', mousewheel, false)
24 | // domElement.addEventListener('wheel', mousewheel, false)
25 |
26 | // domElement.addEventListener('touchstart', touchstart, false)
27 | // domElement.addEventListener('touchmove', touchmove, false)
28 | // domElement.addEventListener('touchend', touchend, false)
29 | // domElement.addEventListener('touchcancel', touchend, false)
30 |
31 | function mousedown(e) {
32 | // console.log(
33 | // 'mouse event 1',
34 | // // e,
35 | // // mouse,
36 | // // group.translation,
37 | // twoJSInstance.scene.translation
38 | // )
39 | var rect = group.getBoundingClientRect()
40 | dragging =
41 | mouse.x > rect.left &&
42 | mouse.x < rect.right &&
43 | mouse.y > rect.top &&
44 | mouse.y < rect.bottom
45 | mouse.x = e.clientX
46 | mouse.y = e.clientY
47 | window.addEventListener('mousemove', mousemove, false)
48 | window.addEventListener('mouseup', mouseup, false)
49 | }
50 |
51 | function mousemove(e) {
52 | const displacementX = parseInt(localStorage.getItem('displacement_x'))
53 | const displacementY = parseInt(localStorage.getItem('displacement_y'))
54 |
55 | let dx = e.clientX - mouse.x
56 | let dy = e.clientY - mouse.y
57 | console.log(
58 | 'mouse event 2',
59 | e,
60 | group.getBoundingClientRect(),
61 | group.getBoundingClientRect(true),
62 | displacementX,
63 | displacementY
64 | // twoJSInstance.scene.translation.x,
65 | // scale
66 | )
67 | // console.log('mouse event 2 domEle', domElement.getBoundingClientRect())
68 | // console.log('mouse event 2 group', group.getBoundingClientRect())
69 | // zui.translateSurface(dx, dy)
70 | // mouse.set(e.clientX, e.clientY)
71 | // group.translation.set(
72 | // e.clientX - displacementX,
73 | // e.clientY - displacementY
74 | // )
75 | if (dragging) {
76 | group.position.x += dx / 1
77 | group.position.y += dy / 1
78 | } else {
79 | zui.translateSurface(dx, dy)
80 | }
81 |
82 | twoJSInstance.update()
83 | cb && cb()
84 | }
85 |
86 | function mouseup(e) {
87 | let scale = twoJSInstance.scene.scale
88 | // console.log('mouse event 3', e)
89 |
90 | // setting final data into LS cache
91 | localStorage.setItem(
92 | `${el}_coordX`,
93 | parseInt(e.clientX) + scale * twoJSInstance.scene.translation.x
94 | )
95 | localStorage.setItem(
96 | `${el}_coordY`,
97 | parseInt(
98 | e.clientY +
99 | offsetHeight +
100 | scale * twoJSInstance.scene.translation.y
101 | )
102 | )
103 |
104 | window.removeEventListener('mousemove', mousemove, false)
105 | window.removeEventListener('mouseup', mouseup, false)
106 | twoJSInstance.update()
107 | }
108 |
109 | // function mousewheel(e) {
110 | // let dy = (e.wheelDeltaY || -e.deltaY) / 1000
111 | // zui.zoomBy(dy, e.clientX, e.clientY)
112 | // two.update()
113 | // }
114 |
115 | return {
116 | mousemove: mousemove,
117 | mouseup: mouseup,
118 | }
119 | }
120 |
121 | export default handleDrag
122 |
--------------------------------------------------------------------------------
/src/components/utils/editWrapper.js:
--------------------------------------------------------------------------------
1 | import ObjectSelector from "components/utils/objectSelector";
2 | import ToolBar from "components/utils/toolbarConnector";
3 |
4 | const getEditComponents = (two, group, constant1) => {
5 | const selector = new ObjectSelector(two, group, 0, 0, 0, 0, constant1);
6 | selector.create();
7 |
8 | const toolbar = new ToolBar();
9 | toolbar.create();
10 |
11 | return { selector, toolbar };
12 | };
13 |
14 | export default getEditComponents;
15 |
--------------------------------------------------------------------------------
/src/components/utils/loader.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const BounceLoader = () => {
4 | return
5 | }
6 | export default BounceLoader
7 |
--------------------------------------------------------------------------------
/src/components/utils/objectSelector.js:
--------------------------------------------------------------------------------
1 | import Two from 'two.js'
2 |
3 | export default class Selector {
4 | constructor(instance, group, x1, x2, y1, y2, showCircles) {
5 | this.two = instance
6 | this.group = group
7 | this.area = null
8 | this.circle1 = null
9 | this.circle2 = null
10 | this.circle3 = null
11 | this.circle4 = null
12 | this.circleGroup = null
13 | this.showCircles = showCircles
14 | this.vertices = {
15 | x1,
16 | x2,
17 | y1,
18 | y2,
19 | }
20 | }
21 | create() {
22 | const { x1, x2, y1, y2 } = this.vertices
23 | // console.log("vertices", this.two, x1, x2, y1, y2);
24 |
25 | const area = this.two.makePath(x1, y1, x2, y1, x2, y2, x1, y2)
26 | area.fill = 'rgba(0,0,0,0)'
27 | area.opacity = 1
28 | area.linewidth = 2
29 | area.dashes[0] = 6
30 | area.stroke = '#505F79'
31 | // area.curved = true;
32 | // console.log("area", area);
33 | this.area = area
34 |
35 | let circleGroup = null
36 | if (this.showCircles) {
37 | // console.log("show circles 1", this.showCircles);
38 | switch (this.showCircles) {
39 | case 2:
40 | // console.log("falls in case 2");
41 | const yAxisMidpoint = (y1 + y2) / 2
42 | const circleLeft = this.two.makeCircle(x1, yAxisMidpoint, 3)
43 | const circleRight = this.two.makeCircle(
44 | x2,
45 | yAxisMidpoint,
46 | 3
47 | )
48 | // const circleGroup = this.two.makeGroup(circle1, circle2, circle3, circle4);
49 | this.circle1 = circleLeft
50 | this.circle2 = circleRight
51 | this.circle3 = null
52 | this.circle4 = null
53 | circleGroup = this.two.makeGroup(circleLeft, circleRight)
54 | // circleGroup.opacity = 0;
55 | this.circleGroup = circleGroup
56 |
57 | case 4:
58 | const circle1 = this.two.makeCircle(x1, y1, 4)
59 | const circle2 = this.two.makeCircle(x2, y1, 4)
60 | const circle3 = this.two.makeCircle(x2, y2, 4)
61 | const circle4 = this.two.makeCircle(x1, y2, 4)
62 | // const circleGroup = this.two.makeGroup(circle1, circle2, circle3, circle4);
63 | this.circle1 = circle1
64 | this.circle2 = circle2
65 | this.circle3 = circle3
66 | this.circle4 = circle4
67 | circleGroup = this.two.makeGroup(
68 | circle1,
69 | circle2,
70 | circle3,
71 | circle4
72 | )
73 | circleGroup.linewidth = 1.5
74 | circleGroup.opacity = 0
75 | this.circleGroup = circleGroup
76 | break
77 |
78 | default:
79 | break
80 | }
81 | }
82 |
83 | const areaGroup = this.two.makeGroup(area, circleGroup)
84 |
85 | this.areaGroup = areaGroup
86 | this.group.add(areaGroup)
87 | this.two.update()
88 |
89 | const clearSelector = () => {
90 | this.areaGroup.opacity = 0
91 | }
92 | window.addEventListener('clearSelector', clearSelector, false)
93 | }
94 |
95 | show() {
96 | this.areaGroup.opacity = 1
97 | }
98 |
99 | hide() {
100 | this.areaGroup.opacity = 0
101 | }
102 |
103 | getInstance() {
104 | return this.areaGroup.id
105 | }
106 |
107 | update(x1, x2, y1, y2) {
108 | // console.log("on selector update", x1, x2, y1, y2);
109 | this.vertices = {
110 | x1,
111 | x2,
112 | y1,
113 | y2,
114 | }
115 |
116 | this.area.vertices = [
117 | new Two.Anchor(x1, y1, null, null, null, null, Two.Commands.line),
118 | new Two.Anchor(x2, y1, null, null, null, null, Two.Commands.line),
119 |
120 | new Two.Anchor(x2, y2, null, null, null, null, Two.Commands.line),
121 | new Two.Anchor(x1, y2, null, null, null, null, Two.Commands.line),
122 | ]
123 |
124 | if (this.showCircles) {
125 | this.circleGroup.opacity = 1
126 | // console.log("show circles 2", this.showCircles);
127 | switch (this.showCircles) {
128 | case 2:
129 | const yAxisMidpoint = (y1 + y2) / 2
130 | this.circle1.translation.set(x1, yAxisMidpoint)
131 | this.circle2.translation.set(x2, yAxisMidpoint)
132 | break
133 | case 4:
134 | this.circle1.translation.set(x1, y1)
135 | this.circle2.translation.set(x2, y1)
136 | this.circle3.translation.set(x2, y2)
137 | this.circle4.translation.set(x1, y2)
138 | break
139 | default:
140 | break
141 | }
142 | }
143 |
144 | this.areaGroup.opacity = 1
145 | this.two.update()
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/components/utils/opacitySlider.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useState, useRef } from 'react'
2 | import PropTypes from 'prop-types'
3 | import styled, { css } from 'styled-components'
4 | import { useRanger, Ranger } from '@tanstack/react-ranger'
5 | import { motion } from 'framer-motion'
6 |
7 | const SliderContainer = styled.div`
8 | width: 250px;
9 | height: 44px;
10 | border-radius: 6px;
11 | `
12 |
13 | const Track = styled('div')`
14 | display: inline-block;
15 | height: 4px;
16 | width: 90%;
17 | margin: 0;
18 | `
19 |
20 | const Tick = styled('div')`
21 | :before {
22 | content: '';
23 | position: absolute;
24 | left: 0px;
25 | background: #0052cc;
26 | height: 6px;
27 | width: 5px;
28 | border-radius: 50%;
29 | top: -2px;
30 | }
31 | `
32 |
33 | const TickLabel = styled('div')`
34 | position: absolute;
35 | font-size: 0.6rem;
36 | color: rgba(0, 0, 0, 0.5);
37 | top: 100%;
38 | transform: translate(-50%, 1.2rem);
39 | white-space: nowrap;
40 | `
41 |
42 | const Segment = styled('div')`
43 | background: ${(props) =>
44 | props.index === 0
45 | ? '#0052CC'
46 | : props.index === 1
47 | ? '#0052CC'
48 | : props.index === 2
49 | ? '#f5c200'
50 | : '#ff6050'};
51 | height: 50%;
52 | `
53 |
54 | const Handle = styled('div')`
55 | position: absolute;
56 | top: -7px;
57 | background: #0052cc;
58 | width: 0.8rem;
59 | height: 0.8rem;
60 | border-radius: 100%;
61 | left: -2px;
62 | cursor: pointer;
63 | `
64 |
65 | const OpacitySlider = ({
66 | title,
67 | handleOnChange,
68 | handleOnDrag,
69 | currentOpacity,
70 | }) => {
71 | // const [values, setValues] = React.useState([1])
72 | const [values, setValues] = useState([1])
73 | const rangerRef = useRef(null)
74 |
75 | const rangerInstance = useRanger({
76 | getRangerElement: () => rangerRef.current,
77 | values,
78 | min: 0,
79 | max: 1,
80 | stepSize: 0.1,
81 | onDrag: (instance) => {
82 | setValues(instance.sortedValues)
83 | handleOnDrag(instance.sortedValues)
84 | },
85 | onChange: (instance) => handleOnChange(instance.sortedValues),
86 | })
87 |
88 | // const { getTrackProps, ticks, segments, handles } = useRanger({
89 | // values,
90 | // onDrag: (e) => {
91 | // console.log('on drag ranger', e)
92 | // setValues(e)
93 | // handleOnDrag(e)
94 | // },
95 | // onChange: (e) => {
96 | // console.log('on change ranger', e)
97 | // setValues(e)
98 | // handleOnChange(e)
99 | // },
100 | // min: 0,
101 | // max: 1,
102 | // steps: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
103 | // ticks: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9],
104 | // })
105 |
106 | useEffect(() => {
107 | let arr = currentOpacity ? [currentOpacity] : [0]
108 | setValues(arr)
109 | }, [])
110 |
111 | return (
112 |
113 |
117 | {title && {title}
}
118 | {/* */}
150 |
162 | {rangerInstance
163 | .handles()
164 | .map(
165 | (
166 | {
167 | value,
168 | onKeyDownHandler,
169 | onMouseDownHandler,
170 | onTouchStart,
171 | isActive,
172 | },
173 | i
174 | ) => (
175 |
201 | )
202 | )}
203 |
204 |
205 |
206 | )
207 | }
208 |
209 | // OpacitySlider.defaultProps = {
210 | // currentOpacity: 1,
211 | // }
212 |
213 | OpacitySlider.propTypes = {
214 | currentOpacity: PropTypes.number,
215 | handleOnChange: PropTypes.func,
216 | }
217 |
218 | export default OpacitySlider
219 |
--------------------------------------------------------------------------------
/src/components/utils/toolbarConnector.js:
--------------------------------------------------------------------------------
1 | import { properties } from "utils/constants";
2 |
3 | export default class ToolBar {
4 | constructor() {
5 | this.toolBarDOM = document.getElementById("floating-toolbar");
6 | }
7 | create_color_bg() {
8 | // contains DOM creation and styling part
9 | }
10 | create_color_text() {
11 | // contains DOM creation and styling part
12 | }
13 | create_color_icon() {
14 | // contains DOM creation and styling part
15 | }
16 | create_font_size() {
17 | // contains DOM creation and styling part
18 | }
19 | create_font_weight() {
20 | // contains DOM creation and styling part
21 | }
22 | create_alignment() {
23 | // contains DOM creation and styling part
24 | }
25 | create_border_color() {
26 | // contains DOM creation and styling part
27 | }
28 | create_border_width() {
29 | // contains DOM creation and styling part
30 | }
31 | create_link_url() {
32 | // contains DOM creation and styling part
33 | }
34 | create_opacity() {
35 | // contains DOM creation and styling part
36 | }
37 | create() {
38 | // create bare bones for toolbar
39 | // this.toolBarDOM.style.left = "500px";
40 | // this.toolBarDOM.style.top = "300px";
41 | }
42 | hide() {
43 | // this.toolBarDOM.hidden = true;
44 | }
45 | forceHide(callback) {
46 | // this.toolBarDOM.hidden = true;
47 | // this.toolBarDOM.removeEventListener("blur", callback);
48 | }
49 | show() {
50 | // this.toolBarDOM.style.visibility = "visible";
51 | // console.log("on show toolbar connector");
52 | // this.toolBarDOM.hidden = false;
53 | }
54 | shift(pageX, pageY) {
55 | // this.toolBarDOM.style.left = `${pageX}px`;
56 | // this.toolBarDOM.style.top = `${
57 | // pageY - this.toolBarDOM.getBoundingClientRect().height
58 | // }px`;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/utils/zoomer.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { useImmer } from 'use-immer'
3 |
4 | import AddSVG from 'assets/add_icon_only.svg'
5 | import SubtractSVG from 'assets/subtract.svg'
6 |
7 | const CanvasZoomer = (props) => {
8 | const [scale, setScale] = useState(1)
9 |
10 | const handleZoomIn = (e) => {
11 | console.log('props.scene', props.scene)
12 | setScale(scale + 0.2)
13 | props.sceneInstance.scene.scale = scale + 0.2
14 | props.sceneInstance.update()
15 | }
16 |
17 | const handleZoomOut = (e) => {
18 | console.log('props.scene', props.scene)
19 | setScale(scale - 0.2)
20 | props.sceneInstance.scene.scale = scale - 0.2
21 | props.sceneInstance.update()
22 | }
23 |
24 | return (
25 |
26 |
40 |
41 | )
42 | }
43 |
44 | export default CanvasZoomer
45 |
--------------------------------------------------------------------------------
/src/constants/elementSchema.js:
--------------------------------------------------------------------------------
1 | const avatar = {
2 | id: '',
3 | name: 'avatar',
4 | width: 0,
5 | height: 0,
6 | x: 0,
7 | y: 0,
8 | icon_name: '',
9 | bg_color: '',
10 | icon_color: '',
11 | }
12 |
13 | const button = {
14 | id: '',
15 | name: 'button',
16 | width: 0,
17 | height: 0,
18 | x: 0,
19 | y: 0,
20 | bg_color: '',
21 | text: '',
22 | text_color: '',
23 | }
24 |
25 | const buttonWithIcon = {
26 | id: '',
27 | name: 'buttonWithIcon',
28 | width: 0,
29 | height: 0,
30 | x: 0,
31 | y: 0,
32 | icon_name: '',
33 | bg_color: '',
34 | icon_color: '',
35 | text: '',
36 | text_color: '',
37 | }
38 |
39 | const checkbox = {
40 | id: '',
41 | name: 'checkbox',
42 | width: 0,
43 | height: 0,
44 | x: 0,
45 | y: 0,
46 | text_arr: [],
47 | checked_indices: [],
48 | theme_color: '',
49 | }
50 |
51 | const radiobox = {
52 | id: '',
53 | name: 'radiobox',
54 | width: 0,
55 | height: 0,
56 | x: 0,
57 | y: 0,
58 | text_arr: [],
59 | checked_indices: [],
60 | theme_color: '',
61 | }
62 |
63 | const circle = {
64 | id: '',
65 | name: 'circle',
66 | width: 0,
67 | height: 0,
68 | x: 0,
69 | y: 0,
70 | bg_color: '',
71 | }
72 |
73 | const divider = {
74 | id: '',
75 | name: 'divider',
76 | width: 0,
77 | height: 0,
78 | x: 0,
79 | y: 0,
80 | start_pos: '',
81 | end_pos: '',
82 | stroke_color: '',
83 | }
84 |
85 | const imageCard = {
86 | id: '',
87 | name: 'imageCard',
88 | width: 0,
89 | height: 0,
90 | x: 0,
91 | y: 0,
92 | icon_name: '',
93 | bg_color: '',
94 | icon_color: '',
95 | }
96 |
97 | const linkWithIcon = {
98 | id: '',
99 | name: 'linkWithIcon',
100 | width: 0,
101 | height: 0,
102 | x: 0,
103 | y: 0,
104 | icon_name: '',
105 | icon_color: '',
106 | text_color: '',
107 | text: '',
108 | }
109 |
110 | const overlay = {
111 | id: '',
112 | name: 'overlay',
113 | width: 0,
114 | height: 0,
115 | x: 0,
116 | y: 0,
117 | bg_color: '',
118 | opacity: '',
119 | }
120 |
121 | const rectangle = {
122 | id: '',
123 | name: 'rectangle',
124 | width: 0,
125 | height: 0,
126 | x: 0,
127 | y: 0,
128 | bg_color: '',
129 | }
130 |
131 | const text = {
132 | id: '',
133 | name: 'text',
134 | width: 0,
135 | height: 0,
136 | x: 0,
137 | y: 0,
138 | text: '',
139 | text_color: '',
140 | }
141 |
142 | const textarea = {
143 | id: '',
144 | name: 'textarea',
145 | width: 0,
146 | height: 0,
147 | x: 0,
148 | y: 0,
149 | text: '',
150 | text_color: '',
151 | }
152 |
153 | const textinput = {
154 | id: '',
155 | name: 'textinput',
156 | width: 0,
157 | height: 0,
158 | x: 0,
159 | y: 0,
160 | text: '',
161 | text_color: '',
162 | }
163 |
164 | const toggle = {
165 | id: '',
166 | name: 'toggle',
167 | width: 0,
168 | height: 0,
169 | x: 0,
170 | y: 0,
171 | actives: false,
172 | theme_color: '',
173 | }
174 |
--------------------------------------------------------------------------------
/src/constants/exportHooks.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useMediaQuery } from 'react-responsive'
3 |
4 | export const useMediaQueryUtils = () => {
5 | const isDesktop = useMediaQuery({ minWidth: 1280, maxWidth: 1535 })
6 | const isLaptop = useMediaQuery({ minWidth: 1024, maxWidth: 1279 })
7 | const isTablet = useMediaQuery({ minWidth: 768, maxWidth: 1023 })
8 | const isMobile = useMediaQuery({ maxWidth: 767 })
9 |
10 | return {
11 | isDesktop,
12 | isTablet,
13 | isMobile,
14 | isLaptop,
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/constants/misc.js:
--------------------------------------------------------------------------------
1 | export const offsetHeight = 0
2 | export const GROUP_COMPONENT = 'groupobject'
3 |
--------------------------------------------------------------------------------
/src/constants/properties.js:
--------------------------------------------------------------------------------
1 | export const WIDTH = 'width'
2 | export const HEIGHT = 'height'
3 | export const TYPE = 'type'
4 | export const STROKE = 'stroke'
5 | export const FILL = 'fill'
6 | export const OPACITY = 'opacity'
7 | export const ROTATION = 'rotation'
8 | export const SCALE = 'scale'
9 | export const TRANSLATION = 'translation'
10 |
--------------------------------------------------------------------------------
/src/factory/arrowLine.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class ArrowLineFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill, x1, x2, y1, y2 } = this.properties
9 |
10 | // console.log('arrow line factory x1, y1, x2, y2', x1, y1, x2, y2)
11 | let line = two.makeArrow(x1, y1, x2, y2)
12 | line.linewidth = 2
13 | // line.vertices[1].y = 200
14 | // const centerPointCircle = two.makeEllipse(0, 0, 5, 5)
15 | // centerPointCircle.fill = '#FFF'
16 | // centerPointCircle.stroke = '#0052CC'
17 | // centerPointCircle.linewidth = 2
18 |
19 | const pointCircle1 = two.makeEllipse(0, 0, 5, 5)
20 | pointCircle1.fill = '#FFF'
21 | pointCircle1.stroke = '#0052CC'
22 | pointCircle1.linewidth = 2
23 | // pointCircle1.noStroke()
24 |
25 | const pointCircle2 = two.makeEllipse(0, 0, 5, 5)
26 | pointCircle2.fill = '#FFF'
27 | pointCircle2.stroke = '#0052CC'
28 | pointCircle2.linewidth = 2
29 | // pointCircle2.noStroke()
30 |
31 | // const resizeLine = two.makeGroup(pointCircle1, pointCircle2)
32 | // resizeLine.translation.y = 0 - line.linewidth
33 | // resizeLine.opacity = 0
34 |
35 | let pointCircle1Group = two.makeGroup(pointCircle1)
36 |
37 | let pointCircle2Group = two.makeGroup(pointCircle2)
38 |
39 | let group = two.makeGroup(
40 | line,
41 | pointCircle1Group,
42 | pointCircle2Group
43 | // centerPointCircle
44 | )
45 | // console.log('main group', group.getBoundingClientRect())
46 |
47 | // Overriding the circle point group's coordinate and
48 | // manipulating it with line's coordinate
49 | pointCircle1Group.translation.x = line.vertices[0].x
50 | pointCircle1Group.translation.y = line.vertices[0].y
51 | pointCircle2Group.translation.x = line.vertices[1].x
52 | pointCircle2Group.translation.y = line.vertices[1].y
53 | pointCircle1Group.opacity = 0
54 | pointCircle2Group.opacity = 0
55 | // const calcX = parseInt(prevX) + (parseInt(rectangle.width / 2) - 10);
56 | // const calcY = parseInt(prevY) - (parseInt(46) - parseInt(rectangle.height / 2));
57 |
58 | group.translation.x = parseInt(prevX)
59 | group.translation.y = parseInt(prevY)
60 |
61 | // centerPointCircle.translation.x = line.translation.x
62 | // centerPointCircle.translation.y = line.translation.y
63 |
64 | return {
65 | group,
66 | pointCircle1Group,
67 | pointCircle2Group,
68 | pointCircle1,
69 | pointCircle2,
70 | line,
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/factory/avatar.js:
--------------------------------------------------------------------------------
1 | import Icon from 'icons/icons'
2 | import Main from './main'
3 | import { color_blue } from 'utils/constants'
4 |
5 | export default class AvatarFactory extends Main {
6 | createElement() {
7 | const two = this.two
8 | const prevX = this.x
9 | const prevY = this.y
10 | const {
11 | width = 70,
12 | height = 70,
13 | radius,
14 | fill = color_blue,
15 |
16 | iconStroke,
17 | stroke,
18 | linewidth,
19 | children = {},
20 | } = this.properties
21 |
22 | const circle = two.makeCircle(0, 0, 0)
23 | circle.width = width
24 | circle.height = height
25 | circle.radius = parseInt(width / 2)
26 | circle.fill = fill
27 |
28 | circle.stroke = stroke ? stroke : '#fff'
29 | circle.linewidth = linewidth ? linewidth : 0
30 |
31 | let iconType = children?.icon?.iconType
32 | ? children?.icon?.iconType
33 | : 'ICON_IMAGE_AVATAR_WHITE'
34 | // creates svg with proper template
35 | const svgImage = new DOMParser().parseFromString(
36 | Icon[iconType].data,
37 | 'text/xml'
38 | )
39 |
40 | const externalSVG = two.interpret(svgImage.firstChild)
41 | // console.log("svgImage", svgImage, circle.width / 2);
42 | externalSVG.scale = children?.icon?.iconScale
43 | ? children?.icon?.iconScale
44 | : 1
45 | externalSVG.stroke = iconStroke ? iconStroke : '#fff'
46 | externalSVG.fill = 'transparent'
47 |
48 | const externalSVGGroup = two.makeGroup(externalSVG)
49 | externalSVGGroup.center()
50 | const circleSvgGroup = two.makeGroup(circle, externalSVGGroup)
51 |
52 | const group = two.makeGroup(circleSvgGroup)
53 | group.center()
54 | group.translation.x = parseInt(prevX)
55 | group.translation.y = parseInt(prevY)
56 |
57 | return { group, circleSvgGroup, circle, externalSVG, externalSVGGroup }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/factory/button.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 | import { color_blue } from 'utils/constants'
3 |
4 | export default class ButtonFactory extends Main {
5 | createElement() {
6 | const two = this.two
7 | const prevX = this.x
8 | const prevY = this.y
9 | const {
10 | width = 70,
11 | height = 70,
12 | fill = color_blue,
13 | textColor,
14 | stroke,
15 | linewidth,
16 | children = {},
17 | } = this.properties
18 |
19 | const rectangle = two.makeRoundedRectangle(0, 0, 140, 45, 5)
20 | rectangle.width = width
21 | rectangle.height = height
22 | rectangle.fill = fill
23 |
24 | rectangle.stroke = stroke ? stroke : '#fff'
25 | rectangle.linewidth = linewidth ? linewidth : 0
26 |
27 | const text = two.makeText('Button', 10, 0)
28 |
29 | text.value = children?.text?.value || 'Button'
30 | text.size = children?.text?.size || '16'
31 | text.fill = textColor || '#fff'
32 | text.weight = children?.text?.weight || '500'
33 |
34 | const textGroup = two.makeGroup(text)
35 | textGroup.center()
36 | const rectGroup = two.makeGroup(rectangle)
37 | const rectTextGroup = two.makeGroup(rectGroup, textGroup)
38 |
39 | const group = two.makeGroup(rectTextGroup)
40 | group.translation.x = parseInt(prevX)
41 | group.translation.y = parseInt(prevY)
42 |
43 | return { group, rectTextGroup, text, textGroup, rectangle }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/factory/buttonwithicon.js:
--------------------------------------------------------------------------------
1 | import Icon from 'icons/icons'
2 | import Main from './main'
3 | import { color_blue } from 'utils/constants'
4 |
5 | export default class ButtonWithIconFactory extends Main {
6 | createElement() {
7 | const two = this.two
8 | const prevX = this.x
9 | const prevY = this.y
10 | const {
11 | width = 70,
12 | height = 70,
13 | fill = color_blue,
14 | textColor,
15 | stroke,
16 | linewidth,
17 | children = {},
18 | } = this.properties
19 |
20 | // Implement core element
21 |
22 | const text = two.makeText('Button', -15, 0)
23 | text.value = children?.text?.value || 'Button'
24 | text.size = children?.text?.size || '16'
25 | text.fill = textColor || '#fff'
26 | text.weight = children?.text?.weight || '500'
27 | // text.baseline = "sub";
28 | text.alignment = 'right'
29 |
30 | let iconType = children?.icon?.iconType
31 | ? children?.icon?.iconType
32 | : 'ICON_IMAGE_PHONE_WHITE'
33 |
34 | // Implement custom svg
35 | const svgImage = new DOMParser().parseFromString(
36 | Icon[iconType].data,
37 | 'text/xml'
38 | )
39 | // console.log("svgImage", svgImage);
40 | const externalSVG = two.interpret(svgImage.firstChild)
41 | // externalSVG.translation.x = -10
42 | // externalSVG.translation.y = -1
43 | externalSVG.scale = children?.icon?.iconScale
44 | ? children?.icon?.iconScale
45 | : 0.8
46 | // externalSVG.center()
47 |
48 | let externalSVGGroup = two.makeGroup(externalSVG)
49 | externalSVGGroup.center()
50 |
51 | let textGroup = two.makeGroup(text)
52 | // textGroup.center()
53 | // console.log("textGroup", textGroup, textGroup.id);
54 |
55 | const textSvgGroup = two.makeGroup(externalSVGGroup, textGroup)
56 | // textSvgGroup.translation.x = -10
57 | textSvgGroup.center()
58 |
59 | const rectangle = two.makeRoundedRectangle(0, 0, 140, 45, 5)
60 | rectangle.width = width
61 | rectangle.height = height
62 | rectangle.fill = fill
63 | if (stroke && linewidth) {
64 | rectangle.stroke = stroke
65 | rectangle.linewidth = linewidth
66 | } else {
67 | rectangle.noStroke()
68 | }
69 |
70 | const rectTextSvgGroup = two.makeGroup(rectangle, textSvgGroup)
71 | // rectTextSvgGroup.center()
72 | rectangle.noStroke()
73 | const group = two.makeGroup(rectTextSvgGroup)
74 |
75 | // group.center()
76 | group.translation.x = parseInt(prevX)
77 | group.translation.y = parseInt(prevY)
78 |
79 | // Implement external layer of rectangle
80 | // const rectangle = two.makePath(
81 | // group.getBoundingClientRect(true).left - 40,
82 | // group.getBoundingClientRect(true).top - 10,
83 |
84 | // group.getBoundingClientRect(true).right + 10,
85 | // group.getBoundingClientRect(true).top - 10,
86 |
87 | // group.getBoundingClientRect(true).right + 10,
88 | // group.getBoundingClientRect(true).bottom + 10,
89 |
90 | // group.getBoundingClientRect(true).left - 40,
91 | // group.getBoundingClientRect(true).bottom + 10
92 | // )
93 | // rectangle.fill = fill
94 |
95 | // rectangle.linewidth = 8
96 | // rectangle.join = 'round'
97 |
98 | // if (stroke) {
99 | // rectangle.stroke = stroke
100 | // // rectangle.linewidth = linewidth
101 | // } else {
102 | // rectangle.noStroke()
103 | // }
104 | return {
105 | group,
106 | text,
107 | rectangle,
108 | textSvgGroup,
109 | externalSVG,
110 | rectTextSvgGroup,
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/factory/circle.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 | import { color_blue } from 'utils/constants'
3 |
4 | export default class CircleFactory extends Main {
5 | createElement() {
6 | const two = this.two
7 | const prevX = this.x
8 | const prevY = this.y
9 | const { fill, width, height, radius, stroke, linewidth } =
10 | this.properties
11 |
12 | // Implement core element
13 | const circle = two.makeEllipse(0, 0, 0, 0)
14 | circle.width = width || 100
15 | circle.height = height || 100
16 | circle.fill = fill ? fill : color_blue
17 |
18 | circle.stroke = stroke ? stroke : '#fff'
19 | circle.linewidth = linewidth ? linewidth : 0
20 |
21 | this.circle = circle
22 |
23 | // Create group and take children elements as a parameter
24 | const group = two.makeGroup(circle)
25 | group.translation.x = parseInt(prevX)
26 | group.translation.y = parseInt(prevY)
27 | this.group = group
28 | // console.log('group.id circle', group.id)
29 | return { group: this.group, circle: this.circle }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/factory/divider.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class DividerFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill, x1, x2, y1, y2 } = this.properties
9 |
10 | let line = two.makeLine(x1, y1, x2, y2)
11 | line.linewidth = 3
12 |
13 | const pointCircle1 = two.makeEllipse(0, 0, 5, 5)
14 | pointCircle1.fill = '#FFF'
15 | pointCircle1.stroke = '#0052CC'
16 | // pointCircle1.noStroke()
17 |
18 | const pointCircle2 = two.makeEllipse(10, 0, 5, 5)
19 | pointCircle2.fill = '#FFF'
20 | pointCircle2.stroke = '#0052CC'
21 | // pointCircle2.noStroke()
22 |
23 | const resizeLine = two.makeGroup(pointCircle1, pointCircle2)
24 | resizeLine.translation.y = 0 - line.linewidth
25 | resizeLine.opacity = 0
26 |
27 | let group = two.makeGroup(line, resizeLine)
28 | console.log('main group', group.getBoundingClientRect())
29 |
30 | // Overriding the circle point group's coordinate and
31 | // manipulating it with line's coordinate
32 | pointCircle1.translation.x = line.vertices[0].x - 0
33 | pointCircle1.translation.y = line.vertices[0].y - 0
34 | pointCircle2.translation.x = line.vertices[1].x + 0
35 | pointCircle2.translation.y = line.vertices[1].y - 0
36 |
37 | // const calcX = parseInt(prevX) + (parseInt(rectangle.width / 2) - 10);
38 | // const calcY = parseInt(prevY) - (parseInt(46) - parseInt(rectangle.height / 2));
39 | group.center()
40 | group.translation.x = parseInt(prevX)
41 | group.translation.y = parseInt(prevY)
42 |
43 | return { group, pointCircle1, pointCircle2, resizeLine, line }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/factory/dropdown.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class DropdownFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill } = this.properties
9 |
10 | // Implement core element
11 | const circle = two.makeEllipse(0, 0, 70, 70)
12 | circle.fill = fill ? fill : '#EBECF0'
13 | circle.noStroke()
14 | this.circle = circle
15 |
16 | // Create group and take children elements as a parameter
17 | const group = two.makeGroup(circle)
18 | group.translation.x = parseInt(prevX)
19 | group.translation.y = parseInt(prevY)
20 | this.group = group
21 | return { group: this.group, circle: this.circle }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/factory/frame.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 | import { color_blue } from 'utils/constants'
3 |
4 | export default class FrameFactory extends Main {
5 | createElement() {
6 | const two = this.two
7 | const prevX = this.x
8 | const prevY = this.y
9 | const { fill, width, height, stroke, linewidth } = this.properties
10 |
11 | // Implement core element
12 |
13 | const rectangle = two.makeRectangle(0, 0, width || 210, height || 110)
14 |
15 | if (stroke && linewidth) {
16 | rectangle.stroke = stroke
17 | rectangle.linewidth = linewidth
18 | } else {
19 | rectangle.stroke = '#000'
20 | rectangle.linewidth = 1
21 | // rectangle.noStroke()
22 | }
23 |
24 | rectangle.fill = 'transparent'
25 | rectangle.stroke = '#000'
26 | rectangle.linewidth = 2
27 |
28 | console.log('rectangle', rectangle.getBoundingClientRect())
29 |
30 | const group = two.makeGroup(rectangle)
31 |
32 | group.translation.x = parseInt(prevX)
33 | group.translation.y = parseInt(prevY)
34 |
35 | return { group, rectangle }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/factory/imagecard.js:
--------------------------------------------------------------------------------
1 | import Icon from 'icons/icons'
2 | import Main from './main'
3 | import { color_blue } from 'utils/constants'
4 |
5 | export default class ImageCardFactory extends Main {
6 | createElement() {
7 | const two = this.two
8 | const prevX = this.x
9 | const prevY = this.y
10 | const {
11 | width = 70,
12 | height = 70,
13 | radius,
14 | fill = color_blue,
15 |
16 | iconStroke,
17 | stroke,
18 | linewidth,
19 | children = {},
20 | } = this.properties
21 |
22 | const rectangle = two.makeRectangle(0, 0, 60, 60)
23 | rectangle.width = width
24 | rectangle.height = height
25 | rectangle.fill = fill
26 |
27 | rectangle.stroke = stroke ? stroke : '#fff'
28 | rectangle.linewidth = linewidth ? linewidth : 0
29 |
30 | let iconType = children?.icon?.iconType
31 | ? children?.icon?.iconType
32 | : 'ICON_IMAGE_AVATAR_WHITE'
33 |
34 | const svgImage = new DOMParser().parseFromString(
35 | Icon[iconType].data,
36 | 'text/xml'
37 | )
38 | // console.log("svgImage", svgImage, rectangle.width / 2);
39 | const externalSVG = two.interpret(svgImage.firstChild)
40 | externalSVG.scale = children?.icon?.iconScale
41 | ? children?.icon?.iconScale
42 | : 1
43 | externalSVG.stroke = iconStroke ? iconStroke : '#fff'
44 |
45 | const externalSVGGroup = two.makeGroup(externalSVG)
46 | externalSVGGroup.center()
47 | const rectSvgGroup = two.makeGroup(rectangle, externalSVGGroup)
48 |
49 | const group = two.makeGroup(rectSvgGroup)
50 | group.center()
51 | group.translation.x = parseInt(prevX)
52 | group.translation.y = parseInt(prevY)
53 |
54 | return { group, rectSvgGroup, externalSVG, externalSVGGroup, rectangle }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/factory/linkwithicon.js:
--------------------------------------------------------------------------------
1 | import Icon from 'icons/icons'
2 | import Main from './main'
3 | import { color_blue } from 'utils/constants'
4 |
5 | export default class LinkWithIconFactory extends Main {
6 | createElement() {
7 | const two = this.two
8 | const prevX = this.x
9 | const prevY = this.y
10 | const {
11 | width = 70,
12 | height = 70,
13 | fill = color_blue,
14 |
15 | stroke,
16 | linewidth,
17 | children = {},
18 | } = this.properties
19 |
20 | // Implement core element
21 | const text = two.makeText('Link', -15, 0)
22 | text.value = children?.text?.value || 'Button'
23 | text.size = children?.text?.size || '16'
24 | text.weight = children?.text?.weight || '500'
25 | text.decoration = 'underline'
26 | text.size = 18
27 | // text.baseline = "sub";
28 | text.alignment = 'right'
29 |
30 | let iconType = children?.icon?.iconType
31 | ? children?.icon?.iconType
32 | : 'ICON_IMAGE_PHONE_WHITE'
33 | const svgImage = new DOMParser().parseFromString(
34 | Icon[iconType].data,
35 | 'text/xml'
36 | )
37 | // console.log("svgImage", svgImage);
38 | const externalSVG = two.interpret(svgImage.firstChild)
39 | // externalSVG.translation.x = -3
40 | // externalSVG.translation.y = -1
41 | externalSVG.scale = children?.icon?.iconScale
42 | ? children?.icon?.iconScale
43 | : 1.2
44 | externalSVG.stroke = fill
45 |
46 | let externalSVGGroup = two.makeGroup(externalSVG)
47 | externalSVGGroup.center()
48 |
49 | let textGroup = two.makeGroup(text)
50 | const textSvgGroup = two.makeGroup(externalSVGGroup, textGroup)
51 | textSvgGroup.center()
52 | textSvgGroup.fill = fill
53 |
54 | const rectangle = two.makeRoundedRectangle(0, 0, 140, 45, 5)
55 | rectangle.width = width
56 | rectangle.height = height
57 | rectangle.fill = 'transparent'
58 | if (stroke && linewidth) {
59 | rectangle.stroke = stroke
60 | rectangle.linewidth = linewidth
61 | } else {
62 | rectangle.noStroke()
63 | }
64 |
65 | const rectTextSvgGroup = two.makeGroup(rectangle, textSvgGroup)
66 | // rectTextSvgGroup.center()
67 | rectangle.noStroke()
68 | // rectTextSvgGroup.fill = fill
69 |
70 | const group = two.makeGroup(rectTextSvgGroup)
71 | group.translation.x = parseInt(prevX)
72 | group.translation.y = parseInt(prevY)
73 | return {
74 | group,
75 | externalSVG,
76 | textSvgGroup,
77 | rectangle,
78 | text,
79 | rectTextSvgGroup,
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/factory/main.js:
--------------------------------------------------------------------------------
1 | export default class Main {
2 | constructor(instance, x, y, properties) {
3 | this.two = instance
4 | this.x = x === 0 ? 500 : x
5 | this.y = y === 0 ? 200 : y
6 | this.properties = properties
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/factory/newArrowLine.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class ArrowLineFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill, x1, x2, y1, y2 } = this.properties
9 |
10 | // console.log('arrow line factory x1, y1, x2, y2', x1, y1, x2, y2)
11 | let line = two.makeArrow(x1, y1, x2, y2, 10)
12 | line.linewidth = 4
13 | // line.vertices[1].y = 200
14 |
15 | const pointCircle1 = two.makeEllipse(0, 0, 5, 5)
16 | pointCircle1.fill = '#FFF'
17 | pointCircle1.stroke = '#0052CC'
18 | pointCircle1.linewidth = 2
19 | // pointCircle1.noStroke()
20 |
21 | const pointCircle2 = two.makeEllipse(10, 0, 5, 5)
22 | pointCircle2.fill = '#FFF'
23 | pointCircle2.stroke = '#0052CC'
24 | pointCircle2.linewidth = 2
25 | // pointCircle2.noStroke()
26 |
27 | const resizeLine = two.makeGroup(pointCircle1, pointCircle2)
28 | resizeLine.translation.y = 0 - line.linewidth
29 | resizeLine.opacity = 0
30 |
31 | let group = two.makeGroup(line, resizeLine)
32 | // console.log('main group', group.getBoundingClientRect())
33 |
34 | // Overriding the circle point group's coordinate and
35 | // manipulating it with line's coordinate
36 | pointCircle1.translation.x = line.vertices[0].x - 0
37 | pointCircle1.translation.y = line.vertices[0].y - 0
38 | pointCircle2.translation.x = line.vertices[1].x + 4
39 | pointCircle2.translation.y = line.vertices[1].y - 0
40 |
41 | // const calcX = parseInt(prevX) + (parseInt(rectangle.width / 2) - 10);
42 | // const calcY = parseInt(prevY) - (parseInt(46) - parseInt(rectangle.height / 2));
43 | group.center()
44 | group.translation.x = parseInt(prevX)
45 | group.translation.y = parseInt(prevY)
46 |
47 | return { group, pointCircle1, pointCircle2, resizeLine, line }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/factory/overlay.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class OverlayFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill, width, height, stroke, linewidth } = this.properties
9 |
10 | const rectangle = two.makeRectangle(0, 0, width || 210, height || 110)
11 | rectangle.fill = fill
12 | rectangle.opacity = 0.5
13 |
14 | if (stroke && linewidth) {
15 | rectangle.stroke = stroke
16 | rectangle.linewidth = linewidth
17 | } else {
18 | rectangle.noStroke()
19 | }
20 |
21 | // console.log("rectangle", rectangle.getBoundingClientRect());
22 |
23 | const group = two.makeGroup(rectangle)
24 |
25 | group.translation.x = parseInt(prevX)
26 | group.translation.y = parseInt(prevY)
27 |
28 | return { group, rectangle }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/factory/pencil.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 | import { color_blue } from 'utils/constants'
3 | import Two from 'two.js'
4 |
5 | export default class PencilFactory extends Main {
6 | createElement() {
7 | const two = this.two
8 | const prevX = this.x
9 | const prevY = this.y
10 | const { fill, width, height, radius, stroke, linewidth, metadata } =
11 | this.properties
12 |
13 | // let paths = []
14 | let path = two.makePath()
15 | if (metadata.length > 0) {
16 | metadata.forEach(function (point) {
17 | path.vertices.push(
18 | new Two.Vector(point.x - prevX, point.y - prevY)
19 | )
20 | })
21 | path.noFill()
22 | path.stroke = '#000'
23 | path.closed = false
24 | // two.add(path)
25 | // paths.push(path)
26 | }
27 |
28 | // path.fill = fill ? fill : color_blue
29 |
30 | // path.linewidth = linewidth ? linewidth : 0
31 |
32 | this.path = path
33 | // Create group and take children elements as a parameter
34 | const group = two.makeGroup(path)
35 | group.translation.x = parseInt(prevX)
36 | group.translation.y = parseInt(prevY)
37 | this.group = group
38 | console.log('group.id pencil', group.id)
39 | return { group: this.group, path }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/factory/rectangle.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 | import { color_blue } from 'utils/constants'
3 |
4 | export default class RectangleFactory extends Main {
5 | createElement() {
6 | const two = this.two
7 | const prevX = this.x
8 | const prevY = this.y
9 | const { fill, width, height, stroke, linewidth } = this.properties
10 |
11 | // Implement core element
12 |
13 | const rectangle = two.makeRoundedRectangle(
14 | 0,
15 | 0,
16 | width || 210,
17 | height || 110,
18 | 5
19 | )
20 |
21 | rectangle.fill = fill ? fill : color_blue
22 | rectangle.stroke = stroke ? stroke : '#fff'
23 | rectangle.linewidth = linewidth ? linewidth : 0
24 |
25 | // console.trace('rectangle trace')
26 | // console.log('rectangle', rectangle.getBoundingClientRect())
27 |
28 | const group = two.makeGroup(rectangle)
29 |
30 | group.translation.x = parseInt(prevX)
31 | group.translation.y = parseInt(prevY)
32 |
33 | return { group, rectangle }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/factory/text.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class TextFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const {
9 | fill = 'rgba(0,0,0,0)',
10 | width,
11 | height,
12 | id,
13 | textColor,
14 | } = this.properties || {}
15 | const { content = '' } = this.properties.metadata || {}
16 |
17 | // pass width and height here for transparent rectangle container
18 | const rectangle = two.makeRoundedRectangle(0, 0, width, height, 5)
19 | rectangle.noFill()
20 | rectangle.noStroke()
21 |
22 | const rectTextGroup = two.makeGroup(rectangle)
23 |
24 | const group = two.makeGroup(rectTextGroup)
25 | group.translation.x = parseInt(prevX)
26 | group.translation.y = parseInt(prevY)
27 |
28 | two.update()
29 |
30 | const svgElem = rectTextGroup._renderer.elem
31 | svgElem.innerHTML = `
32 |
35 | ${content}
36 |
37 | `
38 | rectTextGroup.center()
39 |
40 | two.update()
41 |
42 | return { group, rectTextGroup, svgElem, rectangle }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/factory/textarea.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class TextAreaFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill = 'rgba(0,0,0,0)', content = '' } = this?.properties || {}
9 |
10 | const text = two.makeText('Enter something here', -30, 0)
11 | text.size = '16'
12 | text.weight = '400'
13 | text.fill = '#B3BAC5'
14 | // text.baseline = "sub";
15 | text.alignment = 'left'
16 |
17 | let textGroup = two.makeGroup(text)
18 | textGroup.center()
19 | // console.log("textGroup", textGroup, textGroup.id);
20 |
21 | const group = two.makeGroup(textGroup)
22 | // group.center();
23 | group.translation.x = parseInt(prevX)
24 | group.translation.y = parseInt(prevY)
25 |
26 | // console.log("text bounding initial", text.getBoundingClientRect(true));
27 |
28 | // Shifting order of objects in group to reflect "z-index alias" mechanism for text box
29 |
30 | const rectangle = two.makePath(
31 | group.getBoundingClientRect(true).left - 10,
32 | group.getBoundingClientRect(true).top - 10,
33 |
34 | group.getBoundingClientRect(true).right + 80,
35 | group.getBoundingClientRect(true).top - 10,
36 |
37 | group.getBoundingClientRect(true).right + 80,
38 | group.getBoundingClientRect(true).bottom + 10,
39 |
40 | group.getBoundingClientRect(true).left - 10,
41 | group.getBoundingClientRect(true).bottom + 10
42 | )
43 |
44 | rectangle.fill = '#fff'
45 | rectangle.stroke = '#B3BAC5'
46 | rectangle.linewidth = 1
47 | rectangle.join = 'round'
48 |
49 | // rectangle.noStroke();
50 |
51 | group.add(rectangle)
52 | return { group, rectangle, textGroup, text, rectangle }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/factory/textinput.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class TextInputFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill } = this.properties
9 |
10 | const text = two.makeText('Text input', -30, 0)
11 | text.size = '14'
12 | text.weight = '400'
13 | text.fill = '#B3BAC5'
14 | // text.baseline = "sub";
15 | text.alignment = 'left'
16 |
17 | let textGroup = two.makeGroup(text)
18 | textGroup.center()
19 | console.log('textGroup', textGroup, textGroup.id)
20 |
21 | const group = two.makeGroup(textGroup)
22 |
23 | // group.center();
24 | group.translation.x = parseInt(prevX)
25 | group.translation.y = parseInt(prevY)
26 |
27 | console.log('text bounding initial', text.getBoundingClientRect(true))
28 |
29 | // Shifting order of objects in group to reflect "z-index alias" mechanism for text box
30 |
31 | const rectangle = two.makePath(
32 | group.getBoundingClientRect(true).left - 10,
33 | group.getBoundingClientRect(true).top - 10,
34 |
35 | group.getBoundingClientRect(true).right + 80,
36 | group.getBoundingClientRect(true).top - 10,
37 |
38 | group.getBoundingClientRect(true).right + 80,
39 | group.getBoundingClientRect(true).bottom + 10,
40 |
41 | group.getBoundingClientRect(true).left - 10,
42 | group.getBoundingClientRect(true).bottom + 10
43 | )
44 |
45 | rectangle.fill = '#fff'
46 | rectangle.stroke = '#B3BAC5'
47 | rectangle.linewidth = 1
48 | rectangle.join = 'round'
49 |
50 | // rectangle.noStroke();
51 |
52 | const rectTextGroup = two.makeGroup(rectangle, textGroup)
53 | group.add(rectangle)
54 |
55 | return { group, textGroup, rectTextGroup, rectangle, text }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/factory/toggle.js:
--------------------------------------------------------------------------------
1 | import Main from './main'
2 |
3 | export default class ToggleFactory extends Main {
4 | createElement() {
5 | const two = this.two
6 | const prevX = this.x
7 | const prevY = this.y
8 | const { fill, width, height, radius, stroke, linewidth } =
9 | this.properties
10 |
11 | // Implement outer rectangle
12 | const rect = two.makeRoundedRectangle(0, 0, 55, 30, 16)
13 | rect.fill = '#0052CC'
14 | rect.noStroke()
15 |
16 | const calcCirclePointX = parseInt(rect.width / 4)
17 | // Implement circle shape control
18 | const circle = two.makeCircle(calcCirclePointX, 0, 10)
19 | circle.noStroke()
20 |
21 | const rectCircleGroup = two.makeGroup(rect, circle)
22 | const group = two.makeGroup(rectCircleGroup)
23 |
24 | // const calcX = parseInt(prevX) + (parseInt(rect.width / 2) - 10);
25 | // const calcY = parseInt(prevY) - (parseInt(46) - parseInt(rect.height / 2));
26 | // group.center();
27 | group.translation.x = parseInt(prevX)
28 | group.translation.y = parseInt(prevY)
29 |
30 | return { group, circle, rectCircleGroup, rect }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/hooks/intersectionObserver.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | const useIntersectionObserver = ({
3 | target,
4 | onIntersect,
5 | threshold = 0.1,
6 | rootMargin = '0px',
7 | }) => {
8 | React.useEffect(() => {
9 | const observer = new IntersectionObserver(onIntersect, {
10 | rootMargin,
11 | threshold,
12 | })
13 | const current = target.current
14 | observer.observe(current)
15 | return () => {
16 | observer.unobserve(current)
17 | }
18 | })
19 | }
20 | export default useIntersectionObserver
21 |
--------------------------------------------------------------------------------
/src/icons/icon.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import PropTypes from 'prop-types'
3 | import Icons from './icons'
4 |
5 | class Icon extends Component {
6 | render() {
7 | return (
8 |
17 | )
18 | }
19 |
20 | shouldComponentUpdate() {
21 | return false
22 | }
23 | }
24 |
25 | Icon.propTypes = {
26 | width: PropTypes.number,
27 | height: PropTypes.number,
28 | }
29 |
30 | export default Icon
31 |
--------------------------------------------------------------------------------
/src/icons/image-gallery-landscape-square-potrait-pic-interface-ui-3 (2).svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './App'
5 | import * as serviceWorker from './serviceWorker'
6 |
7 | ReactDOM.render(, document.getElementById('root'))
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.unregister()
13 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/routes.js:
--------------------------------------------------------------------------------
1 | export default {
2 | home: '/',
3 | board: '/board/:id',
4 | about: '/about',
5 | }
6 |
--------------------------------------------------------------------------------
/src/scalingRotate.js:
--------------------------------------------------------------------------------
1 | // import Two from 'https://cdn.skypack.dev/two.js@latest'
2 |
3 | // var two = new Two({
4 | // type: Two.Types.svg,
5 | // fullscreen: true,
6 | // autostart: true,
7 | // }).appendTo(document.body)
8 |
9 | // var mouse = new Two.Vector()
10 | // var rect
11 |
12 | // var points = generateRandomPoints(two.height * 0.33)
13 | // var path = new Two.Path(points)
14 | // // var path = new Two.Star(0, 0, two.height * 0.0625, two.height * 0.33, 9);
15 | // path.scale = new Two.Vector(1, 1)
16 | // path.position.set(two.width / 2, two.height / 2)
17 | // path.closed = true
18 | // path.curved = true
19 | // path.fill = 'orange'
20 | // path.noStroke()
21 |
22 | // var box = new Two.Rectangle(0, 0, 0, 0)
23 | // box.stroke = '#00AEFF'
24 | // box.noFill()
25 |
26 | // var endpoints = new Two.Points(box.vertices)
27 | // endpoints.size = 6
28 | // endpoints.fill = '#00AEFF'
29 | // endpoints.noStroke()
30 |
31 | // var stage = new Two.Group()
32 | // var ui = new Two.Group()
33 | // var text = new Two.Text(`Operation: none`, 0, 0, {
34 | // size: 17,
35 | // baseline: 'bottom',
36 | // })
37 |
38 | // stage.add(path)
39 | // ui.add(box, endpoints)
40 | // two.add(stage, ui, text)
41 |
42 | // two.bind('update', update).bind('resize', resize)
43 |
44 | // resize()
45 |
46 | // var domElement = two.renderer.domElement
47 |
48 | // domElement.addEventListener('pointerdown', pointerdown, false)
49 | // domElement.addEventListener('pointermove', pointermove, false)
50 | // domElement.addEventListener('pointerup', pointerup, false)
51 |
52 | // function resize() {
53 | // text.position.x = two.width / 2
54 | // text.position.y = two.height - text.size
55 | // }
56 |
57 | // function update(frameCount) {
58 | // var minX = Infinity
59 | // var minY = Infinity
60 | // var maxX = -Infinity
61 | // var maxY = -Infinity
62 |
63 | // for (var i = 0; i < 1; i += 0.01) {
64 | // var v = path.getPointAt(i)
65 | // minX = Math.min(minX, v.x * path.scale.x)
66 | // maxX = Math.max(maxX, v.x * path.scale.x)
67 | // minY = Math.min(minY, v.y * path.scale.y)
68 | // maxY = Math.max(maxY, v.y * path.scale.y)
69 | // }
70 |
71 | // box.width = maxX - minX
72 | // box.height = maxY - minY
73 |
74 | // ui.position = path.position
75 | // ui.rotation = path.rotation
76 | // }
77 |
78 | // //
79 |
80 | // var dragging = false,
81 | // scaling = false,
82 | // rotating = false
83 |
84 | // function pointerdown(e) {
85 | // var rect = box.getBoundingClientRect()
86 |
87 | // mouse.x = e.clientX - two.scene.position.x
88 | // mouse.y = e.clientY - two.scene.position.y
89 |
90 | // dragging = true
91 |
92 | // rotating = atCorner(box, mouse)
93 | // if (rotating) {
94 | // mouse.theta = Math.atan2(rotating.point.y, rotating.point.x)
95 | // }
96 |
97 | // scaling = !rotating && atCorner(box, mouse, 25)
98 | // if (scaling) {
99 | // mouse.scale = path.scale.clone()
100 | // }
101 | // }
102 |
103 | // function pointermove(e) {
104 | // var rect = box.getBoundingClientRect()
105 | // var dx = e.clientX - mouse.x
106 | // var dy = e.clientY - mouse.y
107 | // var theta =
108 | // Math.atan2(e.clientY - path.position.y, e.clientX - path.position.x) -
109 | // mouse.theta
110 |
111 | // var isRotating = atCorner(box, mouse)
112 | // var isScaling = atCorner(box, mouse, 25)
113 | // var isPositioning = !isRotating && contains(rect, mouse)
114 |
115 | // mouse.x = e.clientX
116 | // mouse.y = e.clientY
117 |
118 | // if (rotating || isRotating) {
119 | // two.renderer.domElement.style.cursor = 'alias'
120 | // text.value = `Operation: rotate`
121 | // } else if (scaling || isScaling) {
122 | // two.renderer.domElement.style.cursor = 'ns-resize'
123 | // text.value = `Operation: scale`
124 | // } else if (isPositioning) {
125 | // two.renderer.domElement.style.cursor = 'grab'
126 | // text.value = `Operation: position`
127 | // } else {
128 | // two.renderer.domElement.style.cursor = 'default'
129 | // text.value = `Operation: none`
130 | // }
131 |
132 | // if (rotating) {
133 | // text.value = 'Operation: rotating'
134 | // path.rotation = theta
135 | // } else if (scaling) {
136 | // text.value = 'Operation: scaling'
137 | // path.scale.x += dx * 0.01
138 | // path.scale.y += dy * 0.01
139 | // } else if (dragging) {
140 | // if (isPositioning) {
141 | // text.value = 'Operation: positioning'
142 | // two.renderer.domElement.style.cursor = 'grabbing'
143 | // path.position.add(dx, dy)
144 | // }
145 | // }
146 | // }
147 |
148 | // function pointerup() {
149 | // dragging = false
150 | // scaling = false
151 | // rotating = false
152 | // }
153 |
154 | // //
155 |
156 | // function contains(rect, point) {
157 | // return (
158 | // point.x > rect.left &&
159 | // point.x < rect.right &&
160 | // point.y > rect.top &&
161 | // point.y < rect.bottom
162 | // )
163 | // }
164 |
165 | // function atCorner(object, point, limit) {
166 | // var v
167 |
168 | // if (typeof limit !== 'number') {
169 | // limit = 10
170 | // }
171 |
172 | // limit *= limit
173 |
174 | // var matrix = Two.Utils.getComputedMatrix(object)
175 |
176 | // v = object.vertices[0]
177 | // var tl = matrix.multiply(v.x, v.y, 1)
178 | // v = object.vertices[1]
179 | // var tr = matrix.multiply(v.x, v.y, 1)
180 | // v = object.vertices[2]
181 | // var bl = matrix.multiply(v.x, v.y, 1)
182 | // v = object.vertices[3]
183 | // var br = matrix.multiply(v.x, v.y, 1)
184 | // var dbs = Two.Vector.distanceBetweenSquared
185 |
186 | // if (dbs(point, tl) < limit) {
187 | // return { name: 'nw-resize', point: object.vertices[0] }
188 | // } else if (dbs(point, tr) < limit) {
189 | // return { name: 'ne-resize', point: object.vertices[1] }
190 | // } else if (dbs(point, bl) < limit) {
191 | // return { name: 'sw-resize', point: object.vertices[2] }
192 | // } else if (dbs(point, br) < limit) {
193 | // return { name: 'se-resize', point: object.vertices[3] }
194 | // } else {
195 | // return false
196 | // }
197 | // }
198 |
199 | // function generateRandomPoints(size, count) {
200 | // var points = []
201 | // var i = 0
202 | // var length = count || 32
203 | // var radius = size / 2
204 |
205 | // while (i < length) {
206 | // var pct = i / length
207 | // var theta = pct * Math.PI * 2
208 | // var r = Math.random() * radius * 0.5 + radius * 0.5
209 | // var x = r * Math.cos(theta)
210 | // var y = r * Math.sin(theta)
211 | // var anchor = new Two.Anchor(x, y)
212 | // points.push(anchor)
213 | // i++
214 | // }
215 |
216 | // return points
217 | // }
218 |
--------------------------------------------------------------------------------
/src/schema/mutations/index.js:
--------------------------------------------------------------------------------
1 | import { gql } from '@apollo/client'
2 |
3 | export const UPDATE_COMPONENT_INFO = gql`
4 | mutation UPDATE_COMPONENT_INFO(
5 | $id: uuid = ""
6 | $updateObj: components_component_set_input = {}
7 | ) {
8 | update_components_component_by_pk(
9 | pk_columns: { id: $id }
10 | _set: $updateObj
11 | ) {
12 | id
13 | }
14 | }
15 | `
16 |
17 | export const INSERT_COMPONENT = gql`
18 | mutation insertComponent($object: components_component_insert_input = {}) {
19 | component: insert_components_component_one(object: $object) {
20 | id
21 | componentType
22 | }
23 | }
24 | `
25 |
26 | export const UPDATE_BOARD_COMPONENTS = gql`
27 | mutation updateBoardComponents($id: uuid = "", $components: jsonb = "") {
28 | update_boards_board_by_pk(
29 | pk_columns: { id: $id }
30 | _set: { components: $components }
31 | ) {
32 | id
33 | }
34 | }
35 | `
36 |
37 | export const DELETE_COMPONENT_BY_ID = gql`
38 | mutation deleteComponentById($id: uuid = "") {
39 | delete_components_component_by_pk(id: $id) {
40 | boardId
41 | }
42 | }
43 | `
44 |
45 | export const INSERT_BULK_COMPONENTS = gql`
46 | mutation MyMutation($objects: [components_component_insert_input!]! = {}) {
47 | insert_components_component(objects: $objects) {
48 | affected_rows
49 | returning {
50 | boardId
51 | componentType
52 | id
53 | }
54 | }
55 | }
56 | `
57 |
58 | export const INSERT_USER_ONE = gql`
59 | mutation insertUser($object: users_user_insert_input! = {}) {
60 | user: insert_users_user_one(object: $object) {
61 | id
62 | firstName
63 | }
64 | }
65 | `
66 |
67 | export const CREATE_BOARD = gql`
68 | mutation createBoard($object: boards_board_insert_input! = {}) {
69 | board: insert_boards_board_one(object: $object) {
70 | id
71 | createdBy
72 | }
73 | }
74 | `
75 |
76 | export const DELETE_BULK_COMPONENTS = gql`
77 | mutation deleteComponents($_in: [uuid!]! = "") {
78 | deleteComponents: delete_components_component(
79 | where: { id: { _in: $_in } }
80 | ) {
81 | affected_rows
82 | }
83 | }
84 | `
85 |
86 | export const UPDATE_USER_REVISIT_COUNT = gql`
87 | mutation updateUserRevisitCount($userId: String!) {
88 | update_users_user_revisits_by_pk(
89 | pk_columns: { user_id: $userId }
90 | _inc: { count: "1" }
91 | ) {
92 | count
93 | user_id
94 | }
95 | }
96 | `
97 |
--------------------------------------------------------------------------------
/src/schema/queries/index.js:
--------------------------------------------------------------------------------
1 | import { gql } from '@apollo/client'
2 |
3 | export const GET_USER_DETAILS = gql`
4 | query MyQuery($id: uuid = "") {
5 | users: users_user(where: { id: { _eq: $id } }) {
6 | firstName
7 | id
8 | }
9 | }
10 | `
11 |
12 | export const GET_COMPONENT_TYPES = gql`
13 | query getComponentTypes {
14 | componentTypes: components_componentType {
15 | label
16 | metadata
17 | logo
18 | width
19 | height
20 | fill
21 | textColor
22 | }
23 | }
24 | `
25 |
26 | export const GET_COMPONENTS_FOR_BOARD_QUERY = gql`
27 | query getComponentsForBoard($boardId: String = "") {
28 | components: components_component(
29 | where: { boardId: { _eq: $boardId } }
30 | ) {
31 | id
32 | componentType
33 | children
34 | metadata
35 | x
36 | x1
37 | x2
38 | y
39 | y1
40 | y2
41 | fill
42 | width
43 | height
44 | iconStroke
45 | stroke
46 | linewidth
47 | }
48 | }
49 | `
50 |
51 | export const GET_COMPONENT_INFO_QUERY = gql`
52 | query getComponentInfoQuery($id: uuid = "") {
53 | component: components_component_by_pk(id: $id) {
54 | metadata
55 | width
56 | height
57 | fill
58 | id
59 | stroke
60 | linewidth
61 | x
62 | y
63 | x1
64 | y1
65 | x2
66 | y2
67 | componentType
68 | children
69 | updatedBy
70 | iconStroke
71 | textColor
72 | }
73 | }
74 | `
75 |
76 | export const GET_BOARD_DATA_QUERY = gql`
77 | query getBoardComponents($boardId: String! = "") {
78 | components: components_component(
79 | where: { boardId: { _eq: $boardId } }
80 | ) {
81 | id
82 | componentType
83 | }
84 | }
85 | `
86 |
--------------------------------------------------------------------------------
/src/schema/subscriptions/index.js:
--------------------------------------------------------------------------------
1 | import { gql } from '@apollo/client'
2 |
3 | export const GET_USER_DETAILS = gql`
4 | subscription MyQuery($id: uuid = "") {
5 | users: users_user(where: { id: { _eq: $id } }) {
6 | firstName
7 | id
8 | }
9 | }
10 | `
11 |
12 | export const GET_BOARD_DATA_SUBSCRIPTION = gql`
13 | subscription getBoardComponents($boardId: String! = "") {
14 | components: components_component(
15 | where: { boardId: { _eq: $boardId } }
16 | ) {
17 | id
18 | componentType
19 | }
20 | }
21 | `
22 |
23 | export const GET_COMPONENT_INFO_SUBSCRIPTION = gql`
24 | subscription getComponentInfoSubscription($id: uuid = "") {
25 | component: components_component_by_pk(id: $id) {
26 | metadata
27 | width
28 | height
29 | fill
30 | id
31 | stroke
32 | linewidth
33 | x
34 | y
35 | x1
36 | y1
37 | x2
38 | y2
39 | componentType
40 | children
41 | updatedBy
42 | iconStroke
43 | textColor
44 | }
45 | }
46 | `
47 |
48 | export const GET_COMPONENTS_FOR_BOARD_SUBSCRIPTION = gql`
49 | subscription getComponentsForBoard($boardId: String = "") {
50 | components: components_component(
51 | where: { boardId: { _eq: $boardId } }
52 | ) {
53 | id
54 | componentType
55 | children
56 | metadata
57 | x
58 | x1
59 | x2
60 | y
61 | y1
62 | y2
63 | fill
64 | width
65 | height
66 | iconStroke
67 | stroke
68 | linewidth
69 | }
70 | }
71 | `
72 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' }
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect';
6 |
--------------------------------------------------------------------------------
/src/store/actions/main.js:
--------------------------------------------------------------------------------
1 | export function setPeronsalInformation(type, data) {
2 | return async function (dispatch) {
3 | try {
4 | await dispatch({
5 | type,
6 | payload: data,
7 | });
8 |
9 | // successCallback && successCallback(response);
10 | } catch (e) {
11 | console.error(e);
12 | // errorCallback && errorCallback(e.response);
13 | }
14 | };
15 | }
16 |
17 | export function getElementsData(type, data) {
18 | return async function (dispatch) {
19 | try {
20 | await dispatch({
21 | type,
22 | payload: data,
23 | });
24 |
25 | // successCallback && successCallback(response);
26 | } catch (e) {
27 | console.error(e);
28 | // errorCallback && errorCallback(e.response);
29 | }
30 | };
31 | }
32 |
33 | export function addElement(type, data) {
34 | return async function (dispatch) {
35 | try {
36 | await dispatch({
37 | type,
38 | payload: data,
39 | });
40 |
41 | // successCallback && successCallback(response);
42 | } catch (e) {
43 | console.error(e);
44 | // errorCallback && errorCallback(e.response);
45 | }
46 | };
47 | }
48 |
49 | export function ungroupElements(type, data) {
50 | return async function (dispatch) {
51 | try {
52 | await dispatch({
53 | type,
54 | payload: data,
55 | });
56 |
57 | // successCallback && successCallback(response);
58 | } catch (e) {
59 | console.error(e);
60 | // errorCallback && errorCallback(e.response);
61 | }
62 | };
63 | }
64 |
--------------------------------------------------------------------------------
/src/store/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import main from 'store/reducers/main';
4 |
5 | const appReducer = combineReducers({
6 | main,
7 | });
8 |
9 | const rootReducer = (state, action) => {
10 | console.log('action.type in root reducer', action.type);
11 | if (action.type === 'LOGOUT') {
12 | state = undefined;
13 | }
14 | return appReducer(state, action);
15 | };
16 |
17 | export default rootReducer;
18 |
--------------------------------------------------------------------------------
/src/store/types.js:
--------------------------------------------------------------------------------
1 | export const CONSTRUCT = 'CONSTRUCT';
2 | export const COMPLETE = 'COMPLETE';
3 | export const ADD_ELEMENT = 'ADD_ELEMENT';
4 | export const UNGROUP_ELEMENT = 'UNGROUP_ELEMENT';
5 | export const AREA_SELECTION = 'AREA_SELECTION';
6 | export const UPDATE_ELEMENT_DATA = 'UPDATE_ELEMENT_DATA';
7 |
--------------------------------------------------------------------------------
/src/styles/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | @tailwind components;
4 |
5 | @tailwind utilities;
6 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export const removeAllObjects = (obj) => {
2 | let newObj = { ...obj };
3 | let objKeys = Object.keys(obj);
4 | Object.values(obj).forEach((item, index) => {
5 | if (typeof item === "object") {
6 | delete newObj[objKeys[index]];
7 | }
8 | });
9 | return newObj;
10 | };
11 |
12 | export const calcCoordsFromRect = (rect) => {
13 | const width = rect.width;
14 | const height = rect.height;
15 | const left = rect.left;
16 | const top = rect.top;
17 |
18 | const btnCoordX = parseInt(left) + parseInt(width) / 4;
19 | const btnCoordY = parseInt(top) + parseInt(height) / 4;
20 | return { left: btnCoordX, top: btnCoordY };
21 | };
22 |
23 | export const getFourthValue = (x1, x2, y1) => {
24 | const divisor = parseInt(x2 * y1);
25 | const dividend = x1;
26 | const output = divisor / dividend;
27 | return output;
28 | };
29 |
30 | export const getDiffForTwoValues = (x, y) => {
31 | const result = Math.abs(parseInt(x) - parseInt(y));
32 | return result;
33 | };
34 |
--------------------------------------------------------------------------------
/src/utils/misc.js:
--------------------------------------------------------------------------------
1 | export const elementOnBlurHandler = (e, selectorInstance, two) => {
2 | // Callback for add and remove event listener for floating showToolbar
3 | const blurListenerCB = (e) => {
4 | if (e?.relatedTarget?.dataset.parent === 'floating-toolbar') {
5 | // no action required
6 | } else {
7 | selectorInstance && selectorInstance.hide()
8 | // toggleToolbar(false);
9 | }
10 | }
11 |
12 | // Check if user interacts with toolbar
13 | if (
14 | e?.relatedTarget?.id === 'floating-toolbar' ||
15 | e?.relatedTarget?.dataset.parent === 'floating-toolbar'
16 | ) {
17 | document
18 | .getElementById('floating-toolbar')
19 | .addEventListener('blur', blurListenerCB)
20 | } else {
21 | selectorInstance && selectorInstance.hide()
22 | // toggleToolbar(false);
23 | }
24 | two.update()
25 | }
26 |
27 | export const generateRandomUsernames = () => {
28 | let names = [
29 | 'cake_salad',
30 | 'raspberry_waffle',
31 | 'tropical_owl',
32 | 'high_antopera',
33 | 'banestick_watermelon',
34 | 'zephyr_pomegranate',
35 | 'optimus_prime',
36 | 'network_tea',
37 | 'floral_cake',
38 | 'volcano_bee',
39 | 'hurricane_cat',
40 | 'juice_walrus',
41 | 'groundhog_day',
42 | 'spacex_dragon',
43 | 'icecream_fox',
44 | 'astronout_fly',
45 | 'icecoffee_cat',
46 | 'pumpkin_bat',
47 | 'anonymous_galileo',
48 | 'raspberry_cat',
49 | 'water_rabbit',
50 | 'violet_turtle',
51 | ]
52 |
53 | let rB = Math.floor(Math.random() * names.length)
54 | let name = names[rB]
55 | let firstName = name.split('_')[0]
56 | let lastName = name.split('_')[1]
57 |
58 | return { nickname: name, firstName, lastName }
59 | }
60 |
61 | export const generateUUID = () => {
62 | // Public Domain/MIT
63 | let d = new Date().getTime() //Timestamp
64 | let d2 =
65 | (typeof performance !== 'undefined' &&
66 | performance.now &&
67 | performance.now() * 1000) ||
68 | 0 //Time in microseconds since page-load or 0 if unsupported
69 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
70 | /[xy]/g,
71 | function (c) {
72 | let r = Math.random() * 16 //random number between 0 and 16
73 | if (d > 0) {
74 | //Use timestamp until depleted
75 | r = (d + r) % 16 | 0
76 | d = Math.floor(d / 16)
77 | } else {
78 | //Use microseconds since page-load if supported
79 | r = (d2 + r) % 16 | 0
80 | d2 = Math.floor(d2 / 16)
81 | }
82 | return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
83 | }
84 | )
85 | }
86 |
--------------------------------------------------------------------------------
/src/utils/updateVertices.js:
--------------------------------------------------------------------------------
1 | export const updateX1Y1Vertices = (TwoRef, line, x1, y1, pointCircle1, two) => {
2 | // copied code from definition of makeArrow
3 | let headlen = 10
4 |
5 | let angle = Math.atan2(line.vertices[1].y - y1, line.vertices[1].x - x1)
6 |
7 | let vertices = [
8 | new TwoRef.Anchor(
9 | x1,
10 | y1,
11 | undefined,
12 | undefined,
13 | undefined,
14 | undefined,
15 | TwoRef.Commands.move
16 | ),
17 | new TwoRef.Anchor(
18 | line.vertices[1].x,
19 | line.vertices[1].y,
20 | undefined,
21 | undefined,
22 | undefined,
23 | undefined,
24 | TwoRef.Commands.line
25 | ),
26 | new TwoRef.Anchor(
27 | line.vertices[1].x - headlen * Math.cos(angle - Math.PI / 4),
28 | line.vertices[1].y - headlen * Math.sin(angle - Math.PI / 4),
29 | undefined,
30 | undefined,
31 | undefined,
32 | undefined,
33 | TwoRef.Commands.line
34 | ),
35 |
36 | new TwoRef.Anchor(
37 | line.vertices[1].x,
38 | line.vertices[1].y,
39 | undefined,
40 | undefined,
41 | undefined,
42 | undefined,
43 | TwoRef.Commands.move
44 | ),
45 | new TwoRef.Anchor(
46 | line.vertices[1].x - headlen * Math.cos(angle + Math.PI / 4),
47 | line.vertices[1].y - headlen * Math.sin(angle + Math.PI / 4),
48 | undefined,
49 | undefined,
50 | undefined,
51 | undefined,
52 | TwoRef.Commands.line
53 | ),
54 | ]
55 | line.vertices = vertices
56 |
57 | // old code
58 | // pointCircle1.translation.x = line.vertices[0].x + 0
59 | // pointCircle1.translation.y = line.vertices[0].y + parseInt(line.linewidth)
60 |
61 | pointCircle1.translation.x = line.vertices[0].x
62 | pointCircle1.translation.y = line.vertices[0].y
63 |
64 | two.update()
65 | }
66 |
67 | export const updateX2Y2Vertices = (TwoRef, line, x2, y2, pointCircle2, two) => {
68 | // copied code from definition of makeArrow
69 | let headlen = 10
70 |
71 | let angle = Math.atan2(y2 - line.vertices[0].y, x2 - line.vertices[0].x)
72 |
73 | let vertices = [
74 | new TwoRef.Anchor(
75 | line.vertices[0].x,
76 | line.vertices[0].y,
77 | undefined,
78 | undefined,
79 | undefined,
80 | undefined,
81 | TwoRef.Commands.move
82 | ),
83 | new TwoRef.Anchor(
84 | x2,
85 | y2,
86 | undefined,
87 | undefined,
88 | undefined,
89 | undefined,
90 | TwoRef.Commands.line
91 | ),
92 | new TwoRef.Anchor(
93 | x2 - headlen * Math.cos(angle - Math.PI / 4),
94 | y2 - headlen * Math.sin(angle - Math.PI / 4),
95 | undefined,
96 | undefined,
97 | undefined,
98 | undefined,
99 | TwoRef.Commands.line
100 | ),
101 |
102 | new TwoRef.Anchor(
103 | x2,
104 | y2,
105 | undefined,
106 | undefined,
107 | undefined,
108 | undefined,
109 | TwoRef.Commands.move
110 | ),
111 | new TwoRef.Anchor(
112 | x2 - headlen * Math.cos(angle + Math.PI / 4),
113 | y2 - headlen * Math.sin(angle + Math.PI / 4),
114 | undefined,
115 | undefined,
116 | undefined,
117 | undefined,
118 | TwoRef.Commands.line
119 | ),
120 | ]
121 | line.vertices = vertices
122 |
123 | // old code
124 | // pointCircle2.translation.x =
125 | // line.vertices[1].x < line.vertices[0].x
126 | // ? line.vertices[1].x
127 | // : line.vertices[1].x + 6
128 | // pointCircle2.translation.y = line.vertices[1].y + parseInt(line.linewidth)
129 |
130 | pointCircle2.translation.x = line.vertices[1].x
131 | pointCircle2.translation.y = line.vertices[1].y
132 |
133 | two.update()
134 | }
135 |
--------------------------------------------------------------------------------
/src/views/Board/errorBoundary.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | class ErrorBoundaryBoardView extends React.Component {
4 | constructor(props) {
5 | super(props)
6 | this.state = { hasError: false }
7 | }
8 |
9 | static getDerivedStateFromError(error) {
10 | return { hasError: true }
11 | }
12 |
13 | componentDidCatch(error, errorInfo) {
14 | // sentry.post()
15 | }
16 |
17 | render() {
18 | if (this.state.hasError) {
19 | // You can render any custom fallback UI
20 | return Couldn't load board view. Something went wrong
21 | }
22 |
23 | return this.props.children
24 | }
25 | }
26 |
27 | export default ErrorBoundaryBoardView
28 |
--------------------------------------------------------------------------------
/src/views/Board/index.css:
--------------------------------------------------------------------------------
1 | #show-select-any-shape-btn::before {
2 | position: absolute;
3 | content: '';
4 | width: 10px;
5 | height: 10px;
6 | background: #00875a;
7 | transform: rotate(45deg);
8 | /* border: 10px solid transparent; */
9 | margin: 0;
10 | left: -5px;
11 | top: 37px;
12 | z-index: -1;
13 | }
14 |
15 | #show-select-any-element-btn::before {
16 | position: absolute;
17 | content: '';
18 | width: 10px;
19 | height: 10px;
20 | background: #00875a;
21 | transform: rotate(45deg);
22 | /* border: 10px solid transparent; */
23 | margin: 0;
24 | left: -5px;
25 | top: 37px;
26 | z-index: -1;
27 | }
28 |
--------------------------------------------------------------------------------
/src/views/Board/index.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react'
2 |
3 | import ErrorBoundary from './errorBoundary'
4 | import Spinner from 'components/common/spinner'
5 | import './index.css'
6 |
7 | const BoardViewPage = React.lazy(() => import('./board'))
8 |
9 | const BoardViewContainer = (props) => (
10 | }>
11 |
12 |
13 |
14 |
15 | )
16 |
17 | export default BoardViewContainer
18 |
--------------------------------------------------------------------------------
/src/views/Home/errorBoundary.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | class ErrorBoundaryHomePageView extends React.Component {
4 | constructor(props) {
5 | super(props)
6 | this.state = { hasError: false }
7 | }
8 |
9 | static getDerivedStateFromError(error) {
10 | return { hasError: true }
11 | }
12 |
13 | componentDidCatch(error, errorInfo) {
14 | // sentry.post()
15 | }
16 |
17 | render() {
18 | if (this.state.hasError) {
19 | // You can render any custom fallback UI
20 | return Couldn't load home page view. Something went wrong
21 | }
22 |
23 | return this.props.children
24 | }
25 | }
26 |
27 | export default ErrorBoundaryHomePageView
28 |
--------------------------------------------------------------------------------
/src/views/Home/index.css:
--------------------------------------------------------------------------------
1 | .animate-fade {
2 | animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1);
3 | }
4 |
5 | .home-arrow-icon {
6 | opacity: 0.4;
7 | transition: all 0.2s ease;
8 | }
9 | .primary-btn-home:hover .home-arrow-icon {
10 | opacity: 1;
11 | transform: translateX(5px);
12 | }
13 |
14 | .home-page-main-video iframe {
15 | z-index: 1 !important;
16 | }
17 |
18 | /* #banner-wave-svg {
19 | transition: opacity 0.3s ease;
20 | animation: fadeIn 0.3s;
21 | } */
22 | /*
23 | #branding-imgs {
24 | transition: transform 1s ease;
25 | animation: fadeIn 0.3s;
26 | }
27 |
28 | @keyframes fadeIn {
29 | from {
30 | opacity: 0;
31 | transform: scale(0.4);
32 | }
33 |
34 | to {
35 | opacity: 1;
36 | transform: scale(1);
37 | }
38 | } */
39 | /*
40 | @keyframes fadeIn {
41 | 0% {
42 | opacity: 0;
43 |
44 | }
45 |
46 | 30% {
47 | opacity: 0.2;
48 |
49 | }
50 |
51 | 60% {
52 | opacity: 0.6;
53 |
54 | }
55 |
56 | 100% {
57 | opacity: 1;
58 | }
59 | } */
60 |
--------------------------------------------------------------------------------
/src/views/Home/index.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react'
2 | import ErrorBoundary from './errorBoundary'
3 | import Spinner from 'components/common/spinner'
4 | import './index.css'
5 |
6 | const HomePage = React.lazy(() => import('./home'))
7 |
8 | const HomePageViewContainer = (props) => (
9 | }>
10 |
11 |
12 |
13 |
14 | )
15 |
16 | export default HomePageViewContainer
17 |
--------------------------------------------------------------------------------
/src/wireframeAssets/avatar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/btn.svg:
--------------------------------------------------------------------------------
1 |
2 | Button
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/btnWithIcon.svg:
--------------------------------------------------------------------------------
1 |
2 | Button
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/checkbox.svg:
--------------------------------------------------------------------------------
1 |
2 | checkbox 1checkbox 2checkbox 3
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/circle.svg:
--------------------------------------------------------------------------------
1 | Circle
--------------------------------------------------------------------------------
/src/wireframeAssets/cursor.svg:
--------------------------------------------------------------------------------
1 | Cursor
--------------------------------------------------------------------------------
/src/wireframeAssets/divider.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/frame.svg:
--------------------------------------------------------------------------------
1 | Rectangle
--------------------------------------------------------------------------------
/src/wireframeAssets/imageCard.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/miniAvatar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/pencil.svg:
--------------------------------------------------------------------------------
1 | Edit
--------------------------------------------------------------------------------
/src/wireframeAssets/radiobox.svg:
--------------------------------------------------------------------------------
1 |
2 | radiobox 1radiobox 2radiobox3
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/rectangle.svg:
--------------------------------------------------------------------------------
1 | Rectangle
--------------------------------------------------------------------------------
/src/wireframeAssets/text.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/wireframeAssets/toggle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/temp.js:
--------------------------------------------------------------------------------
1 | let sampleBoardData = {
2 | data: {
3 | components: [
4 | {
5 | id: '456e5b31-b7ec-4e1c-bdfc-13bc10aa5eeb',
6 | componentType: 'rectangle',
7 | },
8 | {
9 | id: 'c0738fa5-7769-406e-aa08-7860fc392cd3',
10 | componentType: 'circle',
11 | },
12 | {
13 | id: '416b6286-e936-42fd-bce4-fe168216eeef',
14 | componentType: 'rectangle',
15 | },
16 | ],
17 | },
18 | }
19 |
20 | /**
21 | * @typedef {Object} componentInfo
22 | * @property {object} metadata
23 | * @property {number} width
24 | * @property {number} height
25 | * @property {text} fill
26 | * @property {text} id
27 | * @property {text} stroke
28 | * @property {number} linewidth
29 | * @property {text} fill
30 | * @property {number} x
31 | * @property {number} y
32 | * @property {number} x1
33 | * @property {number} x2
34 | * @property {number} y1
35 | * @property {number} y2
36 | * @property {text} componentType
37 | * @property {object} children
38 | * @property {text} updatedBy
39 | * @property {text} iconStroke
40 | * @property {text} textColor
41 | */
42 |
43 | /** @type {componentInfo} */
44 | let componentInfo = {
45 | metadata: {},
46 | width: 120,
47 | height: 120,
48 | fill: '#0052CC',
49 | id: null,
50 | stroke: null,
51 | linewidth: null,
52 | x: 0,
53 | y: 0,
54 | x1: 100,
55 | x2: 400,
56 | y1: 100,
57 | y2: 100,
58 | componentType: '',
59 | children: {},
60 | updatedBy: null,
61 | iconStroke: null,
62 | textColor: null,
63 | }
64 |
--------------------------------------------------------------------------------
/temp.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI0ZmIyYjUwNS1jODE1LTRhMDEtOGZhNC03ZDdiMzI0NjhkZTIiLCJpYXQiOjE2MzYyODQzMjN9.Y_ldhZKYyoSzTFarqEg2GzxgIyNHSsf0fGKZNYoNw-8"
3 | }
4 |
--------------------------------------------------------------------------------