├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── axes-size.gif
├── choose-chart.gif
├── compilation-stats.json
├── electron-scripts
└── preload.js
├── export.gif
├── logo.png
├── main.js
├── npm_package_entry.js
├── package.json
├── penguin-icon-old.png
├── penguin-icon.png
├── select-properties.gif
├── src
├── AllRoutes.jsx
├── AppRoutes.jsx
├── Archives
│ ├── charts
│ │ ├── BarChart
│ │ │ ├── BarChart.jsx
│ │ │ ├── BarChartCodePreview.jsx
│ │ │ └── BarChartForm.jsx
│ │ ├── Histogram
│ │ │ ├── Histogram.jsx
│ │ │ ├── HistogramCodePreview.jsx
│ │ │ └── HistogramForm.jsx
│ │ ├── LineChartCodePreview.jsx
│ │ ├── LineChartForm.jsx
│ │ └── Scatterplot
│ │ │ ├── ScatterPlotCodePreview.jsx
│ │ │ └── ScatterPlotForm.jsx
│ ├── reducers
│ │ ├── barPaddingSlice.js
│ │ ├── dataSlice.js
│ │ ├── heightSlice.js
│ │ ├── nameSlice.js
│ │ ├── radiusSlice.js
│ │ ├── thresholdsSlice.js
│ │ ├── widthSlice.js
│ │ ├── xAxisLabelSlice.js
│ │ ├── xKeySlice.js
│ │ ├── yAxisLabelSlice.js
│ │ └── yKeySlice.js
│ └── utilities
│ │ ├── Archive
│ │ ├── Axis.jsx
│ │ ├── AxisHorizontal.js
│ │ ├── AxisVertical.js
│ │ ├── Axis_comments.jsx
│ │ ├── Bars.jsx
│ │ ├── Chart.jsx
│ │ ├── Chart_comments.jsx
│ │ ├── CodePreview.js
│ │ ├── accessorPropsType.jsx
│ │ ├── combineChartDimensions.jsx
│ │ ├── createApplication.js
│ │ ├── createFiles.js
│ │ ├── dimensionsPropsType.jsx
│ │ ├── exportButton.js
│ │ ├── useAccessor.jsx
│ │ ├── useChartDimensions_comments.jsx
│ │ ├── useUniqueId.js
│ │ └── utils.js
│ │ └── exportProject.js
├── Dropdown
│ ├── Dropdown.jsx
│ ├── DropdownFunctionality.jsx
│ ├── Menu.Item.jsx
│ ├── Menu.jsx
│ └── MenuButton.jsx
├── app
│ ├── dictionary.js
│ ├── preloadedState.js
│ └── store.js
├── babel-plugin-macros.config.js
├── babel.config.js
├── components
│ ├── ChartComponents
│ │ ├── Chart.css
│ │ ├── JSX
│ │ │ ├── Arc.jsx
│ │ │ ├── Axis.jsx
│ │ │ ├── Bars.jsx
│ │ │ ├── Chart.css
│ │ │ ├── Chart.jsx
│ │ │ ├── ChartWithDimensions.jsx
│ │ │ ├── Circles.jsx
│ │ │ ├── CodeRender.jsx
│ │ │ ├── Container.jsx
│ │ │ ├── ErrorBoundary.jsx
│ │ │ ├── ExportCodeButton.jsx
│ │ │ ├── ExportDataButton.jsx
│ │ │ ├── Form.jsx
│ │ │ ├── Gradient.jsx
│ │ │ ├── Line.jsx
│ │ │ ├── Pie.jsx
│ │ │ └── Rectangle.jsx
│ │ └── chartstyles.css
│ ├── Charts
│ │ ├── BarChart
│ │ │ └── JSX
│ │ │ │ └── BarChart.jsx
│ │ ├── Histogram
│ │ │ └── JSX
│ │ │ │ └── Histogram.jsx
│ │ ├── LineChart
│ │ │ └── JSX
│ │ │ │ └── LineChart.jsx
│ │ ├── PieChart
│ │ │ └── JSX
│ │ │ │ └── PieChart.jsx
│ │ └── ScatterPlot
│ │ │ └── JSX
│ │ │ └── ScatterPlot.jsx
│ ├── HelloWorldcopy.js
│ ├── NavBar.tsx
│ └── pages
│ │ ├── ChartCards.jsx
│ │ ├── Homepage.jsx
│ │ ├── TheCarousel.jsx
│ │ ├── chart1.svg
│ │ ├── chart2.svg
│ │ ├── chart3.svg
│ │ ├── chart4.svg
│ │ └── chart5.svg
├── dashboard.png
├── features
│ └── chart
│ │ ├── chartsSlice.js
│ │ └── propsSlice.js
├── index.d.ts
├── index.html
├── index.jsx
├── mockup.html
├── styles.css
├── topy.png
└── utils
│ ├── CodePreview.js
│ ├── EnteredData.jsx
│ ├── ExportData.js
│ ├── dummyTimelineData.js
│ ├── dummyfruitsdata.js
│ ├── dummypenguinsdata.js
│ ├── handlers.js
│ ├── hooks.js
│ ├── jsonFruits
│ ├── jsonLine
│ ├── jsonpenguins
│ ├── observable.js
│ ├── parseData.js
│ ├── utils.js
│ └── utils.ts
├── stats.json
├── tailwind.config.js
├── tsconfig.json
├── webpack-stats.json
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["syntax-dynamic-import"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | src/*
3 | build/*
4 |
5 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "plugins": ["@typescript-eslint", "prettier"],
5 | "extends": [
6 | "eslint:recommended",
7 | "plugin:@typescript-eslint/eslint-recommended",
8 | "plugin:@typescript-eslint/recommended",
9 | "plugin:@typescript-eslint/recommended-requiring-type-checking",
10 | "prettier",
11 | "airbnb",
12 | "airbnb-typescript",
13 | "airbnb/hooks"
14 | ],
15 | "rules": {
16 | "prettier/prettier": 2,
17 | "import/no-extraneous-dependencies": 0,
18 | "max-len": 0,
19 | "comma-dangle": 0,
20 | "curly": 0,
21 | "no-extraneous-dependencies": 0
22 | }
23 | // "parserOptions": { "project": "./tsconfig.json" }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 | ignore.tsx
4 | *.tsx
5 | *.ts
6 | LibrarySample.jsx
7 | archives
8 | .tsx
9 | src/components/Charts/LineGraph/**/*.tsx.jsx
10 | src/components/Charts/Timeline/**/*.tsx.jsx
11 | installer
12 | dist
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "trailingComma": "none",
4 | "singleQuote": true,
5 | "printWidth": 80
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OSLabs Beta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ad3lie
2 |
3 | ad3lie is an open-source application and package for creating elegant and responsive data visualizations built with React and D3.
4 |
5 | The focus of this application is to generate user customized charts that can be used in any React project.
6 |
7 | # Installation
8 |
9 | ## Download ad3lie Application
10 |
11 | Begin by downloading the app from our [website](https://ad3lie.dev/).
12 |
13 | After opening the app, choose which graph to create. Don't worry, if you decide to chose another graph, simply click on the home button and choose another graph from the home page. All of your input data will be saved for the duration of the application's life span.
14 |
15 | 
16 |
17 | Input the required fields (i.e. Data, xKey, and yKey) and adjust the graph based on the inputs given
18 |
19 | 
20 |
21 | 
22 |
23 | Click the export button and select the file directory of your project for both the data file and React component.
24 |
25 | 
26 |
27 | ## Install ad3lie Package
28 |
29 | Downloading ad3lie will include other necessary dependencies in order to generate your data visualizations. This allows for the use of the React component exported from the ad3lie application.
30 |
31 | Simply download the npm package
32 |
33 | ```
34 | npm i ad3lie
35 | ```
36 |
37 | OR
38 |
39 | ```
40 | yarn add ad3lie
41 | ```
42 |
43 | From here, import the React component as a child component.
44 |
45 | ```
46 | import Chart from "./file/path/to/component"
47 | ```
48 |
49 | From here, simply use the componet as you would any other child component in your React app.
50 |
51 | ### Happy visualizing! <|^o^|>
52 |
53 | # Documentation
54 |
55 | For more detailed information, please check the related package [documentation](https://docs.ad3lie.dev/) or go directly to our [npm package](https://www.npmjs.com/package/ad3lie).
56 |
57 | # How to Setup React App
58 | Make sure to install these npm packages
59 | ```
60 | npm i d3
61 | npm i postcss
62 | npm i postcss-loader
63 | npm i autoprefixer
64 | ```
65 |
66 | tailwind.config.js
67 | ```
68 | module.exports = {
69 | content: ["./client/src/**/*.{html,js}"],
70 | theme: {
71 | extend: {},
72 | },
73 | plugins: [],
74 | }
75 | ```
76 |
77 | webpack.config.js
78 | ```javascript
79 | const path = require('path');
80 | const HtmlWebpackPlugin = require('html-webpack-plugin');
81 |
82 | module.exports = (env) => {
83 | return {
84 | mode: env.mode,
85 | entry: './client/src/index.js',
86 | output: {
87 | path: path.resolve(__dirname, 'client', 'build'),
88 | filename: 'bundle.js'
89 | },
90 | module: {
91 | rules: [{
92 | test: /\.jsx?/,
93 | use: {
94 | loader: 'babel-loader',
95 | options: {
96 | presets: ['@babel/preset-env',
97 | '@babel/preset-react'],
98 | targets: {chrome: "100"}
99 |
100 | }
101 | }
102 | },
103 | {
104 | test: /.+\.css$/i,
105 | // exclude: /node_modules/,
106 | use: [
107 | 'style-loader',
108 | 'css-loader',
109 | {
110 | loader: 'postcss-loader',
111 | options: {
112 | postcssOptions: {
113 | plugins: {
114 | tailwindcss: {},
115 | autoprefixer: {},
116 | }
117 | }
118 | }
119 | }
120 | ]
121 | }
122 | ]
123 | },
124 | resolve: {
125 | extensions: ['', '.js', '.jsx'],
126 | alias: {
127 | 'react': path.resolve(__dirname, 'node_modules/react'),
128 | }
129 | },
130 |
131 | plugins: [new HtmlWebpackPlugin({
132 | template: './client/src/index.html'
133 | })],
134 | devServer: {
135 | static: './client/build',
136 | port: 8888
137 | }
138 | }
139 | }
140 | ```
141 |
142 | Checkout our [website](https://ad3lie.dev/) to see incoming features, how to get involved, and meet our team!
143 |
--------------------------------------------------------------------------------
/axes-size.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/axes-size.gif
--------------------------------------------------------------------------------
/choose-chart.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/choose-chart.gif
--------------------------------------------------------------------------------
/electron-scripts/preload.js:
--------------------------------------------------------------------------------
1 | // Preload (Isolated World)
2 | const { contextBridge, ipcRenderer, dialog } = require('electron');
3 | const { writeFileSync, mkdirSync } = require('fs');
4 | const path = require('path');
5 | console.log('1st dialog', dialog);
6 |
7 | contextBridge.exposeInMainWorld('electron', {
8 | writeFileSync,
9 | mkdirSync,
10 | path,
11 | __dirname,
12 | // dialog
13 | //difference between .send and .invoke?
14 | showSaveDialog: () => ipcRenderer.invoke('show-save-dialog')
15 | });
16 |
17 | /*
18 | DESCRIPTION: This file appears to limit the node methods the Electron app can access.
19 |
20 | Per the docs:
21 | -Main World is the JavaScript context in which the renderer code runs (that is, the page)
22 | -Isolated World is where preload scripts run
23 | -contextBridge is a module that safely exposes APIs from the isolated context in which preload scripts run
24 | to the context in which the website or application runs (i.e., from Isolated World to Main World)
25 |
26 | We likely should not change this file unless we determine additional methods are necessary
27 | or some methods are not used.
28 |
29 | */
30 |
31 | // Expose protected methods that allow the renderer process to use select node methods
32 | // without exposing all node functionality. This is a critical security feature
33 | // 'mainWorld" is the context that the main renderer runs in
34 | // with contextIsolation on (see webpreferences on main.js), this preload script runs isolated
35 | // "electron" is the key that injects the api into the window
36 | // to access these keys in the renderer process, we'll do window.electron
37 | // the electron object (second arg) can contain functions, strings, bools, numbers, arrays, obects in value
38 | // data primitives sent on the bridge are immutable and changes in one context won't carry over to another context
39 |
--------------------------------------------------------------------------------
/export.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/export.gif
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/logo.png
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const { app, contextBridge, BrowserWindow, ipcMain, dialog } = require('electron');
2 | const fs = require('fs');
3 | const path = require('path');
4 |
5 | const createWindow = () => {
6 | const win = new BrowserWindow({
7 | width: 1400,
8 | height: 1200,
9 | webPreferences: {
10 | preload: path.resolve(__dirname, 'electron-scripts', 'preload.js'),
11 | devTools: true
12 | }
13 | });
14 |
15 | // Uses Webpack Dev Server in Development
16 | // Must open
17 | // win.loadURL('http://localhost:8080/index.html');
18 | win.loadFile('./dist/index.html');
19 | // const contents = win.webContents
20 | // console.log(contents)
21 | };
22 |
23 | app.whenReady().then(() => {
24 | // Setup an Event listener to listen on the 'export-chart' channel.
25 | // ipcMain.on('export-chart', (event, code, data) => {
26 | // // Make a Direcotry
27 | // // Write the Data file in directory
28 | // // Write the Code as a File in the Directory
29 | // fs.mkdirSync(path.resolve(__dirname, 'temp'));
30 | // fs.writeFileSync(path.resolve(__dirname, 'temp', 'data.json'), data)
31 | // fs.writeFileSync(path.resolve(__dirname, 'temp', 'code.js'), code)
32 | // })
33 | // EXPORT FUNCTIONALITY
34 | // ipcMain.handle & ipcRenderer.invoke allows for a two-way communication between renderer and main
35 | // BarChartCodePreview > ipcMain.handle > > .then( {canceled, filePaths})
36 | ipcMain.handle('show-save-dialog', (event) => {
37 | const saveDialogPromise = dialog.showOpenDialog({
38 | properties: ['openDirectory'],
39 | buttonLabel: 'Export'
40 | })
41 | .then(({ canceled, filePaths }) => {
42 | if (canceled) {
43 | console.log('Request Canceled')
44 | return
45 | } else {
46 | console.log('File Selected:', filePaths)
47 | return filePaths[0]
48 | }
49 | })
50 | .catch(err => console.log('ERROR on "show-save-dialog" event: ', err))
51 |
52 | // return promise
53 | return saveDialogPromise
54 | });
55 |
56 | createWindow();
57 | });
58 |
59 |
--------------------------------------------------------------------------------
/npm_package_entry.js:
--------------------------------------------------------------------------------
1 | import BarChart from './src/components/Charts/BarChart/JSX/BarChart'
2 | import Histogram from './src/components/Charts/Histogram/JSX/Histogram'
3 | import LineChart from './src/components/Charts/LineChart/JSX/LineChart'
4 | import PieChart from './src/components/Charts/PieChart/JSX/PieChart'
5 | import ScatterPlot from './src/components/Charts/ScatterPlot/JSX/ScatterPlot'
6 |
7 | export {BarChart, Histogram, LineChart, PieChart, ScatterPlot}
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ad3lie",
3 | "version": "0.0.5",
4 | "description": "",
5 | "main": "main.js",
6 | "exports": {
7 | ".": "./npm_package_entry.js"
8 | },
9 | "scripts": {
10 | "start": "webpack && electron .",
11 | "build": "webpack",
12 | "start-dev-server": "webpack serve",
13 | "mac:installer": "electron-builder --mac",
14 | "win:installer": "electron-builder --win",
15 | "linux:installer": "electron-builder --linux",
16 | "tsc": "tsc",
17 | "lint": "eslint . --ext .ts",
18 | "lint-and-fix": "eslint . --ext .ts --fix",
19 | "prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/oslabs-beta/ad3lie.git"
24 | },
25 | "build": {
26 | "appId": "com.electron.d3act",
27 | "mac": {
28 | "category": "public.app-category.developer-tools",
29 | "icon": "penguin-icon.png",
30 | "target": [
31 | "dmg"
32 | ]
33 | },
34 | "dmg": {
35 | "title": "ad3lie",
36 | "backgroundColor": "#ffffff",
37 | "window": {
38 | "width": "400",
39 | "height": "300"
40 | },
41 | "contents": [
42 | {
43 | "x": 100,
44 | "y": 100
45 | },
46 | {
47 | "x": 300,
48 | "y": 100,
49 | "type": "link",
50 | "path": "/Applications"
51 | }
52 | ]
53 | },
54 | "win": {
55 | "target": [
56 | "nsis"
57 | ],
58 | "icon": "penguin-icon.png"
59 | },
60 | "nsis": {
61 | "oneClick": false,
62 | "uninstallDisplayName": "ad3lie-uninstaller",
63 | "allowToChangeInstallationDirectory": true
64 | },
65 | "linux": {
66 | "target": [
67 | "Appimage"
68 | ]
69 | },
70 | "directories": {
71 | "output": "installer"
72 | }
73 | },
74 | "keywords": [],
75 | "author": "",
76 | "license": "ISC",
77 | "bugs": {
78 | "url": "https://github.com/oslabs-beta/ad3lie/issues"
79 | },
80 | "homepage": "https://github.com/oslabs-beta/ad3lie#readme",
81 | "devDependencies": {
82 | "@babel/cli": "^7.17.10",
83 | "@babel/core": "^7.18.5",
84 | "@babel/preset-env": "^7.18.0",
85 | "@babel/preset-react": "^7.17.12",
86 | "@babel/preset-typescript": "^7.17.12",
87 | "@types/d3": "7.4.0",
88 | "@types/lodash": "^4.14.182",
89 | "@types/react": "^18.0.9",
90 | "@types/react-dom": "^18.0.5",
91 | "@typescript-eslint/eslint-plugin": "^5.26.0",
92 | "@typescript-eslint/parser": "^5.26.0",
93 | "autoprefixer": "^10.4.7",
94 | "babel-loader": "^8.2.5",
95 | "css-loader": "^6.7.1",
96 | "d3": "^7.4.4",
97 | "dedent-js": "^1.0.1",
98 | "electron": "^18.2.4",
99 | "electron-builder": "^23.0.3",
100 | "eslint": "^8.16.0",
101 | "eslint-config-airbnb-typescript": "^17.0.0",
102 | "eslint-config-prettier": "^8.5.0",
103 | "eslint-plugin-prettier": "^4.0.0",
104 | "eslint-plugin-react": "^7.27.1",
105 | "eslint-plugin-react-hooks": "^4.3.0",
106 | "express": "^4.17.3",
107 | "file-loader": "^6.2.0",
108 | "html-react-parser": "^1.4.14",
109 | "html-webpack-plugin": "^5.5.0",
110 | "lodash": "^4.17.21",
111 | "node-polyfill-webpack-plugin": "^1.1.4",
112 | "path-browserify": "^1.0.1",
113 | "postcss": "^8.4.14",
114 | "postcss-loader": "^7.0.0",
115 | "prettier": "^2.6.2",
116 | "prettier-format": "^3.1.0",
117 | "prettier-standalone": "^1.3.1-0",
118 | "react": "^18.1.0",
119 | "react-dom": "^18.1.0",
120 | "react-responsive-carousel": "^3.2.23",
121 | "react-router-dom": "^6.3.0",
122 | "react-test-renderer": "^18.1.0",
123 | "resize-observer-polyfill": "^1.5.1",
124 | "rollup": "^2.61.1",
125 | "rollup-plugin-cleanup": "^3.2.1",
126 | "rollup-plugin-prettier": "^2.2.0",
127 | "rollup-plugin-size": "^0.1.0",
128 | "rollup-plugin-strip-banner": "^2.0.0",
129 | "rollup-plugin-terser": "^7.0.2",
130 | "rollup-plugin-visualizer": "^5.5.2",
131 | "style-loader": "^3.3.1",
132 | "svg-inline-loader": "^0.8.2",
133 | "tailwindcss": "^3.0.24",
134 | "ts-loader": "^9.3.0",
135 | "typescript": "^4.6.4",
136 | "undefined": "^0.1.0",
137 | "webpack": "^5.72.1",
138 | "webpack-cli": "^4.10.0",
139 | "webpack-dev-server": "^4.9.0"
140 | },
141 | "dependencies": {
142 | "@reduxjs/toolkit": "^1.8.2",
143 | "asar": "^3.1.0",
144 | "babel-plugin-macros": "^3.1.0",
145 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
146 | "babel-prettier-parser": "^0.10.8",
147 | "eslint-config-airbnb": "^19.0.4",
148 | "eslint-plugin-import": "^2.26.0",
149 | "eslint-plugin-jsx-a11y": "^6.5.1",
150 | "express": "^4.17.3",
151 | "file-loader": "^6.2.0",
152 | "lodash": "^4.17.21",
153 | "node-polyfill-webpack-plugin": "^1.1.4",
154 | "nodeify": "^1.0.1",
155 | "prettier-format": "^3.1.0",
156 | "prop-types": "^15.8.1",
157 | "react-redux": "^8.0.2",
158 | "react-router-dom": "^6.3.0",
159 | "resize-observer-polyfill": "^1.5.1",
160 | "socket.io-client": "^4.5.1",
161 | "styled-components": "^5.3.5",
162 | "ts-node": "^10.4.0",
163 | "tw-elements": "^1.0.0-alpha12"
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/penguin-icon-old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/penguin-icon-old.png
--------------------------------------------------------------------------------
/penguin-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/penguin-icon.png
--------------------------------------------------------------------------------
/select-properties.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/select-properties.gif
--------------------------------------------------------------------------------
/src/AllRoutes.jsx:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense } from "react";
2 | import { Route, Routes } from "react-router-dom";
3 | import Homepage from "./components/pages/Homepage.jsx";
4 | import dictionary from './app/dictionary.js'
5 |
6 | export const RouteHook = () => {
7 |
8 | const Container = lazy(() => import(`./components/ChartComponents/JSX/Container.jsx`))
9 |
10 | // Programmatically creating unique routes for all charts in our dictionary
11 | const routes = Object.values(dictionary).reduce((acc, { type, name, children, properties }) => {
12 | acc.push(
)
16 | const useCodeRef = (processNode) => {
17 | const [node, setNode] = useState(null);
18 | const setCodeRef = useCallback(newNode => {
19 | if (newNode) {
20 | // console.log("ref", node); // node = codeRef.current //
21 | setNode(processNode(newNode));
22 | }
23 | }, []);
24 | return [node, setCodeRef]
25 | }
26 |
27 | const [codeRef, setCodeRef] = useCodeRef(node => node)
28 |
29 | // To reflect on every code change, we use useEffect to reassign the new codeRef on rerender
30 | useEffect(() => {
31 | console.log(codeRef)
32 | }, [codeRef]);
33 |
34 | return (
35 |
36 |
37 |
38 | {code}
39 |
40 |
41 |
42 |
43 | {/* Something weird happening when putting export button here.
44 | Think it's setting codeRef in an infinite loop which leads to stack overflow lol
45 |
46 | */}
47 |
48 |
83 |
84 |
85 | )
86 | }
87 |
88 | export default BarChartCodePreview
89 |
--------------------------------------------------------------------------------
/src/Archives/charts/Histogram/Histogram.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 | import * as d3 from "d3"
4 |
5 |
6 | import React, { useState, useEffect, useMemo, Fragment} from 'react';
7 | import * as d3 from 'd3';
8 | import PropTypes from "prop-types"
9 | import { useChartDimensions, accessorPropsType } from '../../../../utils/utils.js';
10 | import Axis_noticks from "../../../ChartComponents/JSX/Axis_noticks.jsx"
11 | import Axis from "../../../ChartComponents/JSX/Axis.jsx"
12 | import Rectangle from "../../../ChartComponents/JSX/Rectangle.jsx"
13 | import Bars from "../../../ChartComponents/JSX/Bars.jsx"
14 | import Chart from "../../../ChartComponents/JSX/Chart.jsx"
15 | import { parseDate, dateAccessor, temperatureAccessor, humidityAccessor, getData } from '../../ScatterPlot/App'
16 |
17 | const Histogram = ({ data, xKey, yKey, xAxisLabel, yAxisLabel, height, width }) => {
18 | const gradientColors = ["#9980FA", "rgb(226, 222, 243)"]
19 | const Histogram = ({ data, xAccessor, label }) => {
20 | const gradientId = useUniqueId("Histogram-gradient")
21 | const [ref, dimensions] = useChartDimensions({
22 | marginBottom: 77,
23 | })
24 |
25 | const numberOfThresholds = 9
26 |
27 | const xScale = d3.scaleLinear()
28 | .domain(d3.extent(data, xAccessor))
29 | .range([0, dimensions.boundedWidth])
30 | .nice(numberOfThresholds)
31 |
32 | const binsGenerator = d3.bin()
33 | .domain(xScale.domain())
34 | .value(xAccessor)
35 | .thresholds(xScale.ticks(numberOfThresholds))
36 |
37 | const bins = binsGenerator(data)
38 |
39 | const yAccessor = d => d.length
40 | const yScale = d3.scaleLinear()
41 | .domain([0, d3.max(bins, yAccessor)])
42 | .range([dimensions.boundedHeight, 0])
43 | .nice()
44 |
45 | const barPadding = 2
46 |
47 | const xAccessorScaled = d => xScale(d.x0) + barPadding
48 | const yAccessorScaled = d => yScale(yAccessor(d))
49 | const widthAccessorScaled = d => xScale(d.x1) - xScale(d.x0) - barPadding
50 | const heightAccessorScaled = d => dimensions.boundedHeight - yScale(yAccessor(d))
51 | const keyAccessor = (d, i) => i
52 |
53 | return (
54 |
55 |
56 |
57 |
63 |
64 |
70 |
76 |
85 |
86 |
87 | )
88 | }
89 |
90 | Histogram.propTypes = {
91 | xAccessor: accessorPropsType,
92 | yAccessor: accessorPropsType,
93 | xLabel: PropTypes.string,
94 | yLabel: PropTypes.string,
95 | }
96 |
97 | Histogram.defaultProps = {
98 | xAccessor: d => d.x,
99 | yAccessor: d => d.y,
100 | }
101 | }
102 |
103 | export default Histogram
--------------------------------------------------------------------------------
/src/Archives/charts/Histogram/HistogramCodePreview.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { generateChartCode, CodeBlock, Code } from '../../utils/CodePreview';
3 |
4 | const HistogramCodePreview = ({ name, data, children, ...codeProps }) => {
5 |
6 | const code = generateChartCode(`${name}`, codeProps, {
7 | dataKey: data !== undefined ? 'data' : undefined,
8 | children: children,
9 | defaults: {},
10 | pkg: 'histogram',
11 | })
12 |
13 | return (
14 |
15 |
16 | {code}
17 |
18 | )
19 | }
20 |
21 | export default HistogramCodePreview
--------------------------------------------------------------------------------
/src/Archives/charts/LineChartCodePreview.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function LineChartCodePreview(props) {
4 | return(
5 |
6 | Code Preview
7 |
8 | )
9 | }
10 |
11 | export default LineChartCodePreview;
--------------------------------------------------------------------------------
/src/Archives/charts/LineChartForm.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const LineChartForm = ({
5 | data,
6 | xKey,
7 | yKey,
8 | xAxisLabel,
9 | yAxisLabel,
10 | height,
11 | width,
12 | handlers: {
13 | handleData,
14 | handleXKey,
15 | handleYKey,
16 | handleXAxisLabel,
17 | handleYAxisLabel,
18 | handleWidth,
19 | handleHeight
20 | }
21 | }) => {
22 | return (
23 | //
24 | // Chart Customizer Form
25 | //
26 |
145 | //
146 | //
147 | );
148 | };
149 |
150 | export default LineChartForm;
151 |
--------------------------------------------------------------------------------
/src/Archives/charts/Scatterplot/ScatterPlotCodePreview.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { generateChartCode, CodeBlock, Code } from '../../../../utils/CodePreview';
3 |
4 | const ScatterPlotCodePreview = ({ name, data, children, ...codeProps }) => {
5 |
6 | const code = generateChartCode(`${name}`, codeProps, {
7 | dataKey: data !== undefined ? 'data' : undefined,
8 | children: children,
9 | defaults: {},
10 | pkg: 'barchart',
11 | })
12 |
13 | return (
14 |
15 |
16 | {code}
17 |
18 | )
19 | }
20 |
21 | export default ScatterPlotCodePreview
--------------------------------------------------------------------------------
/src/Archives/reducers/barPaddingSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 2
5 | };
6 |
7 | export const barPaddingSlice = createSlice({
8 | name: 'barPadding',
9 | initialState,
10 | reducers: {
11 | changeBarPadding: (state, action) => {
12 | console.log('Changing barPadding');
13 | state.value = +action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeBarPadding } = barPaddingSlice.actions;
20 |
21 | export default barPaddingSlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/reducers/dataSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 | import { sampleData } from '../../utils/dummypenguinsdata';
3 | //this slice/reducer should be the same as what handleData fn does
4 |
5 | const initialState = {
6 | value: sampleData
7 | };
8 |
9 | export const dataSlice = createSlice({
10 | name: 'data',
11 | initialState,
12 | reducers: {
13 | // Redux Toolkit allows us to write "mutating" logic in reducers. It
14 | // doesn't actually mutate the state because it uses the Immer library,
15 | // which detects changes to a "draft state" and produces a brand new
16 | // immutable state based off those changes
17 | // increment: (state) => {
18 | // state.value += 1;
19 | // },
20 | // decrement: (state) => {
21 | // state.value -= 1;
22 | // },
23 | // incrementByAmount: (state, action) => {
24 | // state.value += action.payload;
25 | // },
26 | changeData: (state, action) => {
27 | console.log('changing data');
28 | state.value = JSON.parse(action.payload);
29 | }
30 | }
31 | });
32 |
33 | // Action creators are generated for each case reducer function
34 | export const { changeData } = dataSlice.actions;
35 |
36 | export default dataSlice.reducer;
37 |
--------------------------------------------------------------------------------
/src/Archives/reducers/heightSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 500
5 | };
6 |
7 | export const heightSlice = createSlice({
8 | name: 'height',
9 | initialState,
10 | reducers: {
11 | changeHeight: (state, action) => {
12 | if (+action.payload < 100) {
13 | console.log(
14 | 'Value must not be less than 100 px. Resetting to default.'
15 | );
16 | action.payload = 500;
17 | console.log(action.payload);
18 | state.value = action.payload;
19 | return;
20 | }
21 | console.log('Changing height');
22 | state.value = +action.payload;
23 | }
24 | }
25 | });
26 |
27 | // Action creators are generated for each case reducer function
28 | export const { changeHeight } = heightSlice.actions;
29 |
30 | export default heightSlice.reducer;
31 |
--------------------------------------------------------------------------------
/src/Archives/reducers/nameSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: ''
5 | };
6 |
7 | export const nameSlice = createSlice({
8 | name: 'name',
9 | initialState,
10 | reducers: {
11 | changeName: (state, action) => {
12 | console.log('Changing Chart name');
13 | state.value = action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeName } = nameSlice.actions;
20 |
21 | export default nameSlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/reducers/radiusSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 5
5 | };
6 |
7 | export const radiusSlice = createSlice({
8 | name: 'radius',
9 | initialState,
10 | reducers: {
11 | changeRadius: (state, action) => {
12 | if (+action.payload < 1) {
13 | console.log('Value must not be less than 1. Resetting to default.');
14 | action.payload = 5;
15 | console.log(action.payload);
16 | state.value = action.payload;
17 | return;
18 | }
19 | console.log('Changing radius');
20 | state.value = +action.payload;
21 | }
22 | }
23 | });
24 |
25 | // Action creators are generated for each case reducer function
26 | export const { changeRadius } = radiusSlice.actions;
27 |
28 | export default radiusSlice.reducer;
29 |
--------------------------------------------------------------------------------
/src/Archives/reducers/thresholdsSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 9
5 | };
6 |
7 | export const thresholdsSlice = createSlice({
8 | name: 'thresholds',
9 | initialState,
10 | reducers: {
11 | changeThresholds: (state, action) => {
12 | console.log('Changing thresholds');
13 | state.value = +action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeThresholds } = thresholdsSlice.actions;
20 |
21 | export default thresholdsSlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/reducers/widthSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 500
5 | };
6 |
7 | export const widthSlice = createSlice({
8 | name: 'width',
9 | initialState,
10 | reducers: {
11 | changeWidth: (state, action) => {
12 | if (+action.payload < 100) {
13 | console.log(
14 | 'Value must not be less than 100 px. Resetting to default.'
15 | );
16 | action.payload = 500;
17 | console.log(action.payload);
18 | state.value = action.payload;
19 | return;
20 | }
21 | console.log('Changing width');
22 | state.value = +action.payload;
23 | }
24 | }
25 | });
26 |
27 | // Action creators are generated for each case reducer function
28 | export const { changeWidth } = widthSlice.actions;
29 |
30 | export default widthSlice.reducer;
31 |
--------------------------------------------------------------------------------
/src/Archives/reducers/xAxisLabelSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 'X-axis: Species'
5 | };
6 |
7 | export const xAxisLabelSlice = createSlice({
8 | name: 'xAxisLabel',
9 | initialState,
10 | reducers: {
11 | changeXAxisLabel: (state, action) => {
12 | console.log('Changing X Axis Label');
13 | state.value = action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeXAxisLabel } = xAxisLabelSlice.actions;
20 |
21 | export default xAxisLabelSlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/reducers/xKeySlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: ''
5 | };
6 |
7 | export const xKeySlice = createSlice({
8 | name: 'xKey',
9 | initialState,
10 | reducers: {
11 | changeXKey: (state, action) => {
12 | console.log('Changing X Key');
13 | state.value = action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeXKey } = xKeySlice.actions;
20 |
21 | export default xKeySlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/reducers/yAxisLabelSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: 'Y-axis: Body Mass'
5 | };
6 |
7 | export const yAxisLabelSlice = createSlice({
8 | name: 'yAxisLabel',
9 | initialState,
10 | reducers: {
11 | changeYAxisLabel: (state, action) => {
12 | console.log('Changing Y Axis Label');
13 | state.value = action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeYAxisLabel } = yAxisLabelSlice.actions;
20 |
21 | export default yAxisLabelSlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/reducers/yKeySlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | value: ''
5 | };
6 |
7 | export const yKeySlice = createSlice({
8 | name: 'yKey',
9 | initialState,
10 | reducers: {
11 | changeYKey: (state, action) => {
12 | console.log('Changing Y Key');
13 | state.value = action.payload;
14 | }
15 | }
16 | });
17 |
18 | // Action creators are generated for each case reducer function
19 | export const { changeYKey } = yKeySlice.actions;
20 |
21 | export default yKeySlice.reducer;
22 |
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/Axis.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 | import * as d3 from 'd3'
4 | import { dimensionsPropsType } from "./utils";
5 | import { useChartDimensions } from "./Chart";
6 |
7 | const axisComponentsByDimension = {
8 | x: AxisHorizontal,
9 | y: AxisVertical,
10 | }
11 | const Axis = ({ dimension, ...props }) => {
12 | const dimensions = useChartDimensions()
13 | const Component = axisComponentsByDimension[dimension]
14 | if (!Component) return null
15 |
16 | return (
17 |
21 | )
22 | }
23 |
24 | Axis.propTypes = {
25 | dimension: PropTypes.oneOf(["x", "y"]),
26 | scale: PropTypes.func,
27 | label: PropTypes.string,
28 | formatTick: PropTypes.func,
29 | }
30 |
31 | Axis.defaultProps = {
32 | dimension: "x",
33 | scale: null,
34 | formatTick: d3.format(","),
35 | }
36 |
37 | export default Axis
38 |
39 |
40 | function AxisHorizontal ({ dimensions, label, formatTick, scale, ...props }) {
41 | const numberOfTicks = dimensions.boundedWidth < 600
42 | ? dimensions.boundedWidth / 100
43 | : dimensions.boundedWidth / 250
44 |
45 | const ticks = scale.ticks(numberOfTicks)
46 |
47 | return (
48 |
49 |
53 |
54 | {ticks.map((tick, i) => (
55 |
60 | { formatTick(tick) }
61 |
62 | ))}
63 |
64 | {label && (
65 |
69 | { label }
70 |
71 | )}
72 |
73 | )
74 | }
75 |
76 | function AxisVertical ({ dimensions, label, formatTick, scale, ...props }) {
77 | const numberOfTicks = dimensions.boundedHeight / 70
78 |
79 | const ticks = scale.ticks(numberOfTicks)
80 |
81 | return (
82 |
83 |
87 |
88 | {ticks.map((tick, i) => (
89 |
94 | { formatTick(tick) }
95 |
96 | ))}
97 |
98 | {label && (
99 |
105 | { label }
106 |
107 | )}
108 |
109 | )
110 | }
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/AxisHorizontal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name: AxisHorizontal
3 | * @description: produces a modularized template for the Horizontal Axis
4 | * @param: (object with keys domain: number[] and range: number[]), pixelsPerTick: number
5 | * @returns: A rendering of the X-Axis
6 | * @author: Antonio Ayala, Sophia Chiao
7 | */
8 |
9 |
10 | // import modules and libraries
11 | import React, { useMemo } from 'react';
12 | import { scaleLinear, domain, range, ticks } from 'd3';
13 |
14 | export const AxisHorizontal = ({
15 | domain = [0, 100],
16 | range = [10, 290],
17 | pixelsPerTick = 30,
18 | }) => {
19 | // creating the tick marks for the X-Axis (Axis Bottom) utilizing the React Hook useMemo
20 | // useMemo returns a memoized value https://dmitripavlutin.com/react-usememo-hook/
21 | const ticks = useMemo(() => {
22 | // set the xScale using d3's scaleLinear() method
23 | // to create a visual scale point. This method is used to transform data values into
24 | // visual variables.
25 | // .domain is generating every intermediate tick mark
26 | // .range is setting the range at which these tick marks will reside
27 | const xScale = scaleLinear()
28 | .domain(domain)
29 | .range(range);
30 |
31 | // declaring variable width to determine the width of the graph
32 | const width = range[1] - range[0];
33 | // console.log("width: ", width);
34 |
35 | // determine how many ticks to use/can fit in the axis based on range -> width/pixelsPerTick
36 | const numberOfTicksTarget = Math.max(1, Math.floor(width / pixelsPerTick));
37 | // console.log("numberOfTicksTarget: ", numberOfTicksTarget);
38 |
39 | return (
40 | xScale
41 | // using d3's .ticks() method to generate ticks, quantity of ticks based on input value
42 | .ticks(numberOfTicksTarget)
43 | // creating the minor label
44 | .map((value) => ({
45 | value,
46 | xOffset: xScale(value),
47 | }))
48 | );
49 | }, [
50 | // whenever domain and range change, update tickmarks
51 | domain.join("-"),
52 | range.join("-"),
53 | ]);
54 |
55 | return (
56 |
89 | );
90 | };
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/AxisVertical.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @name: AxisVertical
3 | * @description: produces a modularized template for the Vertical Axis
4 | * @param: (object with keys domain: number[] and range: number[]), pixelsPerTick: number
5 | * @returns: A rendering of the Y-Axis
6 | * @author: Antonio Ayala, Sophia Chiao
7 | */
8 |
9 |
10 | // import modules and libraries
11 | import React, { useMemo } from 'react';
12 | import { scaleLinear, domain, range, ticks, axisLeft, scale } from 'd3';
13 |
14 | export const AxisVertical = ({
15 | domain = [0, 100],
16 | range = [10, 290],
17 | pixelsPerTick = 30,
18 | }) => {
19 | // creating the tick marks for the X-Axis (Axis Bottom) utilizing the React Hook useMemo
20 | // useMemo returns a memoized value https://dmitripavlutin.com/react-usememo-hook/
21 | const ticks = useMemo(() => {
22 | // set the yScale using d3's scaleLinear() method
23 | // to create a visual scale point. This method is used to transform data values into
24 | // visual variables.
25 | // .domain is generating every intermediate tick mark
26 | // .range is setting the range at which these tick marks will reside
27 | const yScale = scaleLinear().domain(domain.reverse()).range(range)
28 |
29 | // declaring variable height to determine the height of the graph
30 | const height = range[1] - range[0];
31 |
32 | // determine how many ticks to use/can fit in the axis based on range -> height/pixelsPerTick
33 | const numberOfTicksTarget = Math.max(1, Math.floor(height / pixelsPerTick));
34 |
35 | return (
36 | yScale
37 | // using d3's .ticks() method to generate ticks, quantity of ticks based on input value
38 | .ticks(numberOfTicksTarget)
39 | // creating the minor label
40 | .map((value) => ({
41 | value: value,
42 | yOffset: yScale(value),
43 | }))
44 | );
45 | }, [
46 | // whenever domain and range change, update tickmarks
47 | domain.join("-"),
48 | range.join("-"),
49 | ]);
50 |
51 | return (
52 |
84 | );
85 | };
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/Axis_comments.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @name: Axis
3 | * @description: produces a modularized template for the Horizontal and Vertical Axes
4 | * @param:
5 | * @returns:
6 | * @author: Antonio Ayala, Sophia Chiao
7 | */
8 |
9 | // import modules and libraries
10 | import React from "react"
11 | import PropTypes from "prop-types" // This is essentially typescript but REACT
12 | import * as d3 from 'd3'
13 | import { useChartDimensions } from './useChartDimensions_comments'
14 |
15 | // declaring a const axisComponentsByDimension set where the x and y keys are set to the
16 | // functions AxisHorizontal and AxisVertical through JavaScript/React Hoisting
17 | const axisComponentsByDimension = {
18 | x: AxisHorizontal,
19 | y: AxisVertical,
20 | }
21 |
22 | // declaring a const Axis that returns the invocation of either AxisHorizontal or AxisVertical
23 | // dimension : "x" or "y"
24 | const Axis = ({ dimension, ...props }) => {
25 |
26 | // returns the new dimensions of the window
27 | const dimensions = useChartDimensions()
28 |
29 | // setting the Component variable to be the function AxisHorizontal or AxisVertical
30 | const Component = axisComponentsByDimension[dimension]
31 |
32 | // if Component does not exist, return null
33 | if (!Component) return null
34 |
35 | return (
36 | // Rendering the AxisHorizontal / AxisVertical
37 |
41 | )
42 | }
43 |
44 | // THIS CAN BE REPLACED BY TYPESCRIPT
45 | Axis.propTypes = {
46 | dimension: PropTypes.oneOf(["x", "y"]),
47 | scale: PropTypes.func,
48 | label: PropTypes.string,
49 | formatTick: PropTypes.func,
50 | }
51 |
52 | // defaultProps is a React component property that allows you to set default values for the props argument
53 | Axis.defaultProps = {
54 | dimension: "x",
55 | scale: null,
56 | formatTick: d3.format(","),
57 | }
58 |
59 | // Exporting the Axis function
60 | export default Axis
61 |
62 | /******************************************************************************************************************/
63 | /******************** Below are helper functions for Axis to render the specified X- or Y- Axis *******************/
64 | /******************************************************************************************************************/
65 |
66 | // Creates the rendering of the Horizontal Axis
67 | // input: dimensions, label, formatTick, scale, data, ...props
68 | // output: rendering of the X-Axis
69 | function AxisHorizontal ({ dimensions, label, formatTick, scale, data, ...props }) {
70 | const numberOfTicks = dimensions.boundedWidth < 600
71 | ? dimensions.boundedWidth / 100
72 | : dimensions.boundedWidth / 250
73 |
74 | // const ticks = scale.ticks(numberOfTicks)
75 | // console.log(scale)
76 | // const ticks = axis(scale).tickValues([data.map((d) => d.product)])
77 |
78 | return (
79 |
80 |
84 |
85 | {/* {ticks.map((tick, i) => (
86 |
91 | { formatTick(tick) }
92 |
93 | ))} */}
94 |
95 | {label && (
96 |
100 | { label }
101 |
102 | )}
103 |
104 | )
105 | }
106 |
107 | function AxisVertical ({ dimensions, label, formatTick, scale, ...props }) {
108 | const numberOfTicks = dimensions.boundedHeight / 70
109 |
110 | const ticks = scale.ticks(numberOfTicks)
111 |
112 | return (
113 |
114 |
118 |
119 | {ticks.map((tick, i) => (
120 |
125 | { formatTick(tick) }
126 |
127 | ))}
128 |
129 | {label && (
130 |
136 | { label }
137 |
138 | )}
139 |
140 | )
141 | }
142 |
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/Bars.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import PropTypes from "prop-types"
3 | import * as d3 from 'd3'
4 | import { accessorPropsType } from './Archive/accessorPropsType'
5 | import { useAccessor } from './Archive/useAccessor'
6 |
7 | const Bars = ({ data, keyAccessor, xAccessor, yAccessor, widthAccessor, heightAccessor, ...props }) => (
8 |
9 | {data.map((d, i) => (
10 |
18 | ))}
19 |
20 | )
21 |
22 | Bars.propTypes = {
23 | data: PropTypes.array,
24 | keyAccessor: accessorPropsType,
25 | xAccessor: accessorPropsType,
26 | yAccessor: accessorPropsType,
27 | widthAccessor: accessorPropsType,
28 | heightAccessor: accessorPropsType,
29 | }
30 |
31 | Bars.defaultProps = {
32 | }
33 |
34 | export default Bars
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/Chart.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext } from "react"
2 | import { dimensionsPropsType } from "./utils"
3 |
4 | import "./Chart.css"
5 |
6 | const ChartContext = createContext()
7 | export const useChartDimensions = () => useContext(ChartContext)
8 |
9 | const Chart = ({ dimensions, children }) => (
10 |
11 |
16 |
17 | )
18 |
19 | Chart.propTypes = {
20 | dimensions: dimensionsPropsType
21 | }
22 |
23 | Chart.defaultProps = {
24 | dimensions: {}
25 | }
26 |
27 | export default Chart
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/Chart_comments.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @pr
3 | * @name: Chart
4 | * @description: produces the complete Chart with axes
5 | * @param:
6 | * @returns:
7 | * @author: Antonio Ayala, Sophia Chiao
8 | */
9 |
10 |
11 | // import modules and libraries
12 | import React, { createContext, useContext } from "react";
13 | import { dimensionsPropsType } from './dimensionsPropsType';
14 | import "./Chart.css";
15 |
16 | // setting the return of invoking createContext() to ChartContext
17 | // createContext lets us pass a value deep into the component tree without explicitly threading it through every component.
18 | // the return of invoking createContext() returns an object with { Provider, Consumer }
19 | const ChartContext = createContext();
20 |
21 | // useContext is a React Hook that accepts a context object (value returned from React.createContext) and returns
22 | // the current context value for that context.
23 | // In this case, it would be null
24 | export const useChartDimensions = () => useContext(ChartContext);
25 |
26 |
27 | const Chart = ({ dimensions, children }) => (
28 | // the Context.Provider is an effect of createContext() and allows the designated properties to be passed down
29 | // Any component can read it, no matter how deep it is.
30 | // In this case, we are passing to other components dimensions
31 |
32 |
37 |
38 | )
39 |
40 | Chart.propTypes = {
41 | dimensions: dimensionsPropsType
42 | }
43 |
44 | Chart.defaultProps = {
45 | dimensions: {}
46 | }
47 |
48 | export default Chart
49 |
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/CodePreview.js:
--------------------------------------------------------------------------------
1 | import forOwn from 'lodash/forOwn';
2 | import isPlainObject from 'lodash/isPlainObject';
3 | import isArray from 'lodash/isArray';
4 | import isString from 'lodash/isString';
5 | import isNumber from 'lodash/isNumber';
6 | import isBoolean from 'lodash/isBoolean';
7 | import dedent from 'dedent-js';
8 | import styled from 'styled-components';
9 |
10 | export const indent = (content, spaces = 8) =>
11 | content
12 | .split('\n')
13 | .map((line, i) => {
14 | if (i === 0) return line;
15 | return `${' '.repeat(spaces)}${line}`;
16 | })
17 | .join('\n');
18 |
19 | /* convert all inputs to json 🙃 */
20 | export const toJson = (value) => {
21 | const jsonString = JSON.stringify(value, null, 4);
22 | const normalized = jsonString
23 | .replace(/^(\s+)"([a-z]{1}[a-z]*)"\: /gim, (_match, space, key) => {
24 | return `${space}${key}: `;
25 | })
26 | .replace(/"/gm, `'`);
27 |
28 | if (normalized.length < 80) {
29 | return normalized.replace(/\n/gm, ' ').replace(/\s{2,}/g, ' ');
30 | }
31 | return indent(normalized);
32 | };
33 |
34 | export const generateChartCode = (
35 | name,
36 | props,
37 | { dataKey, children, defaults, pkg }
38 | ) => {
39 | const properties = [];
40 | let args = '';
41 |
42 | if (dataKey !== undefined) {
43 | properties.push(`${dataKey}={${dataKey}}`);
44 | args = `{ ${dataKey} /* see ${dataKey} from PropsData file */ }`;
45 | }
46 |
47 | forOwn(props, (_value, key) => {
48 | if (_value === undefined) return;
49 | if (defaults && defaults[key] === _value) return;
50 | if (key === 'theme') return;
51 |
52 | let value;
53 | if (isPlainObject(_value)) {
54 | value = `{${toJson(_value)}}`;
55 | } else if (isArray(_value)) {
56 | value = `{${toJson(_value)}}`;
57 | } else if (isString(_value)) {
58 | value = `"${_value}"`;
59 | } else if (isBoolean(_value)) {
60 | value = `{${_value ? 'true' : 'false'}}`;
61 | } else if (isNumber(_value)) {
62 | value = `{${_value}}`;
63 | } else if (typeof _value === 'function') {
64 | value = `{${indent(dedent(_value.toString()), 8)}}`;
65 | } else if (_value === null) {
66 | value = `{null}`;
67 | } else {
68 | value = _value;
69 | }
70 |
71 | properties.push(`${key}=${value}`);
72 | });
73 |
74 | const install = `// npm install @d3act @d3act/${pkg}`;
75 |
76 | const imports = [name, ...children.map((c) => c)].map(
77 | (i) => `import { ${i} } from 'd3act/components'`
78 | );
79 |
80 | let warning = '';
81 | if (name) {
82 | warning = [
83 | ``,
84 | `// Before use, remember to npm i all dependencies`,
85 | `// and the @d3act component library to use your charts,`,
86 | `// otherwise, no charts will be rendered.`,
87 | `// Copy the following code to your component file`,
88 | `// along with your PropsData.txt file .`
89 | ].join('\n');
90 | }
91 |
92 | return `// install (please make sure versions match peerDependencies)
93 | ${install}
94 | ${imports.join('\n')}
95 | ${warning}
96 | const My${name} = (${args}) => (
97 | <${name}
98 | ${properties.join('\n ')}
99 | />
100 | )`;
101 | };
102 |
103 | //just using styled components here only for testing html preview
104 | export const CodeBlock = styled.pre`
105 | margin: 0;
106 | font-size: 0.8rem;
107 | line-height: 1.7;
108 | padding: 12px 20px;
109 | `;
110 |
111 | export const Code = styled.div`
112 | position: absolute;
113 | top: 46px;
114 | bottom: 0;
115 | width: 100%;
116 | overflow: auto;
117 | `;
118 |
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/accessorPropsType.jsx:
--------------------------------------------------------------------------------
1 | import PropTypes from "prop-types";
2 | import React from "react"
3 |
4 |
5 | export const accessorPropsType = (
6 | PropTypes.oneOfType([
7 | PropTypes.func,
8 | PropTypes.number,
9 | ])
10 | )
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/combineChartDimensions.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @pr
3 | * @name: combineChartsDimensions
4 | * @description: creates a new chart dimension object based on window changes
5 | * @param:
6 | * @returns:
7 | * @author: Antonio Ayala, Sophia Chiao
8 | */
9 |
10 | // import modules and libraries
11 |
12 | export const combineChartDimensions = dimensions => {
13 | let parsedDimensions = {
14 | marginTop: 40,
15 | marginRight: 30,
16 | marginBottom: 40,
17 | marginLeft: 75,
18 | ...dimensions,
19 | }
20 |
21 | return {
22 | ...parsedDimensions,
23 | boundedHeight: Math.max(parsedDimensions.height - parsedDimensions.marginTop - parsedDimensions.marginBottom, 0),
24 | boundedWidth: Math.max(parsedDimensions.width - parsedDimensions.marginLeft - parsedDimensions.marginRight, 0),
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/createFiles.js:
--------------------------------------------------------------------------------
1 | // Creates all component files (but not the full application files) and places them in a "components" directory
2 | const createFiles = () => {
3 | let dir = path;
4 | // if (exportAppBool === false) {
5 | // if (!dir.match(/components|\*$/)) {
6 | // if (window.api.existsSync(`${dir}/src`)) {
7 | // dir = `${dir}/src`;
8 | // }
9 | // dir = `${dir}/components`;
10 | // if (!window.api.existsSync(dir)) {
11 | // window.api.mkdirSync(dir);
12 | // }
13 | // }
14 | // } else if (exportAppBool) {
15 | // if (!dir.match(/${appName}|\*$/)) {
16 | // dir = `${dir}/${appName}/src/components`;
17 | // }
18 | // }
19 | const promises = [];
20 | components.forEach((component) => {
21 | const newPromise = new Promise((resolve, reject) => {
22 | window.api.writeFileSync(
23 | `${dir}/${component.name}.jsx`,
24 | window.api.formatCode(component.code),
25 | (err) => {
26 | if (err) return reject(err.message);
27 | return resolve(path);
28 | }
29 | );
30 | });
31 | promises.push(newPromise);
32 | });
33 | return Promise.all(promises);
34 | };
35 |
36 | export default createFiles;
37 |
38 | //prop types
39 | // components: any,
40 | // path: string,
41 | // appName: string,
42 | // exportAppBool: boolean
43 |
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/dimensionsPropsType.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @pr
3 | * @name: dimensionsPropsType
4 | * @description: sets the type properties for dimensions
5 | * @param:
6 | * @returns:
7 | * @author: Antonio Ayala, Sophia Chiao
8 | */
9 |
10 | // import modules and libraries
11 | import PropTypes from 'prop-types'
12 |
13 |
14 | export const dimensionsPropsType = (
15 | PropTypes.shape({
16 | height: PropTypes.number,
17 | width: PropTypes.number,
18 | marginTop: PropTypes.number,
19 | marginRight: PropTypes.number,
20 | marginBottom: PropTypes.number,
21 | marginLeft: PropTypes.number,
22 | })
23 | )
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/exportButton.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext, useCallback, useEffect } from 'react';
2 | import StateContext from '../../context/context';
3 | import List from '@material-ui/core/List';
4 | import ListItem from '@material-ui/core/ListItem';
5 | import ListItemText from '@material-ui/core/ListItemText';
6 | import GetAppIcon from '@material-ui/icons/GetApp';
7 | import Button from '@material-ui/core/Button';
8 | import exportProject from '../../utils/exportProject.util';
9 | import createModal from './createModal';
10 | import { styleContext } from '../../containers/AppContainer';
11 |
12 | export default function ExportButton() {
13 | const [modal, setModal] = useState(null);
14 | const [state] = useContext(StateContext);
15 |
16 | const genOptions = [
17 | 'Export components',
18 | 'Export components with application files'
19 | ];
20 | let genOption = 0;
21 |
22 | // Closes out the open modal
23 | const closeModal = () => setModal('');
24 |
25 | const showGenerateAppModal = () => {
26 | const children = (
27 |
28 | {genOptions.map((option, i) => (
29 | chooseGenOptions(i)}
33 | style={{
34 | border: '1px solid #3f51b5',
35 | marginBottom: '2%',
36 | marginTop: '5%'
37 | }}
38 | >
39 |
40 |
41 | ))}
42 |
43 |
44 |
45 |
46 |
47 | );
48 | let testchecked = 0;
49 | // helper function called by showGenerateAppModal
50 | // this function will prompt the user to choose an app directory once they've chosen their export option
51 | const chooseGenOptions = (genOpt) => {
52 | // set export option: 0 --> export only components, 1 --> export full project
53 | genOption = genOpt;
54 | window.api.chooseAppDir();
55 | testchecked = document.getElementById('tests').checked;
56 | closeModal();
57 | };
58 |
59 | // removes all listeners for the app_dir_selected event
60 | // this is important because otherwise listeners will pile up and events will trigger multiple events
61 | window.api.removeAllAppDirChosenListeners();
62 |
63 | // add listener for when an app directory is chosen
64 | // when a directory is chosen, the callback will export the project to the chosen folder
65 | // Note: this listener is imported from the main process via preload.js
66 | window.api.addAppDirChosenListener((path) => {
67 | exportProject(
68 | path,
69 | state.name
70 | ? state.name
71 | : 'New_ReacType_Project_' + Math.ceil(Math.random() * 99).toString(),
72 | genOption,
73 | testchecked,
74 | state.projectType,
75 | state.components,
76 | state.rootComponents
77 | );
78 | });
79 |
80 | // setModal(
81 | // createModal({
82 | // closeModal,
83 | // children,
84 | // message: 'Choose export preference:',
85 | // primBtnLabel: null,
86 | // primBtnAction: null,
87 | // secBtnAction: null,
88 | // secBtnLabel: null,
89 | // open: true
90 | // })
91 | // );
92 | };
93 |
94 | const exportKeyBind = useCallback((e) => {
95 | //Export Project
96 | (e.key === 'e' && e.metaKey) || (e.key === 'e' && e.ctrlKey)
97 | ? showGenerateAppModal()
98 | : '';
99 | }, []);
100 |
101 | useEffect(() => {
102 | document.addEventListener('keydown', exportKeyBind);
103 | return () => {
104 | document.removeEventListener('keydown', exportKeyBind);
105 | };
106 | }, []);
107 | return (
108 |
109 | }
115 | >
116 | EXPORT
117 |
118 | {modal}
119 |
120 | );
121 | }
122 |
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/useAccessor.jsx:
--------------------------------------------------------------------------------
1 | export const useAccessor = (accessor, d, i) => (
2 | typeof accessor == "function" ? accessor(d, i) : accessor
3 | )
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/useChartDimensions_comments.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @pr
3 | * @name: useChartDimensions
4 | * @description: creates a new chart dimension object based on window changes
5 | * @param: passedSettings, and object with a similar model structure as combineChartDimensions' parsedDimensions
6 | * @returns: returns the size of the new window
7 | * @author: Antonio Ayala, Sophia Chiao
8 | */
9 |
10 | // import modules and libraries
11 | import { combineChartDimensions } from './combineChartDimensions'
12 | import { useRef, useState, useEffect } from 'react';
13 |
14 |
15 | // custom React Hook useChartDimensions that helps keep charts responsive and automatically updates any dimensions when
16 | // window is resized
17 | export const useChartDimensions = passedSettings => {
18 |
19 | // React hook that accepts an argument as the initial value and returns a reference (aka ref).
20 | // A reference is an object having a special property: current, which allows access to the updated value through
21 | // ref.current
22 | const ref = useRef()
23 |
24 | // invoking the combineChartDimensions passing in setting preferences and returns an object stored as dimensions
25 | const dimensions = combineChartDimensions(passedSettings)
26 |
27 | // setting the state for the width and height of the windows
28 | const [width, changeWidth] = useState(0)
29 | const [height, changeHeight] = useState(0)
30 |
31 | // React Hook useEffect() tells the React Component to run on the first render
32 | // https://www.w3schools.com/react/react_useeffect.asp
33 | useEffect(() => {
34 |
35 | // if the properties width and height are defined return nothing
36 | if (dimensions.width && dimensions.height) return
37 |
38 | // setting the ref.current to element
39 | const element = ref.current
40 |
41 | // creating a new instance of ResizeObserver which reports changes to the dimensions of an Element's
42 | // content or border box, or the bounding box of an SVGElement
43 | // Syntax: new ResizeObserver(callback) --> where the callback
44 | // accepts an array of ResizeObserverEntry objects that can be used to access the new dimensions
45 | // of the element after each change
46 | // https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver
47 | const resizeObserver = new ResizeObserver(entries => {
48 |
49 | // edge cases to ensure that entries is an array and has a length
50 | if (!Array.isArray(entries)) return
51 | if (!entries.length) return
52 |
53 | // initializing a constant entry set to the zero index of entries
54 | const entry = entries[0]
55 |
56 | // if the width and height state are not updated,
57 | // using the useState React Hook, to set the width and height to the correct values
58 | if (width != entry.contentRect.width) changeWidth(entry.contentRect.width)
59 | if (height != entry.contentRect.height) changeHeight(entry.contentRect.height)
60 |
61 | // using the observe() method in ResizeObserver to observe the specified element
62 | resizeObserver.observe(element)
63 |
64 | // return an anonymous function that uses the unobserve() method in ResizeObserver to
65 | // end the observing of an element
66 | return () => resizeObserver.unobserve(element)
67 | }, [])
68 | })
69 | // create a new object with new dimensions
70 | // const newSettings = combineChartDimensions({
71 | // ...dimensions,
72 | // width: dimensions.width || width,
73 | // height: dimensions.height || height,
74 | // })
75 |
76 | // initial conditions, useEffect will invoke when ref or newSettings changes
77 | // return [ref, newSettings]
78 | return [ref, combineChartDimensions]
79 | }
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/useUniqueId.js:
--------------------------------------------------------------------------------
1 | let lastId = 0
2 | export const useUniqueId = (prefix="") => {
3 | lastId++
4 | return [prefix, lastId].join("-")
5 | }
--------------------------------------------------------------------------------
/src/Archives/utilities/Archive/utils.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import { useEffect, useState, useRef } from "react"
3 | import ResizeObserver from "resize-observer-polyfill"
4 |
5 | export const accessorPropsType = (
6 | PropTypes.oneOfType([
7 | PropTypes.func,
8 | PropTypes.number,
9 | ])
10 | )
11 |
12 | export const useAccessor = (accessor, d, i) => (
13 | typeof accessor == "function" ? accessor(d, i) : accessor
14 | )
15 |
16 | export const dimensionsPropsType = (
17 | PropTypes.shape({
18 | height: PropTypes.number,
19 | width: PropTypes.number,
20 | marginTop: PropTypes.number,
21 | marginRight: PropTypes.number,
22 | marginBottom: PropTypes.number,
23 | marginLeft: PropTypes.number,
24 | })
25 | )
26 |
27 | export const combineChartDimensions = dimensions => {
28 | let parsedDimensions = {
29 | marginTop: 40,
30 | marginRight: 30,
31 | marginBottom: 40,
32 | marginLeft: 75,
33 | ...dimensions,
34 | }
35 |
36 | return {
37 | ...parsedDimensions,
38 | boundedHeight: Math.max(parsedDimensions.height - parsedDimensions.marginTop - parsedDimensions.marginBottom, 0),
39 | boundedWidth: Math.max(parsedDimensions.width - parsedDimensions.marginLeft - parsedDimensions.marginRight, 0),
40 | }
41 | }
42 |
43 | export const useChartDimensions = passedSettings => {
44 | const ref = useRef()
45 | const dimensions = combineChartDimensions(passedSettings)
46 |
47 | if (dimensions.width && dimensions.height) return [ref, dimensions]
48 |
49 | const [width, changeWidth] = useState(0)
50 | const [height, changeHeight] = useState(0)
51 |
52 | useEffect(() => {
53 | const element = ref.current
54 | const resizeObserver = new ResizeObserver(entries => {
55 | if (!Array.isArray(entries)) return
56 | if (!entries.length) return
57 |
58 | const entry = entries[0]
59 |
60 | if (width != entry.contentRect.width) changeWidth(entry.contentRect.width)
61 | if (height != entry.contentRect.height) changeHeight(entry.contentRect.height)
62 | })
63 |
64 | resizeObserver.observe(element)
65 |
66 | return () => resizeObserver.unobserve(element)
67 | }, [])
68 |
69 | const newSettings = combineChartDimensions({
70 | ...dimensions,
71 | width: dimensions.width || width,
72 | height: dimensions.height || height,
73 | })
74 |
75 | return [ref, newSettings]
76 | }
77 |
78 | let lastId = 0
79 | export const useUniqueId = (prefix="") => {
80 | lastId++
81 | return [prefix, lastId].join("-")
82 | }
--------------------------------------------------------------------------------
/src/Archives/utilities/exportProject.js:
--------------------------------------------------------------------------------
1 | import createApplicationUtil from './createApplication.util';
2 | import createFiles from './createFiles.util';
3 |
4 | // When a user clicks the "Export project" function from the app, this function is invoked
5 | const exportProject = (
6 | path,
7 | appName,
8 | genOption,
9 | tests,
10 | projectType,
11 | components,
12 | rootComponents
13 | ) => {
14 | // Create fully functional classic react application
15 | if (genOption === 1 && projectType === 'Classic React') {
16 | createApplicationUtil({
17 | path,
18 | appName,
19 | components,
20 | testchecked: tests
21 | }).catch((err) => console.log(err));
22 | } // export all component files, but don't create all application files
23 | else if (genOption === 0) {
24 | createFiles(components, path, appName, false);
25 | }
26 | };
27 |
28 | export default exportProject;
29 |
--------------------------------------------------------------------------------
/src/Dropdown/Dropdown.jsx:
--------------------------------------------------------------------------------
1 | /* This example requires Tailwind CSS v2.0+ */
2 | import React, { Fragment } from 'react';
3 | import '../components/ChartComponents/chartstyles.css';
4 |
5 | function classNames(...classes) {
6 | return classes.filter(Boolean).join(' ');
7 | }
8 |
9 | export default function Dropdown(props) {
10 | let menuArray = null;
11 | if (props.data) {
12 | menuArray = Object.keys(props.data[0]).map((ele) => {
13 | return (
14 |
17 | );
18 | });
19 | }
20 |
21 | return (
22 |
23 |
32 |
33 |
40 |
41 |
42 | );
43 | }
44 |
45 | {
46 | /*
47 |
50 |
51 |
61 |
62 |
69 |
70 |
71 | */
72 | }
73 |
--------------------------------------------------------------------------------
/src/Dropdown/DropdownFunctionality.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | function Dropdown({ title, items, multiSelect = false }) {
4 | const [open, setOpen] = useState(false);
5 | const [selection, setSelection] = ([]);
6 |
7 | function handleOnclick(item) {}
8 | return (
9 |
10 | toggle(!open)}
15 | onClick={() => toggle(!open)}>
16 |
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/Dropdown/Menu.Item.jsx:
--------------------------------------------------------------------------------
1 | import { Menu } from '@headlessui/react'
2 |
3 | function MyDropdown() {
4 | return (
5 |
24 | )
25 | }
--------------------------------------------------------------------------------
/src/Dropdown/MenuButton.jsx:
--------------------------------------------------------------------------------
1 | import { Menu } from '@headlessui/react'
2 |
3 | function MyDropdown() {
4 | return (
5 |
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/dictionary.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-alert, no-console */
2 |
3 | export const dictionary = {
4 | // Dictionary is an object of objects that we use to initialize all of our charts info to generate our routes and pass down necessary props for selected containers
5 | // no reducers here since this will remain a static piece of state, unless we directly modify/add to it as we contribute to the chart codebase
6 | barchart: {
7 | type: 'barchart',
8 | name: 'BarChart',
9 | children: ['Chart, Axis, Rectangle'],
10 | properties: [
11 | 'data',
12 | 'dataString',
13 | 'xKey',
14 | 'yKey',
15 | 'xAxisLabel',
16 | 'yAxisLabel',
17 | 'height',
18 | 'width'
19 | ]
20 | },
21 | histogram: {
22 | type: 'histogram',
23 | name: 'Histogram',
24 | children: ['Chart, Axis, Bars'],
25 | properties: [
26 | 'data',
27 | 'dataString',
28 | 'xKey',
29 | 'xAxisLabel',
30 | 'yAxisLabel',
31 | 'height',
32 | 'width',
33 | 'thresholds',
34 | 'barPadding'
35 | ]
36 | },
37 |
38 | scatterplot: {
39 | type: 'scatterplot',
40 | name: 'ScatterPlot',
41 | children: ['Chart, Axis, Circles'],
42 | properties: [
43 | 'data',
44 | 'dataString',
45 | 'xKey',
46 | 'yKey',
47 | 'xAxisLabel',
48 | 'yAxisLabel',
49 | 'height',
50 | 'width',
51 | 'radius'
52 | ]
53 | },
54 |
55 | piechart: {
56 | type: 'piechart',
57 | name: 'PieChart',
58 | children: ['Pie'],
59 | properties: ['data', 'dataString', 'innerRadius', 'outerRadius', 'label', 'pieValue']
60 | },
61 |
62 | linechart: {
63 | type: 'linechart',
64 | name: 'LineChart',
65 | children: ['Chart, Axis, Line'],
66 | properties: [
67 | 'data',
68 | 'dataString',
69 | 'xKey',
70 | 'yKey',
71 | 'xAxisLabel',
72 | 'yAxisLabel',
73 | 'height',
74 | 'width'
75 | ]
76 | }
77 | };
78 |
79 | export default dictionary;
80 |
--------------------------------------------------------------------------------
/src/app/preloadedState.js:
--------------------------------------------------------------------------------
1 | export const preloadedState = {
2 | type: '', //barchart, scatterplot, etc.
3 | name: '',
4 | children: [], // what child elements/comps are needed to build chart (idk if we need this), string[]
5 | properties: [],
6 | dictionary: {
7 | // Dictionary is an object of objects that we use to initialize all of our charts info to generate our routes and pass down necessary props for selected containers
8 | // no reducers here since this will remain a static piece of state, unless we directly modify/add to it as we contribute to the chart codebase
9 | barchart: {
10 | type: 'barchart',
11 | name: 'BarChart',
12 | children: ['Chart, Axis, Rectangle'],
13 | properties: [
14 | 'data',
15 | 'xKey',
16 | 'yKey',
17 | 'xAxisLabel',
18 | 'yAxisLabel',
19 | 'height',
20 | 'width'
21 | ]
22 | },
23 | histogram: {
24 | type: 'histogram',
25 | name: 'Histogram',
26 | children: ['Chart, Axis, Bars'],
27 | properties: [
28 | 'data',
29 | 'xKey',
30 | 'xAxisLabel',
31 | 'yAxisLabel',
32 | 'height',
33 | 'width',
34 | 'thresholds',
35 | 'barPadding'
36 | ]
37 | },
38 |
39 | scatterplot: {
40 | type: 'scatterplot',
41 | name: 'ScatterPlot',
42 | children: ['Chart, Axis, Circles'],
43 | properties: [
44 | 'data',
45 | 'xKey',
46 | 'yKey',
47 | 'xAxisLabel',
48 | 'yAxisLabel',
49 | 'height',
50 | 'width',
51 | 'radius'
52 | ]
53 | },
54 |
55 | piechart: {
56 | type: 'piechart',
57 | name: 'PieChart',
58 | children: ['Pie'],
59 | properties: ['data', 'innerRadius', 'outerRadius', 'label', 'pieValue']
60 | },
61 |
62 | linechart: {
63 | type: 'linechart',
64 | name: 'LineChart',
65 | children: ['Chart, Axis, Line'],
66 | properties: [
67 | 'data',
68 | 'xKey',
69 | 'yKey',
70 | 'xAxisLabel',
71 | 'yAxisLabel',
72 | 'height',
73 | 'width'
74 | ]
75 | }
76 | }
77 | };
78 |
--------------------------------------------------------------------------------
/src/app/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from '@reduxjs/toolkit';
2 | import chartsReducer from '../features/chart/chartsSlice';
3 | import propsReducer from '../features/chart/propsSlice';
4 | // import preloadedState from './preloadedState';
5 |
6 | //'reducer' acts as a root reducer here
7 |
8 | export const store = configureStore({
9 | reducer: {
10 | charts: chartsReducer,
11 | props: propsReducer
12 | }
13 | // preloadedState: preloadedState
14 | });
15 |
--------------------------------------------------------------------------------
/src/babel-plugin-macros.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'fontawesome-svg-core': {
3 | 'license': 'free'
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | return {
3 | plugins: ['macros'],
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/Chart.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .Chart {
6 | overflow: visible;
7 | }
8 |
9 | .Chart text {
10 | fill: #595c5c;
11 | }
12 |
13 | .Line {
14 | transition: all 0.3s ease-out;
15 | }
16 |
17 | .Line--type-line {
18 | fill: none;
19 | stroke: #06b6d4;
20 | stroke-width: 3px;
21 | stroke-linecap: round;
22 | }
23 |
24 | .Line--type-area {
25 | fill: none;
26 | stroke-width: 0;
27 | }
28 |
29 | .Line--type-area {
30 | fill: #00dfeb;
31 | stroke-width: 0;
32 | }
33 |
34 | .Axis__line {
35 | stroke: #595c5c;
36 | }
37 |
38 | .Axis__label {
39 | text-anchor: middle;
40 | font-size: 0.9em;
41 | letter-spacing: 0.01em;
42 | }
43 |
44 | .Axis__tick {
45 | font-size: 0.8em;
46 | transition: all 0.3s ease-out;
47 | }
48 |
49 | .AxisHorizontal .Axis__tick {
50 | text-anchor: middle;
51 | }
52 |
53 | .AxisVertical .Axis__tick {
54 | dominant-baseline: middle;
55 | text-anchor: end;
56 | }
57 |
58 | .Circles__circle {
59 | fill: #00dfeb;
60 | transition: all 0.3s ease-out;
61 | }
62 |
63 | .Bars__rect {
64 | fill: #00dfeb;
65 | transition: all 0.3s ease-out;
66 | }
67 |
68 | .domain,
69 | .tick line {
70 | stroke: #dadada;
71 | }
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Arc.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Arc = ({
4 | data,
5 | fill,
6 | stroke,
7 | strokeWidth,
8 | d,
9 | }) => {
10 | return (
11 |
18 | )
19 | }
20 |
21 | export default Arc
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Axis.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import * as d3 from 'd3'
3 | import { useChartDimensions } from "./Chart.jsx";
4 |
5 | const axisComponentsByDimension = {
6 | x: AxisHorizontal,
7 | y: AxisVertical,
8 | xB: AxisBand,
9 | }
10 | const Axis = ({ dimension, ...props }) => {
11 | const dimensions = useChartDimensions()
12 | const Component = axisComponentsByDimension[dimension]
13 | if (!Component) return null
14 |
15 | return (
16 |
20 | )
21 | }
22 |
23 | export default Axis
24 |
25 |
26 | function AxisHorizontal({ dimensions, label, formatTick, scale, data, ...props }) {
27 | const numberOfTicks = dimensions.boundedWidth < 600
28 | ? dimensions.boundedWidth / 100
29 | : dimensions.boundedWidth / 250
30 |
31 | const ticks = scale.ticks(numberOfTicks)
32 |
33 | return (
34 |
35 |
39 |
40 | {ticks.map((tick, i) => (
41 |
46 | {formatTick(tick)}
47 |
48 | ))}
49 |
50 | {label && (
51 |
55 | {label}
56 |
57 | )}
58 |
59 | )
60 | }
61 |
62 | function AxisVertical({ dimensions, label, formatTick, scale, ...props }) {
63 | const numberOfTicks = dimensions.boundedHeight / 70
64 |
65 | const ticks = scale.ticks(numberOfTicks)
66 |
67 | return (
68 |
69 |
73 |
74 | {ticks.map((tick, i) => (
75 |
80 | {formatTick(tick)}
81 |
82 | ))}
83 |
84 | {label && (
85 |
91 | {label}
92 |
93 | )}
94 |
95 | )
96 | }
97 |
98 | function AxisBand({ dimensions, label, formatTick, scale, data, ...props }) {
99 | const numberOfTicks = dimensions.boundedWidth < 600
100 | ? dimensions.boundedWidth / 100
101 | : dimensions.boundedWidth / 250
102 |
103 | const ticks = scale.domain();
104 |
105 | return (
106 |
107 |
111 |
112 | {ticks.map((tick, i) => (
113 |
118 | {(tick)}
119 |
120 | ))}
121 |
122 | {label && (
123 |
127 | {label}
128 |
129 | )}
130 |
131 | )
132 | }
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Bars.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import * as d3 from 'd3'
3 | import { useAccessor } from "../../../utils/utils.js";
4 |
5 | const Bars = ({ data, keyAccessor, xAccessor, yAccessor, widthAccessor, heightAccessor, ...props }) => (
6 |
7 | {data.map((d, i) => (
8 |
16 | ))}
17 |
18 | )
19 |
20 |
21 | export default Bars
22 |
23 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Chart.css:
--------------------------------------------------------------------------------
1 | .Chart {
2 | overflow: visible;
3 | }
4 |
5 | .Chart text {
6 | fill: #4e4f50;
7 | }
8 |
9 | .Line {
10 | transition: all 0.3s ease-out;
11 | }
12 |
13 | .Line--type-line {
14 | fill: none;
15 | stroke: #00dfeb;
16 | stroke-width: 3px;
17 | stroke-linecap: round;
18 | }
19 |
20 | .Line--type-area {
21 | fill: rgba(71, 70, 73, 0.185);
22 | stroke-width: 0;
23 | }
24 |
25 | .Axis__line {
26 | stroke: #bdc3c7;
27 | }
28 |
29 | .Axis__label {
30 | text-anchor: middle;
31 | font-size: 0.9em;
32 | letter-spacing: 0.01em;
33 | }
34 |
35 | .Axis__tick {
36 | font-size: 0.8em;
37 | transition: all 0.3s ease-out;
38 | }
39 |
40 | .AxisHorizontal .Axis__tick {
41 | text-anchor: middle;
42 | }
43 |
44 | .AxisVertical .Axis__tick {
45 | dominant-baseline: middle;
46 | text-anchor: end;
47 | }
48 |
49 | .Circles__circle {
50 | fill: #00dfeb;
51 | transition: all 0.3s ease-out;
52 | }
53 |
54 | .Bars__rect {
55 | fill: #00dfeb;
56 | transition: all 0.3s ease-out;
57 | }
58 |
59 | .domain,
60 | .tick line {
61 | stroke: #dadada;
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Chart.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext, useContext } from "react"
2 |
3 | import "../Chart.css"
4 |
5 | const ChartContext = createContext()
6 | export const useChartDimensions = () => useContext(ChartContext)
7 |
8 | const Chart = ({ dimensions, children }) => (
9 |
10 |
15 |
16 | )
17 |
18 | export default Chart
19 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/ChartWithDimensions.jsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @pr
3 | * @name: ChartWithDimensions
4 | * @description: produces a modularized template for all Charts
5 | * @param: (object with keys domain: number[] and range: number[]), pixelsPerTick: number
6 | * @returns: A rendering of the Chart
7 | * @author: Antonio Ayala, Sophia Chiao
8 | */
9 |
10 | // import modules and libraries
11 | import { useChartDimensions } from '../../../ChartComponents/utilities/utils';
12 |
13 |
14 | const chartSettings = {
15 | "marginLeft": 75
16 | }
17 |
18 | export const ChartWithDimensions = () => {
19 |
20 | // creating a custom hook useChartDimensions
21 | const [ref, dms] = useChartDimensions(chartSettings)
22 |
23 | //
24 | const xScale = useMemo(() => (
25 | d3.scaleLinear()
26 | .domain([0, 100])
27 | .range([0, dms.boundedWidth])
28 | ), [dms.boundedWidth])
29 |
30 | return (
31 |
35 |
56 |
57 | )
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Circles.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { accessorPropsType } from "../../../utils/utils.js"
3 |
4 | const Circles = ({ data, keyAccessor, xAccessor, yAccessor, radius }) => {
5 |
6 | return (
7 |
8 | {data.map((d, i) => (
9 |
16 | ))}
17 |
18 | )
19 | }
20 |
21 | export default Circles
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/CodeRender.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useCallback, Fragment } from 'react';
2 | import { upperFirst } from 'lodash';
3 | import {
4 | generateChartCode,
5 | CodeBlock,
6 | Code,
7 | CodeText,
8 | formatCode
9 | } from '../../../utils/CodePreview';
10 |
11 | const CodeRender = ({ name, children, data, ...currProps }) => {
12 | delete currProps.dataString; // otherwise the entire dataset will be printed to the screen
13 |
14 | const code = generateChartCode(`${upperFirst(name)}`, currProps, {
15 | dataKey: data !== undefined ? 'data' : undefined,
16 | children: children,
17 | defaults: {},
18 | pkg: name, // pkg: 'barchart',
19 | })
20 |
21 | // References created by useRef itself do not trigger component rerenders, and on initial render, ref will be null
22 | // State must be modified to trigger any rerenders, so we use a callback ref to run some code
23 | // whenever React attaches or detaches a ref to a DOM node (html: )
24 | const useCodeRef = (processNode) => {
25 | const [node, setNode] = useState(null);
26 | const setCodeRef = useCallback(newNode => {
27 | if (newNode) {
28 | // console.log("ref", node); // node = codeRef.current //
29 | setNode(processNode(newNode));
30 | }
31 | }, []);
32 | return [node, setCodeRef]
33 | };
34 | const [codeRef, setCodeRef] = useCodeRef((node) => node)
35 |
36 | // To reflect on every code change, we use useEffect to reassign the new codeRef on rerender
37 | useEffect(() => {
38 | console.log(codeRef);
39 | }, [codeRef]);
40 |
41 | return (
42 |
43 |
44 |
45 |
46 | {code}
47 |
48 |
49 |
50 |
51 |
107 |
108 |
109 | );
110 | };
111 |
112 | export default CodeRender;
113 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Container.jsx:
--------------------------------------------------------------------------------
1 | import React, { lazy, Suspense, useEffect, useMemo, useLayoutEffect, useCallback, Fragment, useState } from 'react';
2 | import * as d3 from 'd3';
3 | import Form from './Form.jsx'
4 | import CodeRender from './CodeRender.jsx'
5 | import { useSelector, useDispatch } from 'react-redux'
6 | import { useLocation } from "react-router";
7 | import { Link } from 'react-router-dom';
8 | import '../chartstyles.css'
9 | // Remember to import your specific chart from the chartsSlice here
10 | import {
11 | barchart,
12 | histogram,
13 | scatterplot,
14 | piechart,
15 | linechart,
16 | } from "../../../features/chart/chartsSlice"
17 | import ErrorBoundary from './ErrorBoundary.jsx';
18 |
19 | const BarChart = lazy(() => import('../../Charts/BarChart/JSX/BarChart.jsx'));
20 | const Histogram = lazy(() => import('../../Charts/Histogram/JSX/Histogram.jsx'))
21 | const ScatterPlot = lazy(() => import('../../Charts/ScatterPlot/JSX/ScatterPlot.jsx'))
22 | const PieChart = lazy(() => import('../../Charts/PieChart/JSX/PieChart.jsx'))
23 | const LineChart = lazy(() => import('../../Charts/LineChart/JSX/LineChart.jsx'))
24 |
25 | // Upon navigation to specified route, we first identify our chart using useLocation, then, we will dispatch action using specified chart
26 | // Can we do this with useParams as well? --> grab name direclty without slicing and reassigning
27 |
28 | /**
29 | *
30 | * @param { type, name, children, properties } Container
31 | * @returns Form, MyChart, CodeRender
32 | *
33 | * Container is the general component which contains all of our other modularized components.
34 | * On route selection, we use property accesors for our dispatch (chart selection), to programmatically set the chart-unique type, props, children, etc. in state
35 | * This selection allows us to:
36 | * - dynamically lazy() load in the required chart file when needed
37 | * - filter out chart-specific props to pass/populate the modular child components
38 | *
39 | */
40 |
41 |
42 | const Container = ({ type, name, children, properties }) => {
43 | // use property accessors for our dispatch
44 | const charts = {
45 | "barchart": barchart,
46 | "histogram": histogram,
47 | "scatterplot": scatterplot,
48 | "piechart": piechart,
49 | "linechart": linechart,
50 | }
51 |
52 | //Dispatching chart sets the chart type, props, children, and default props in state
53 | const dispatch = useDispatch();
54 | useEffect(() => {
55 | console.log("dispatching chart action type: ")
56 | dispatch(charts[type]());
57 | }, [dispatch]);
58 |
59 | //Filtering chart-specific props
60 | const props = useSelector((state) => state.props); // object of all current props
61 | const currProps = properties.reduce((acc, curr) => {
62 | acc[curr] = props[curr];
63 | return acc;
64 | }, {});
65 |
66 | // Need to implement a unique key that increases every time state changes.
67 | // This is then passed to error boundary so that it rerenders when state chagnes.
68 | // This is necessary in order to recover from errors. Without Changing the error boundaries key won't reset.
69 | const [errorKey, setErrorKey] = useState(0);
70 | useEffect(() => {
71 | setErrorKey(Date.now());
72 | }, [currProps.data])
73 |
74 | // Memoizing the import
75 | // We want to rerender of chart as state props changes, but import the actual component only once (unless type change during dispatch)
76 | const MyChart = useMemo(() => lazy(() => import(`../../Charts/${name}/JSX/${name}.jsx`)), [dispatch]);
77 |
78 | return (
79 |
80 | {/* }> */}
81 | Home
82 |
83 |
84 |
85 |
86 |
91 |
92 |
93 |
94 |
95 | }>
96 |
99 |
100 |
101 |
102 |
103 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | {/* */}
116 |
117 | );
118 | };
119 |
120 | export default Container;
121 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/ErrorBoundary.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class ErrorBoundary extends React.Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = { hasError: false };
7 | }
8 |
9 | static getDerivedStateFromError(error) {
10 | // Update state so the next render will show the fallback UI.
11 | return { hasError: true };
12 | }
13 |
14 | componentDidCatch(error, errorInfo) {
15 | console.log(error);
16 | }
17 |
18 | render() {
19 | if (this.state.hasError) {
20 | // You can render any custom fallback UI
21 | return
22 | }
23 |
24 | return this.props.children;
25 | }
26 | }
27 |
28 | export default ErrorBoundary;
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/ExportCodeButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { formatCode } from '../../../utils/CodePreview';
3 | import { downloadCode } from "../../../utils/ExportData"
4 |
5 | export const ExportCodeButton = ({ name, codeRef }) => {
6 | return (
7 |
25 | );
26 | };
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/ExportDataButton.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { upperFirst } from 'lodash';
3 |
4 | //Exports data as JS file
5 | export const ExportDataButton = ({ name, data }) => {
6 |
7 | let fileName = upperFirst(name);
8 | let text = `export const data = [${data
9 | .reduce((str, obj) => {
10 | return (str +=
11 | '{' + Object.keys(obj).map((key) => `'${key}': ${obj[key]}`) + `}, \n`);
12 | }, '')
13 | .trim()
14 | .slice(0, -1)}]`;
15 |
16 | return (
17 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Form.jsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react';
2 | import '../../ChartComponents/chartstyles.css';
3 | import { startCase } from 'lodash';
4 | import { useDispatch } from 'react-redux';
5 | import { changeProps } from '../../../features/chart/propsSlice';
6 | import Dropdown from '../../../Dropdown/Dropdown';
7 |
8 | /**
9 | * We do not dynamically use eval() and instead use property accessors to send the correct payload instead
10 | * Instead, we let our reducer handle what gets updated
11 | * We pass input name/value to our single props reducer, which updates the props in state.props
12 | *
13 | * Depending on the type of prop, we render specific form elements (text input, dropdown, slider)
14 | */
15 |
16 | const Form = ({ properties, data, currProps }) => {
17 | const dispatch = useDispatch();
18 |
19 | const handleChange = useCallback(
20 | (e) => {
21 | e.preventDefault();
22 | console.log('handling event...', e);
23 | dispatch(changeProps({ name: e.target.name, value: e.target.value }));
24 | },
25 | [dispatch]
26 | );
27 |
28 | const [invalidJSON, setInvalidJSON] = useState(false);
29 |
30 | const inputs = properties.map((p, i) => {
31 | //If Property is xKey or yKey then do a form
32 | if (p === 'xKey' || p === 'yKey') {
33 | console.log('has Data Changed to undefined?', data)
34 | return (
35 |
36 |
37 |
41 |
42 |
48 |
49 |
50 | );
51 | } else if (typeof currProps[p] === 'number') {
52 | return (
53 |
54 |
55 |
60 |
71 |
72 |
73 | );
74 | } else if (p === 'data') {
75 | //Data needs to be a current property, but doesn't require it's own form field.
76 | return null
77 | }
78 |
79 | else if (p === 'dataString') {
80 | //Otherwise do a TextArea
81 | return (
82 |
83 |
84 |
88 |
106 | {invalidJSON ? Invalid JSON
: ''}
107 |
108 |
109 | );
110 | } else {
111 | //Otherwise do a TextArea
112 | return (
113 |
114 |
115 |
119 |
127 |
128 |
129 | );
130 | }
131 | });
132 |
133 | return (
134 |
137 | );
138 | };
139 |
140 | export default Form;
141 |
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Gradient.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 |
3 | const Gradient = ({ id, colors, ...props }) => (
4 |
5 | {colors.map((color, i) => (
6 |
11 | ))}
12 |
13 | )
14 |
15 | export default Gradient
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Line.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import * as d3 from "d3"
3 |
4 | const Line = ({ data, xAccessor, yAccessor, y0Accessor, width, height, /* type, interpolation, ...props */ }) => {
5 | const lineGenerator = d3.line()
6 | .x(xAccessor)
7 | // .y0(d=> Math.min(0, yscale(yAccessor)))
8 | .y(yAccessor)
9 | .curve(d3.curveMonotoneX)
10 |
11 | return (
12 |
17 | )
18 | }
19 |
20 |
21 | export default Line
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Pie.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react';
2 | import styled from 'styled-components';
3 | import * as d3 from 'd3';
4 | import { pie } from 'd3';
5 | import { sum } from 'lodash';
6 | import Arc from './Arc';
7 |
8 |
9 | const Pie = ({
10 | data,
11 | width,
12 | height,
13 | innerRadius,
14 | outerRadius,
15 | label,
16 | pieValue
17 |
18 | }) => {
19 | // d3.select('#pie-container')
20 | // .select('svg')
21 | // .remove();
22 |
23 | const colorScale = d3
24 | .scaleSequential()
25 | .interpolator(d3.interpolateHcl("#60c96e", "#4d4193"))
26 | .domain([0, data.length]);
27 |
28 | // // Create new svg --> Chart
29 | // const svg = d3
30 | // .select('#pie-container')
31 | // .append('svg')
32 | // .attr('width', width)
33 | // .attr('height', height)
34 | // .append('g')
35 | // .attr('transform', `translate(${width / 2}, ${height / 2})`);
36 |
37 | const arcGenerator = d3
38 | .arc()
39 | .innerRadius(innerRadius)
40 | .outerRadius(outerRadius);
41 |
42 | const pieGenerator = d3
43 | .pie()
44 | .padAngle(0)
45 | .value((d) => d[pieValue]);
46 |
47 | const pie = pieGenerator(data);
48 |
49 | // // const propsPie = useMemo(() => pie.map((d) => ({ [label]: d.data[label], [pieValue]: d.data[pieValue] })), [data]);
50 | const propsPie = pie.map((d) => ({ [label]: d.data[label], [pieValue]: d.data[pieValue] }));
51 |
52 | // for text label
53 | const translatePie = (d) => {
54 | const [x, y] = arcGenerator.centroid(d);
55 | return `translate(${x}, ${y})`;
56 | };
57 |
58 | const PieLabel = styled.text`
59 | font-size: 10;
60 | text-anchor: middle;
61 | alignment-baseline: middle;
62 | fill: black;
63 | `;
64 |
65 | // const arc = svg
66 | // .selectAll()
67 | // .data(pieGenerator(data))
68 | // .enter();
69 |
70 | // // Append arcs --> Arc.jsx
71 | // arc
72 | // .append('path')
73 | // .attr('d', arcGenerator)
74 | // .style('fill', (_, i) => colorScale(i))
75 | // .style('stroke', '#ffffff')
76 | // .style('stroke-width', 0);
77 |
78 | // // Append text labels --> pieLabel
79 | // arc
80 | // .append('text')
81 | // .attr('text-anchor', 'middle')
82 | // .attr('alignment-baseline', 'middle')
83 | // .text((d) => {
84 | // if (!d.data[label]) return ``
85 | // if (d.data[label]) return `${d.data[label]} ${d.data[pieValue]}`
86 | // })
87 | // .style('fill', 'black')
88 | // .style('font-size', 10)
89 | // .attr('transform', (d) => {
90 | // const [x, y] = arcGenerator.centroid(d);
91 | // return `translate(${x}, ${y})`;
92 | // });
93 |
94 | return (
95 | //
96 |
97 | {pie.map((d, i) => (
98 |
99 |
108 | {d.data[label] && (
109 |
112 | {d.data[label]} {d.data[pieValue]}
113 |
114 | )
115 | }
116 |
117 | ))}
118 |
119 | )
120 | }
121 | export default Pie
--------------------------------------------------------------------------------
/src/components/ChartComponents/JSX/Rectangle.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const Bar = styled.rect`
5 | fill-opacity: 0.7;
6 | color: palevioletred;
7 | `;
8 |
9 | const Rectangle = ({
10 | data,
11 | x,
12 | y,
13 | width,
14 | height,
15 | fill
16 | }) => {
17 |
18 | return (
19 |
26 | );
27 | };
28 |
29 | export default Rectangle
--------------------------------------------------------------------------------
/src/components/ChartComponents/chartstyles.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /* body {
6 | padding: 1.6em 2em 4em;
7 | letter-spacing: -0.011em;
8 | font-family: 'Inter var', sans-serif;
9 | font-size: 16px;
10 | color: #34495e;
11 | background: #f1f3f5;
12 | } */
13 |
14 | #root,
15 | .App {
16 | width: 100%;
17 | font-family: 'Inter var', sans-serif;
18 | }
19 |
20 | h1 {
21 | font-weight: 900;
22 | margin: 0.4em 0 0.6em;
23 | }
24 |
25 | /* placeholders */
26 | .placeholder {
27 | background: #ecf0f1;
28 | }
29 |
30 | .Timeline {
31 | height: 300px;
32 | min-width: 500px;
33 | width: calc(100% + 1em);
34 | margin-bottom: 2em;
35 | }
36 |
37 | .Histogram {
38 | height: 500px;
39 | flex: 1;
40 | min-width: 500px;
41 | overflow: hidden;
42 | }
43 |
44 | .BarChart {
45 | height: 500px;
46 | flex: 1;
47 | min-width: 500px;
48 | overflow: hidden;
49 | }
50 |
51 | .ScatterPlot {
52 | height: 500px;
53 | width: 500px;
54 | margin-right: 2em;
55 | }
56 |
57 | /* .LineGraph {
58 | height: 500px;
59 | width: 500px;
60 | margin-right: 2em;
61 | } */
62 |
63 | .App__charts {
64 | display: flex;
65 | align-items: center;
66 | flex-wrap: wrap;
67 | margin: -0.5em;
68 | }
69 |
70 | .Timeline,
71 | .ScatterPlot,
72 | .Histogram,
73 | /* .LineGraph, */
74 | .BarChart {
75 | /* background: rgb(255,255,255); */
76 | }
77 |
--------------------------------------------------------------------------------
/src/components/Charts/BarChart/JSX/BarChart.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as d3 from 'd3';
3 | import { useChartDimensions } from '../../../../utils/utils.js';
4 | import Axis from '../../../ChartComponents/JSX/Axis.jsx';
5 | import Rectangle from '../../../ChartComponents/JSX/Rectangle.jsx';
6 | import Chart from '../../../ChartComponents/JSX/Chart.jsx';
7 | import '../../../ChartComponents/chartstyles.css';
8 | import '../../../ChartComponents/Chart.css';
9 | import { useUniqueId } from '../../../../utils/utils.js';
10 | import Gradient from "../../../ChartComponents/JSX/Gradient"
11 |
12 | /**
13 | * Because of the way the user will import data in their customized code
14 | * ex.
15 | * the base component template has to be able to take props supplied from MyBarChart.jsx
16 | */
17 |
18 | export default function BarChart ({ data, xKey, yKey, xAxisLabel, yAxisLabel, height, width }) {
19 | // export default function BarChart ({ currProps: { data, xKey, yKey, xAxisLabel, yAxisLabel, height, width }}) {
20 |
21 | // if(!data) data = [];
22 | /*
23 | Using useMemo for **referential equality** of depedencies: important for React hooks
24 | 2 common use cases of useMemo:
25 | 1. When you want to make a slow function wrap inside useMemo so that doesn't re-compute every single time you render your component and it only computed when you acually need the value from that function since the inputs actually change
26 | 2. Whenever you want to make sure the reference of an object or an array is exactly the same as it was the last time you rendered if none of the internal workings changed, you're gonna want to useMemo here to make sure that you only update the reference of that object whenever the actual contents of the object change instead of updating every single time you render
27 | */
28 |
29 | // Uncomment below to work with current histogram data (working)
30 | // (oh also useMemo doesn't work so i will get to that later :( )
31 | // const xAccessor = useMemo(() => (data) => data[xKey], []);
32 | // const yAccessor = useMemo(() => (data) => data[yKey], []);
33 | const xAccessor = (data) => data[xKey];
34 | const yAccessor = (data) => data[yKey];
35 |
36 | const gradientId = useUniqueId("Histogram-gradient")
37 | const gradientColors = ["#9980FA", "rgb(226, 222, 243)"]
38 |
39 | // setState input dimensions from Form -> Container passes down updated dims -> Chart passes dims as new args in useChartDimensions
40 | const [ref, dimensions] = useChartDimensions({
41 | marginBottom: 77,
42 | height: height,
43 | width: width
44 | });
45 |
46 | /** For bar charts, it's recommended to use scaleBand() + continuous.rangeRound() fn (since d3 v4) to set the range of the scale to the specified array of values, but in our case, I think either works */
47 | // Should we add a form input to control the padding ?
48 | // Outer padding: space before the first bar and after the last one.
49 | // Inner padding: space between bars
50 | const xScale = d3
51 | .scaleBand()
52 | .domain(data.map(xAccessor))
53 | .paddingInner(0.1)
54 | .paddingOuter(0.1)
55 | .rangeRound([0, dimensions.boundedWidth])
56 | // .padding(0.1)
57 | // .range([0, dimensions.boundedWidth]);
58 |
59 | // Add a form input for user input to change y-scale min, instead of default to 0?
60 | let yMax = d3.max(data, yAccessor);
61 | let yMin = Math.min(0, d3.min(data, yAccessor));
62 | const yScale = d3
63 | .scaleLinear()
64 | // .domain(d3.extent(data, yAccessor))
65 | .domain([yMin, yMax])
66 | .range([dimensions.boundedHeight, 0])
67 | .nice();
68 |
69 | const Bars = data.map((d, i) => {
70 | return (
71 |
80 | );
81 | });
82 |
83 |
84 |
85 | return (
86 |
87 |
88 |
89 |
95 |
96 |
102 |
108 | {Bars}
109 |
110 |
111 |
112 |
113 | );
114 | };
115 |
116 | // export default BarChart;
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/components/Charts/Histogram/JSX/Histogram.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, Fragment } from 'react';
2 | import * as d3 from 'd3';
3 | import { useChartDimensions } from '../../../../utils/utils.js';
4 | import Axis from '../../../ChartComponents/JSX/Axis.jsx';
5 | import Bars from '../../../ChartComponents/JSX/Bars.jsx';
6 | import Chart from '../../../ChartComponents/JSX/Chart.jsx';
7 | import '../../../ChartComponents/chartstyles.css';
8 | import '../../../ChartComponents/Chart.css';
9 | import { useUniqueId } from '../../../../utils/utils.js';
10 | import Gradient from "../../../ChartComponents/JSX/Gradient"
11 |
12 | const Histogram = ({ data, xKey, xAxisLabel, yAxisLabel, height, width, thresholds, barPadding }) => {
13 | // Since histograms compare occurences across a population/data, the y-Accessor must be the length of your dataset
14 | // const yAccessor = d => d.length
15 | const xAccessor = useMemo(() => (data) => data[xKey]);
16 | const yAccessor = useMemo(() => (data) => data.length);
17 |
18 | const gradientId = useUniqueId("Histogram-gradient")
19 | const gradientColors = ["#9980FA", "rgb(226, 222, 243)"]
20 |
21 | // setState input dimensions from Form -> Container passes down updated dims -> Chart passes dims as new args in useChartDimensions
22 | const [ref, dimensions] = useChartDimensions({
23 | marginBottom: 77,
24 | height: height,
25 | width: width
26 | });
27 |
28 | // Thresholds = # scaled bins (user inputs # of bins as thresholds, we scale bins according to their data for them )
29 | const numberOfThresholds = thresholds;
30 |
31 | const xScale = d3
32 | .scaleLinear()
33 | .domain(d3.extent(data, xAccessor))
34 | .range([0, dimensions.boundedWidth])
35 | .nice(numberOfThresholds);
36 |
37 | const binsGenerator = d3
38 | .histogram()
39 | .domain(xScale.domain())
40 | .value(xAccessor)
41 | .thresholds(xScale.ticks(numberOfThresholds));
42 |
43 | const bins = binsGenerator(data);
44 |
45 | const yScale = d3
46 | .scaleLinear()
47 | .domain([0, d3.max(bins, yAccessor)])
48 | .range([dimensions.boundedHeight, 0])
49 | .nice();
50 |
51 | const xAccessorScaled = (d) => xScale(d.x0) + barPadding;
52 | const yAccessorScaled = (d) => yScale(yAccessor(d));
53 | const widthAccessorScaled = (d) => xScale(d.x1) - xScale(d.x0) - barPadding;
54 | const heightAccessorScaled = (d) =>
55 | dimensions.boundedHeight - yScale(yAccessor(d));
56 | const keyAccessor = (d, i) => i;
57 |
58 | return (
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
82 |
91 |
92 |
93 |
94 | );
95 | };
96 |
97 | export default Histogram;
98 |
--------------------------------------------------------------------------------
/src/components/Charts/LineChart/JSX/LineChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, Fragment } from 'react';
2 | import * as d3 from 'd3';
3 | import { useChartDimensions } from '../../../../utils/utils.js';
4 | import Chart from '../../../ChartComponents/JSX/Chart.jsx';
5 | import Line from '../../../ChartComponents/JSX/Line.jsx';
6 | import Axis from '../../../ChartComponents/JSX/Axis.jsx';
7 | import '../../../ChartComponents/chartstyles.css';
8 | import '../../../ChartComponents/Chart.css';
9 |
10 | export default function LineChart({ data, xKey, yKey, xAxisLabel, yAxisLabel, height, width }) {
11 | const xAccessor = (data) => data[xKey];
12 | const yAccessor = (data) => data[yKey];
13 |
14 | const [ref, dimensions] = useChartDimensions({
15 | marginBottom: 77,
16 | height: height,
17 | width: width,
18 | })
19 |
20 | const xScale = d3
21 | .scaleLinear() // returns position within domain and range
22 | .domain(d3.extent(data, xAccessor)) // sets domain with an array [0.2693916329035372, 0.7248443066197088]
23 | .range([0, dimensions.boundedWidth])
24 | .nice();
25 |
26 | const yScale = d3.scaleLinear()
27 | .domain(d3.extent(data, yAccessor))
28 | .range([dimensions.boundedHeight, 0])
29 | .nice()
30 |
31 | const xAccessorScaled = d => xScale(xAccessor(d))
32 | const yAccessorScaled = d => yScale(yAccessor(d))
33 | const y0AccessorScaled = yScale(yScale.domain()[0])
34 |
35 | return (
36 |
37 |
38 |
39 |
45 |
51 |
59 |
60 |
61 |
62 | )
63 | }
--------------------------------------------------------------------------------
/src/components/Charts/PieChart/JSX/PieChart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo, Fragment } from 'react';
2 | import * as d3 from 'd3';
3 | import { useChartDimensions } from '../../../../utils/utils.js';
4 | import Pie from '../../../ChartComponents/JSX/Pie.jsx';
5 | import Chart from '../../../ChartComponents/JSX/Chart.jsx';
6 | import '../../../ChartComponents/chartstyles.css';
7 | import '../../../ChartComponents/Chart.css';
8 |
9 | const PieChart = ({ data, innerRadius, outerRadius, label, pieValue }) => {
10 |
11 | // We can over-write the default values set in useChartDimensions by passing them in as props
12 | // Centering Pie
13 | const [ref, dimensions] = useChartDimensions({
14 | marginTop: 200,
15 | marginBottom: 170,
16 | marginLeft: 270,
17 | marginRight: 170
18 | });
19 |
20 | const width = 2 * outerRadius + dimensions.marginLeft + dimensions.marginRight;
21 | const height = 2 * outerRadius + dimensions.marginTop + dimensions.marginBottom;
22 |
23 | return (
24 |
25 |
26 |
27 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | export default PieChart;
--------------------------------------------------------------------------------
/src/components/Charts/ScatterPlot/JSX/ScatterPlot.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as d3 from 'd3';
3 | import { useChartDimensions } from '../../../../utils/utils.js';
4 | import Axis from "../../../ChartComponents/JSX/Axis.jsx"
5 | import Circles from "../../../ChartComponents/JSX/Circles.jsx"
6 | import Chart from "../../../ChartComponents/JSX/Chart.jsx"
7 | import "../../../ChartComponents/chartstyles.css"
8 | import '../../../ChartComponents/Chart.css';
9 |
10 | const ScatterPlot = ({ data, xKey, yKey, xAxisLabel, yAxisLabel, height, width, radius }) => {
11 | const xAccessor = (data) => data[xKey];
12 | const yAccessor = (data) => data[yKey];
13 |
14 | const [ref, dimensions] = useChartDimensions({
15 | marginBottom: 77,
16 | height: height,
17 | width: width,
18 | })
19 |
20 | //Scatterplot x-range data must be numeric
21 | const xScale = d3
22 | .scaleLinear() // returns position within domain and range
23 | .domain(d3.extent(data, xAccessor)) // sets domain with an array [0.2693916329035372, 0.7248443066197088]
24 | .range([0, dimensions.boundedWidth])
25 | .nice();
26 |
27 | const yScale = d3.scaleLinear()
28 | .domain(d3.extent(data, yAccessor))
29 | .range([dimensions.boundedHeight, 0])
30 | .nice()
31 |
32 | const xAccessorScaled = d => xScale(xAccessor(d)) // returns a position from result of getting humidity in object
33 | const yAccessorScaled = d => yScale(yAccessor(d))
34 | const keyAccessor = (d, i) => i
35 |
36 | return (
37 |
38 |
39 |
45 |
51 |
58 |
59 |
60 | )
61 | }
62 |
63 | export default ScatterPlot
--------------------------------------------------------------------------------
/src/components/HelloWorldcopy.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const HelloWorld = () => {
4 | return (
5 |
6 | hello
7 |
8 | )
9 | }
--------------------------------------------------------------------------------
/src/components/NavBar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom'
3 |
4 | type props = string;
5 |
6 | export default function NavBar () {
7 | return(
8 |
9 | Bar Chart
10 | Line Chart
11 | Scatter Plot
12 | Histogram
13 | )
14 | }
--------------------------------------------------------------------------------
/src/components/pages/ChartCards.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | function ChartCards() {
5 | return (
6 |
7 |
8 |
9 |
10 |
23 |
24 | BarChart
25 |
26 |
27 |
28 | A bar chart or bar graph plots numeric values for categorical data
29 | with rectangular bars with heights or lengths proportional to the
30 | values that they represent.
31 |
32 |
33 |
34 |
35 |
36 |
37 | Line Chart
38 |
39 |
40 |
41 | This chart is used to show information that changes over time. Line
42 | charts are created by plotting a series of several points and
43 | connecting them with a striaght line.
44 |
45 |
46 |
47 |
48 |
49 |
50 | ScatterPlot
51 |
52 |
53 |
54 | A type of data visualization that shows the relationship between
55 | different variables. This data is shown by placing various data
56 | points between an x- and y-axis.
57 |
58 |
59 |
60 |
61 |
62 |
63 | Histogram
64 |
65 |
66 |
67 | Are used to summarize discrete or continuous data that are measured
68 | on an interval scale.
69 |
70 |
71 |
72 |
73 |
74 |
75 | Pie Chart
76 |
77 |
78 |
79 | Are used to show percentages of a whole, and represents percentages
80 | at a set point in time.
81 |
82 |
83 |
84 | {/* */}
85 |
86 |
87 | Timeline Chart
88 |
89 |
90 | Timeline charts illustrate events, in chronological order. For example week, month, year, quarter.
91 |
92 |
93 |
94 |
95 |
96 | Bubble Graph
97 |
98 |
99 | An extension of a scatterplot, a bubble chart is commonly used to visualize relationships between three or more numeric variables.
100 |
101 |
102 |
103 |
104 |
105 | Graph Chart
106 |
107 |
108 | Graph charts show relationships between data and are intended to display the data in a way that is easy to understand and remember.
109 |
110 |
111 |
112 |
113 | );
114 | }
115 |
116 | export default ChartCards;
117 |
118 | {
119 | /*
120 |
121 |
122 | Chart Mixed
123 |
124 |
125 | This is where we will display chart info for each graph
126 |
127 | */
128 | }
129 |
--------------------------------------------------------------------------------
/src/components/pages/Homepage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import ChartCards from './ChartCards'
4 | import TheCarousel from './TheCarousel';
5 |
6 | function Homepage() {
7 | return (
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
15 | export default Homepage;
--------------------------------------------------------------------------------
/src/components/pages/TheCarousel.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ReactDOM } from 'react';
3 | import { render } from 'react-dom';
4 | import { Carousel } from 'react-responsive-carousel';
5 | import styles from 'react-responsive-carousel/lib/styles/carousel.min.css';
6 | import myStyles from '../../components/../styles.css'
7 | import chart1 from './chart1.svg';
8 | import chart2 from './chart2.svg';
9 | import chart3 from './chart3.svg';
10 | import chart4 from './chart4.svg';
11 | import chart5 from './chart5.svg';
12 |
13 | function CarouselComponent() {
14 | return (
15 |
16 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | );
42 | }
43 |
44 | export default CarouselComponent;
45 |
--------------------------------------------------------------------------------
/src/components/pages/chart1.svg:
--------------------------------------------------------------------------------
1 |
26 |
--------------------------------------------------------------------------------
/src/components/pages/chart3.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/src/components/pages/chart4.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/pages/chart5.svg:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/src/dashboard.png
--------------------------------------------------------------------------------
/src/features/chart/chartsSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 |
3 | const initialState = {
4 | type: '', //barchart, scatterplot, etc.
5 | name: '',
6 | children: [], // what child elements/comps are needed to build chart (idk if we need this), string[]
7 | properties: [] //required properties for each chart, string[],
8 | };
9 |
10 | //make switch case based on action/chart type (action = barchart, no payload), or use switch case within the payload/reducer? (if payload = barchart)
11 |
12 | export const chartsSlice = createSlice({
13 | name: 'charts',
14 | initialState,
15 | // reducers: An object containing Redux "case reducer" functions (functions intended to handle a specific action type, equivalent to a single case statement in a switch).
16 | // keys in the object will be used to generate string action type constants
17 | // equivalent of action type charts/barchart
18 | // under the hood: const barchart = createAction('charts/barchart')
19 | reducers: {
20 | // we are writing "mutating" reducers like we are updating state directly,
21 | // but under-the-hood the reducer receives a proxy state that translates all mutations into equivalent copy operations (possible through Immer)
22 |
23 | barchart: (state, action) => {
24 | console.log('Selecting barchart type, children, and properties');
25 | state.type = 'barchart';
26 | state.name = 'BarChart';
27 | state.children = ['Chart, Axis, Rectangle'];
28 | state.properties = [
29 | 'data',
30 | 'xKey',
31 | 'yKey',
32 | 'xAxisLabel',
33 | 'yAxisLabel',
34 | 'height',
35 | 'width'
36 | ];
37 | },
38 |
39 | histogram: (state, action) => {
40 | console.log('Selecting histogram type, children, and properties');
41 | state.type = 'histogram';
42 | state.name = 'Histogram';
43 | state.children = ['Chart, Axis, Bars'];
44 | state.properties = [
45 | 'data',
46 | 'xKey',
47 | 'xAxisLabel',
48 | 'yAxisLabel',
49 | 'height',
50 | 'width',
51 | 'thresholds',
52 | 'barPadding'
53 | ];
54 | },
55 |
56 | scatterplot: (state, action) => {
57 | console.log('Selecting scatterplot type, children, and properties');
58 | state.type = 'scatterplot';
59 | state.name = 'ScatterPlot';
60 | state.children = ['Chart, Axis, Circles'];
61 | state.properties = [
62 | 'data',
63 | 'xKey',
64 | 'yKey',
65 | 'xAxisLabel',
66 | 'yAxisLabel',
67 | 'height',
68 | 'width',
69 | 'radius'
70 | ];
71 | },
72 |
73 | piechart: (state, action) => {
74 | console.log('Selecting piechart type, children, and properties');
75 | state.type = 'piechart';
76 | state.name = 'PieChart';
77 | state.children = ['Pie'];
78 | state.properties = [
79 | 'data',
80 | 'innerRadius',
81 | 'outerRadius',
82 | 'label',
83 | 'pieValue'
84 | ];
85 | },
86 |
87 | linechart: (state, action) => {
88 | console.log('Selecting linechart type, children, and properties');
89 | state.type = 'linechart';
90 | state.name = 'LineChart';
91 | state.children = ['Chart, Axis, Line'];
92 | state.properties = [
93 | 'data',
94 | 'xKey',
95 | 'yKey',
96 | 'xAxisLabel',
97 | 'yAxisLabel',
98 | 'height',
99 | 'width'
100 | ];
101 | }
102 | }
103 | });
104 |
105 | // Action creators are generated for each case reducer function
106 | export const { barchart, scatterplot, histogram, piechart, linechart } =
107 | chartsSlice.actions;
108 |
109 | export default chartsSlice.reducer;
110 |
--------------------------------------------------------------------------------
/src/features/chart/propsSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from '@reduxjs/toolkit';
2 | import { sampleData } from '../../utils/dummypenguinsdata';
3 | import { sampleFruitData } from '../../utils/dummyfruitsdata';
4 | //this slice/reducer is the functionality of our generic handleChange
5 |
6 | /* Redux Toolkit allows us to write "mutating" logic in reducers. It
7 | doesn't actually mutate the state because it uses the Immer library,
8 | which detects changes to a "draft state" and produces a brand new
9 | immutable state based off those changes
10 |
11 | createSlice(): A function that accepts an initial state,
12 | an object of reducer functions, and a "slice name",
13 | and automatically generates action creators and action types that correspond to the reducers and state.
14 |
15 | This API is the standard approach for writing Redux logic.
16 |
17 | Internally, it uses createAction and createReducer,
18 | so you may also use Immer to write "mutating" immutable updates:
19 |
20 | action type: props/changeProps
21 | */
22 |
23 | const initialState = {
24 | name: '',
25 | dataString: JSON.stringify(sampleData),
26 | data: sampleData,
27 | xKey: '',
28 | yKey: '',
29 | xAxisLabel: 'X-axis: Species',
30 | yAxisLabel: 'Y-axis: Body Mass',
31 | height: 500,
32 | width: 500,
33 | thresholds: 9,
34 | barPadding: 2,
35 | radius: 5,
36 | innerRadius: 0,
37 | outerRadius: 100,
38 | label: '',
39 | pieValue: ''
40 | };
41 |
42 | export const propsSlice = createSlice({
43 | name: 'props',
44 | initialState,
45 | reducers: {
46 | changeProps: (state, action) => {
47 | let dataVal;
48 | let numVal;
49 | const { name, value } = action.payload;
50 | // console.log(typeof name);
51 | // console.log(`Updating ${name}`);
52 |
53 | // Need to keep a seperate dataString property to handle cases where the JSON.Parse is invalid.
54 | // When the dataString changes it should also try to update the dataVal
55 | if (name === 'dataString') {
56 |
57 | try{
58 | state['data'] = JSON.parse(value)
59 | console.log(dataVal, 'StateSet')
60 | } catch (error) {
61 | console.log(error, 'error occured')
62 | state['data'] = undefined;
63 | }
64 | }
65 | if (name === 'height' || name === 'width')
66 | numVal = +value < 100 ? 500 : parseInt(value);
67 | else if (name === 'radius') numVal = +value < 1 ? 5 : parseInt(value);
68 | else if (name === 'outerRadius')
69 | numVal = +value > 1000 ? 100 : parseInt(value);
70 | else if (name === 'innerRadius')
71 | numVal = +value > state.outerRadius ? state.outerRadius : parseInt(value);
72 | else if (name === 'barPadding' || name === 'thresholds') numVal = +value;
73 |
74 | state[name] = numVal || value;
75 | }
76 | }
77 | });
78 |
79 | // Action creators are generated for each case reducer function
80 | export const { changeProps } = propsSlice.actions;
81 |
82 | export default propsSlice.reducer;
83 |
84 | // Initial extra boilerplate-y reducers from Approach 1, where i set up each action type and dispatch actions based on name in the form, but this is v repetitive and not dry (but now we only need the one props reducer above !)
85 |
86 | // changeName: (state, action) => {
87 | // console.log('Changing Chart name');
88 | // state.name = action.payload;
89 | // },
90 | // changeData: (state, action) => {
91 | // console.log('changing data');
92 | // state.data = JSON.parse(action.payload);
93 | // },
94 | // changeXKey: (state, action) => {
95 | // console.log('Changing X Key');
96 | // state.xKey = action.payload;
97 | // },
98 | // changeYKey: (state, action) => {
99 | // console.log('Changing Y Key');
100 | // state.yKey = action.payload;
101 | // },
102 | // changeXAxisLabel: (state, action) => {
103 | // console.log('Changing X Axis Label');
104 | // state.xAxisLabel = action.payload;
105 | // },
106 | // changeYAxisLabel: (state, action) => {
107 | // console.log('Changing Y Axis Label');
108 | // state.yAxisLabel = action.payload;
109 | // },
110 | // changeHeight: (state, action) => {
111 | // if (+action.payload < 100) {
112 | // console.log(
113 | // 'Value must not be less than 100 px. Resetting to default.'
114 | // );
115 | // action.payload = 500;
116 | // console.log(action.payload);
117 | // state.height = action.payload;
118 | // return;
119 | // }
120 | // console.log('Changing height');
121 | // state.height = +action.payload;
122 | // },
123 | // changeWidth: (state, action) => {
124 | // if (+action.payload < 100) {
125 | // console.log(
126 | // 'Value must not be less than 100 px. Resetting to default.'
127 | // );
128 | // action.payload = 500;
129 | // console.log(action.payload);
130 | // state.width = action.payload;
131 | // return;
132 | // }
133 | // console.log('Changing width');
134 | // state.width = +action.payload;
135 | // },
136 | // // specific to histograms
137 | // changeBarPadding: (state, action) => {
138 | // console.log('Changing barPadding');
139 | // state.barPadding = +action.payload;
140 | // },
141 | // changeThresholds: (state, action) => {
142 | // console.log('Changing thresholds');
143 | // state.thresholds = +action.payload;
144 | // },
145 | // //specific to scatterplot
146 | // changeRadius: (state, action) => {
147 | // if (+action.payload < 1) {
148 | // console.log('Value must not be less than 1. Resetting to default.');
149 | // action.payload = 5;
150 | // console.log(action.payload);
151 | // state.radius = action.payload;
152 | // return;
153 | // }
154 | // console.log('Changing radius');
155 | // state.radius = +action.payload;
156 | // }
157 |
158 | // // Action creators are generated for each case reducer function
159 | // export const {
160 | // changeProps
161 | // // changeName,
162 | // // changeData,
163 | // // changeXKey,
164 | // // changeYKey,
165 | // // changeXAxisLabel,
166 | // // changeYAxisLabel,
167 | // // changeHeight,
168 | // // changeWidth,
169 | // // changeThresholds,
170 | // // changeBarPadding,
171 | // // changeRadius
172 | // } = propsSlice.actions;
173 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 | ad3lie
11 |
12 |
13 |
14 |
15 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 | import { render } from 'react-dom';
4 | // import App from './App.jsx'; --> Moved to Archives
5 | import AppRoutes from './AppRoutes.jsx';
6 | import styles from './styles.css';
7 | import { store } from './app/store';
8 | import { Provider } from 'react-redux';
9 |
10 | /**
11 | * AppRoutes is the generalized version that programmatically generates routes based on our 'dictionary' of chart info
12 | */
13 |
14 | const root = createRoot(document.getElementById('root'));
15 | root.render(
16 |
17 |
18 |
19 | );
20 |
--------------------------------------------------------------------------------
/src/mockup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
18 |
19 |
20 |
21 | Customizer Container
22 |
23 |
24 |
25 | Chart Container
26 |
27 |
28 |
29 | Code Container
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
49 | }
52 | >
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | -->
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | @tailwind utilities;
2 | @tailwind base;
3 | @tailwind components;
4 |
5 |
6 |
7 |
8 | .glassglow:hover {
9 | color: rgb(255, 255, 255);
10 | text-shadow: 0 0 10px #fff;
11 | text-shadow: 0 0 20px #fff;
12 | text-shadow: 0 0 40px #fff;
13 | text-shadow: 0 0 80px #fff;
14 | box-shadow: 0 0 40px #fff;
15 | transform: translate(0px, -2.5%);
16 | }
17 |
18 |
19 | .glass {
20 | /* From https://css.glass */
21 | backdrop-filter: blur(8px) saturate(190%);
22 | -webkit-backdrop-filter: blur(20px) saturate(190%);
23 | background-color: rgba(249, 243, 243, 0.124);
24 | border-radius: 10px;
25 | border: 8px solid rgba(6, 6, 6, 0);
26 | }
27 | .glass2 {
28 | /* From https://css.glass */
29 | backdrop-filter: blur(5px) saturate(190%);
30 | -webkit-backdrop-filter: blur(20px) saturate(190%);
31 | background-color: rgba(249, 243, 243, 0.19);
32 | border-radius: 22px;
33 | border: 8px solid rgba(6, 6, 6, 0);
34 | }
35 |
36 | /* Glassmorphism Carousel container effect */
37 | .glass33 {
38 | /* From https://css.glass */
39 | backdrop-filter: blur(20px) saturate(190%);
40 | -webkit-backdrop-filter: blur(20px) saturate(190%);
41 | background-color: rgba(243, 245, 248, 0.761);
42 | border-radius: 10px;
43 | border: 1px solid rgba(37, 21, 255, 0);
44 | }
45 |
46 | .glass3 {
47 | background: rgb(252, 252, 252);
48 | box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 );
49 | backdrop-filter: blur( 4px );
50 | -webkit-backdrop-filter: blur( 4px );
51 | border-radius: 10px;
52 | border: 1px solid rgba( 255, 255, 255, 0.18 );
53 | }
54 | /* Glassmorphism card effect */
55 | .card {
56 | backdrop-filter: blur(16px) saturate(180%);
57 | -webkit-backdrop-filter: blur(16px) saturate(180%);
58 | background-color: rgba(250, 250, 250, 0.19);
59 | border-radius: 12px;
60 | border: 1px solid rgba(32, 31, 31, 0);
61 | }
62 |
63 | .glow-on-hover {
64 | width: 220px;
65 | height: 50px;
66 | border: none;
67 | outline: none;
68 | color: #fff;
69 | background: #111;
70 | cursor: pointer;
71 | position: relative;
72 | z-index: 0;
73 | border-radius: 10px;
74 | }
75 |
76 | body {
77 | /* background-image: url('./topy.png'); */
78 | background-color: #1e293b;
79 | background-repeat: no-repeat;
80 | background-size: 100% 100%;
81 | padding: 1.6em 2em 4em;
82 | letter-spacing: -0.011em;
83 | font-family: 'Inter var', sans-serif;
84 | font-size: 16px;
85 | color: #34495e;
86 | }
87 |
88 |
89 |
90 | .slides {
91 | background-size: 50% 50%
92 | }
93 |
94 | .menu-icon,
95 | .fa-regular span {
96 | font-size: 18px;
97 | }
98 |
99 |
100 |
101 | /* KEEP THESE COMMENTED OUT SECTIONS HERE
102 | body{
103 | display: flex;
104 | justify-content: center;
105 | align-items: center;
106 | flex-wrap: nowrap;
107 | min-height:100vh;
108 | background: #212534
109 | } */
110 | /*
111 | .main{
112 | position: relative;
113 | display: flex;
114 | justify-content: center;
115 | align-items: center;
116 | }
117 |
118 |
119 | .card{
120 | position: relative;
121 | display:flex;
122 | height:100px;
123 | width: 250px;
124 | background-color: #0f16e969;
125 | border-radius: 15px;
126 | justify-content: center;
127 | align-items: center;
128 | flex-wrap: nowrap;
129 | flex-direction:column;
130 | text-align:center;
131 | border:2px solid rgba(99, 230, 6, 0.195);
132 | overflow:hidden;
133 | transition:.6s;
134 | box-shadow:0px 0px 100px rgba(8, 118, 128, 0.15);
135 | }
136 |
137 | .text1{
138 | position:relative;
139 | font-size:33px;
140 | font-family:roboto;
141 | color: rgb(88 199 250);
142 | font-weight:100;
143 | letter-spacing:1px;
144 | transition:.7s;
145 | transform:translateY(110px);
146 | }
147 |
148 | .card:hover .text1{transform:translateY(-150px);opacity:0}
149 | .text2{color:#dbf9ff;
150 | font-family:roboto;
151 | font-weight:100;
152 | font-size:14px;
153 | width:100px;
154 | line-height:25px;
155 | transform:translateY(200px);
156 | transition:.7s;
157 | opacity:0
158 | }
159 |
160 | .hover{}
161 | h2{
162 | font-size:38px;
163 | font-family:roboto;
164 | color:rgb(88 199 250);
165 | font-weight:400;
166 | margin:0;
167 | padding:0;
168 | transform:translateY(200px);
169 | opacity:1;
170 | transition:.6s;
171 | opacity:0
172 | }
173 |
174 | button{
175 | transform:translatey(200px);
176 | transition:.88s;
177 | opacity:0;
178 | width:110px;
179 | height:38px;
180 | border-radius:40px;
181 | font-size:12px;
182 | font-family:roboto;
183 | text-transform:uppercase;
184 | font-weight:300;
185 | letter-spacing:1px;
186 | color:white;
187 | background-color: #0beef9;
188 | background-image: linear-gradient(315deg, #0beef9 0%, #48a9fe 74%);
189 | border:none;
190 | cursor:pointer
191 | }
192 | .card:hover .hover{display:block}
193 | .card:hover h2{transform:translatey(-38px);opacity:1}
194 | .card:hover .text2{transform:translatey(-20px);
195 | opacity:1}
196 | .card:hover button{transform:translatey(5px);opacity:1}
197 | .card:hover{
198 | transform:scale(110%);
199 | box-shadow:0px 0px 100px rgb(88 199 250);
200 | }
201 |
202 | button:active{
203 | /* .glass333 {
204 | /* From https://css.glass */
205 | /* backdrop-filter: blur(20px) saturate(190%);
206 | -webkit-backdrop-filter: blur(20px) saturate(190%);
207 | background-color: rgb(255, 255, 255);
208 | border-radius: 22px;
209 | border: 8px solid rgba(6, 6, 6, 0);
210 | } */
211 |
212 |
213 | /* .glass455{ */
214 | /* From https://css.glass */
215 | /* backdrop-filter: blur(8px) saturate(190%); */
216 | /* -webkit-backdrop-filter: blur(20px) saturate(190%); */
217 | /* background-color: rgba(0, 253, 253, 0.585); */
218 | /* border-radius: 22px; */
219 | /* border: 8px solid rgba(6, 6, 6, 0); */
220 | /* }
221 | /* color:#0beef9 */
222 | /* } */
223 |
--------------------------------------------------------------------------------
/src/topy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/src/topy.png
--------------------------------------------------------------------------------
/src/utils/CodePreview.js:
--------------------------------------------------------------------------------
1 | import forOwn from 'lodash/forOwn';
2 | import isPlainObject from 'lodash/isPlainObject';
3 | import isArray from 'lodash/isArray';
4 | import isString from 'lodash/isString';
5 | import isNumber from 'lodash/isNumber';
6 | import isBoolean from 'lodash/isBoolean';
7 | import dedent from 'dedent-js';
8 | import styled from 'styled-components';
9 | import prettier from 'prettier/standalone';
10 | import parserBabel from 'prettier/parser-babel';
11 |
12 | export const indent = (content, spaces = 8) =>
13 | content
14 | .split('\n')
15 | .map((line, i) => {
16 | if (i === 0) return line;
17 | return `${' '.repeat(spaces)}${line}`;
18 | })
19 | .join('\n');
20 |
21 | export const toJson = (value) => {
22 | const jsonString = JSON.stringify(value, null, 4);
23 | const normalized = jsonString
24 | .replace(/^(\s+)"([a-z]{1}[a-z]*)"\: /gim, (_match, space, key) => {
25 | return `${space}${key}: `;
26 | })
27 | .replace(/"/gm, `'`);
28 |
29 | if (normalized.length < 80) {
30 | return normalized.replace(/\n/gm, ' ').replace(/\s{2,}/g, ' ');
31 | }
32 | return indent(normalized);
33 | };
34 |
35 | export const generateChartCode = (
36 | name,
37 | props,
38 | { dataKey, children, defaults, pkg }
39 | ) => {
40 | const properties = [];
41 | let args = '';
42 |
43 | if (dataKey !== undefined) {
44 | properties.push(`${dataKey}={${dataKey}}`);
45 | args = `{ ${dataKey} /* see ${dataKey} from your Javascript data file */ }`;
46 | }
47 |
48 | forOwn(props, (_value, key) => {
49 | if (_value === undefined) return;
50 | if (defaults && defaults[key] === _value) return;
51 | if (key === 'theme') return;
52 |
53 | let value;
54 | if (isPlainObject(_value)) {
55 | value = `{${toJson(_value)}}`;
56 | } else if (isArray(_value)) {
57 | value = `{${toJson(_value)}}`;
58 | } else if (isString(_value)) {
59 | value = `"${_value}"`;
60 | } else if (isBoolean(_value)) {
61 | value = `{${_value ? 'true' : 'false'}}`;
62 | } else if (isNumber(_value)) {
63 | value = `{${_value}}`;
64 | } else if (typeof _value === 'function') {
65 | value = `{${indent(dedent(_value.toString()), 8)}}`;
66 | } else if (_value === null) {
67 | value = `{null}`;
68 | } else {
69 | value = _value;
70 | }
71 |
72 | properties.push(`${key}=${value}`);
73 | });
74 |
75 | const install = `// npm install @ad3lie`;
76 |
77 | // Currently removed children imports as the user should only be importing the required chart template (ex. BarChart.jsx) and their custom data file (MyBarChart.js) in order to use the customized component (MyBarChart.jsx)
78 | // const imports = [name, ...children.map((c) => c)].map(
79 | // (i) => `import { ${i} } from 'ad3lie'`
80 | // );
81 | const imports = [name].map((i) => `import { ${i} } from 'ad3lie'`);
82 | const importReact = `import React from 'react'`
83 |
84 | const importData = `import { ${dataKey} } from 'My${name}Data.js'`;
85 |
86 | let warning = '';
87 | if (name) {
88 | warning = [
89 | ``,
90 | `// Before use, remember to npm i all dependencies`,
91 | `// and the @ad3lie component library to use your charts,`,
92 | `// otherwise, no charts will be rendered.`,
93 | `// Copy the following code to your component file`,
94 | `// along with your Javascript data file.`
95 | ].join('\n');
96 | }
97 |
98 | return `// install (please make sure versions match peerDependencies)
99 | ${install}
100 | ${importReact}
101 | ${importData}
102 | ${imports.join('\n')}
103 | ${warning}
104 | const My${name} = () => (
105 | <${name}
106 | ${properties.join('\n ')}
107 | />
108 | )
109 |
110 | export default My${name}`;
111 | };
112 |
113 | export const formatCode = (code) => {
114 | return prettier.format(code.innerText, {
115 | singleQuote: true,
116 | jsxSingleQuote: true,
117 | trailingComma: 'es5',
118 | bracketSpacing: true,
119 | bracketSameLine: true,
120 | parser: 'babel',
121 | plugins: [parserBabel]
122 | });
123 | };
124 |
125 | // just using styled components here only for testing html preview
126 | // our code is enclosed in an HTML tag
127 | export const CodeText = styled.code``;
128 |
129 | export const CodeBlock = styled.pre`
130 | margin: 0;
131 | font-size: 0.8rem;
132 | line-height: 1.7;
133 | padding: 12px 20px;
134 | overflow: scroll;
135 | height: 100%;
136 | `;
137 |
138 | export const Code = styled.div`
139 | position: absolute;
140 | top: 46px;
141 | bottom: 0;
142 | width: 100%;
143 | overflow: auto;
144 | `;
145 |
--------------------------------------------------------------------------------
/src/utils/ExportData.js:
--------------------------------------------------------------------------------
1 | // ExportData is our current workaround to not being able to use node's fs module
2 | // (cannot run node from a browser)
3 |
4 | // converts array of Javascript objects to a string
5 | // downloadable as a .js file
6 | export const download = (filename, arr) => {
7 | let text = `export const data = [${arr
8 | .reduce((str, obj) => {
9 | return (str +=
10 | '{' + Object.keys(obj).map((key) => `'${key}': ${obj[key]}`) + `}, \n`);
11 | }, '')
12 | .trim()
13 | .slice(0, -1)}]`;
14 |
15 | let element = document.createElement('a');
16 | element.setAttribute(
17 | 'href',
18 | 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
19 | );
20 | element.setAttribute('download', filename);
21 |
22 | element.style.display = 'none';
23 | document.body.appendChild(element);
24 |
25 | element.click();
26 | document.body.removeChild(element);
27 | };
28 |
29 | // general function to download a file (taking in plain text)
30 |
31 | export const downloadCode = (filename, text) => {
32 | let element = document.createElement('a');
33 | element.setAttribute(
34 | 'href',
35 | 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)
36 | );
37 | element.setAttribute('download', filename);
38 |
39 | element.style.display = 'none';
40 | document.body.appendChild(element);
41 |
42 | element.click();
43 | document.body.removeChild(element);
44 | };
45 |
--------------------------------------------------------------------------------
/src/utils/dummyTimelineData.js:
--------------------------------------------------------------------------------
1 | // This generates random fake data to show for linechart
2 | const fakeTimelineData = () => {
3 | const timelineData = []
4 | for (let i = 0; i < 50; i++) {
5 | const obj = {
6 | x: i,
7 | y: Math.random() * 100
8 | }
9 | timelineData.push(obj)
10 | }
11 | return timelineData
12 | }
13 |
14 | export default fakeTimelineData
--------------------------------------------------------------------------------
/src/utils/dummyfruitsdata.js:
--------------------------------------------------------------------------------
1 | export const sampleFruitData = [
2 | {
3 | label: 'apples',
4 | value: 20
5 | },
6 | {
7 | label: 'bananas',
8 | value: 40
9 | },
10 | {
11 | label: 'pears',
12 | value: 30
13 | },
14 | {
15 | label: 'papaya',
16 | value: 50
17 | },
18 | {
19 | label: 'oranges',
20 | value: 70
21 | }
22 | ];
23 |
--------------------------------------------------------------------------------
/src/utils/dummypenguinsdata.js:
--------------------------------------------------------------------------------
1 | //convert this to json
2 |
3 | export const sampleData = [
4 | {
5 | species: 'Adelie',
6 | island: 'Torgersen',
7 | culmen_length_mm: 39.2,
8 | culmen_depth_mm: 19.6,
9 | flipper_length_mm: 195,
10 | body_mass_g: 4675,
11 | sex: 'MALE'
12 | },
13 | {
14 | species: 'Gentoo',
15 | island: 'Torgersen',
16 | culmen_length_mm: 34.1,
17 | culmen_depth_mm: 18.1,
18 | flipper_length_mm: 193,
19 | body_mass_g: 3475,
20 | sex: null
21 | },
22 | {
23 | species: 'Emperor',
24 | island: 'Torgersen',
25 | culmen_length_mm: 42,
26 | culmen_depth_mm: 20.2,
27 | flipper_length_mm: 190,
28 | body_mass_g: 4250,
29 | sex: null
30 | },
31 | {
32 | species: 'Fairy',
33 | island: 'Torgersen',
34 | culmen_length_mm: 37.8,
35 | culmen_depth_mm: 17.1,
36 | flipper_length_mm: 186,
37 | body_mass_g: 3300,
38 | sex: null
39 | },
40 | {
41 | species: 'Rock',
42 | island: 'Torgersen',
43 | culmen_length_mm: 37.8,
44 | culmen_depth_mm: 17.3,
45 | flipper_length_mm: 180,
46 | body_mass_g: 3700,
47 | sex: null
48 | }
49 | ];
50 |
51 | let xKey = 'species';
52 | let yKey = 'body_mass_g';
53 |
54 | const getLineChartData = (data, xKey, yKey) => {
55 | const result = [];
56 | data.reduce(function (acc, curr) {
57 | if (!acc[curr[xKey]]) {
58 | acc[curr[xKey]] = { [xKey]: curr[xKey], [yKey]: 0 };
59 | result.push(acc[curr[xKey]]);
60 | }
61 | acc[curr[xKey]][yKey] += curr[yKey];
62 | return acc;
63 | }, []);
64 | return result;
65 | };
66 | // console.log(getLineChartData(penguins, 'species', 'body_mass_g'));
67 |
--------------------------------------------------------------------------------
/src/utils/handlers.js:
--------------------------------------------------------------------------------
1 | // Data must be input in JSON format
2 | export const handleData = (e) => {
3 | e.preventDefault();
4 | //Input data works for JSON format - see jsonpenguins.txt
5 | setData(JSON.parse(e.target.value));
6 | };
7 |
8 | // Data needs to be re-input as key changes, since grouped data is already set in state
9 | export const handleXKey = (e) => {
10 | e.preventDefault();
11 | setXKey(e.target.value);
12 | };
13 |
14 | export const handleYKey = (e) => {
15 | e.preventDefault();
16 | setYKey(e.target.value);
17 | };
18 |
19 | export const handleXAxisLabel = (e) => {
20 | e.preventDefault();
21 | setXAxisLabel(e.target.value);
22 | };
23 |
24 | export const handleYAxisLabel = (e) => {
25 | e.preventDefault();
26 | setYAxisLabel(e.target.value);
27 | };
28 |
29 | export const handleWidth = (e) => {
30 | e.preventDefault();
31 | if (+e.target.value < 100) {
32 | console.log('Value must not be less than 100 px. Resetting to default.');
33 | setWidth(500);
34 | return;
35 | }
36 | setWidth(+e.target.value);
37 | };
38 |
39 | export const handleHeight = (e) => {
40 | e.preventDefault();
41 | if (+e.target.value < 100) {
42 | console.log('Value must not be less than 100 px. Resetting to default.');
43 | setHeight(500);
44 | return;
45 | }
46 | setHeight(+e.target.value);
47 | };
48 |
49 | export const handleThresholds = (e) => {
50 | e.preventDefault();
51 | setThresholds(+e.target.value);
52 | };
53 |
54 | export const handleBarPadding = (e) => {
55 | e.preventDefault();
56 | setBarPadding(+e.target.value);
57 | };
58 |
59 | //below are unique to pie chart
60 | export const handleOuter = (e) => {
61 | if (+e.target.value > 100) {
62 | console.log(
63 | 'Value must not be greater than or equal to 100. Resetting to default.'
64 | );
65 | setOuter(100);
66 | return;
67 | }
68 | setOuter(+e.target.value);
69 | };
70 |
71 | export const handleInner = (e) => {
72 | if (+e.target.value > outerRadius) {
73 | console.log(
74 | 'Value must not be greater than or equal to size of piechart. Resetting to default.'
75 | );
76 | setInner(0);
77 | return;
78 | }
79 | setInner(+e.target.value);
80 | };
81 |
82 | // Data needs to be re-input as key changes, since grouped data is already set in state
83 | export const handleLabel = (e) => {
84 | e.preventDefault();
85 | setLabel(e.target.value);
86 | };
87 |
88 | export const handleValue = (e) => {
89 | e.preventDefault();
90 | setValue(e.target.value);
91 | };
92 |
--------------------------------------------------------------------------------
/src/utils/hooks.js:
--------------------------------------------------------------------------------
1 | function useInterval(callback, delay) {
2 | const savedCallback = useRef();
3 |
4 | // Remember the latest callback.
5 | useEffect(() => {
6 | savedCallback.current = callback;
7 | });
8 |
9 | // Set up the interval.
10 | useEffect(() => {
11 | function tick() {
12 | savedCallback.current();
13 | }
14 | if (delay !== null) {
15 | let id = setInterval(tick, delay);
16 | return () => clearInterval(id);
17 | }
18 | }, [delay]);
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/jsonFruits:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "label" : "apples",
4 | "value" : 20
5 | },
6 | {
7 | "label" : "bananas",
8 | "value" : 40
9 | },
10 | {
11 | "label" : "pears",
12 | "value" : 30
13 | },
14 | {
15 | "label" : "papaya",
16 | "value" : 50
17 | },
18 | {
19 | "label" : "oranges",
20 | "value" : 70
21 | }
22 | ]
--------------------------------------------------------------------------------
/src/utils/jsonLine:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "x": 0,
4 | "y": 93.69018434064849
5 | },
6 | {
7 | "x": 1,
8 | "y": 46.3018492732896
9 | },
10 | {
11 | "x": 2,
12 | "y": 58.14436737157354
13 | },
14 | {
15 | "x": 3,
16 | "y": 54.51790586843437
17 | },
18 | {
19 | "x": 4,
20 | "y": 22.09286874993026
21 | },
22 | {
23 | "x": 5,
24 | "y": 85.86961518991087
25 | },
26 | {
27 | "x": 6,
28 | "y": 89.11724125479952
29 | },
30 | {
31 | "x": 7,
32 | "y": 9.455992643308742
33 | },
34 | {
35 | "x": 8,
36 | "y": 20.99119171803341
37 | },
38 | {
39 | "x": 9,
40 | "y": 82.7453493304737
41 | },
42 | {
43 | "x": 10,
44 | "y": 50.90040434757954
45 | },
46 | {
47 | "x": 11,
48 | "y": 14.949899532134614
49 | },
50 | {
51 | "x": 12,
52 | "y": 52.7258770048735
53 | },
54 | {
55 | "x": 13,
56 | "y": 1.5991168402831546
57 | },
58 | {
59 | "x": 14,
60 | "y": 84.99314018859734
61 | },
62 | {
63 | "x": 15,
64 | "y": 29.274661441009275
65 | },
66 | {
67 | "x": 16,
68 | "y": 99.74242952649837
69 | },
70 | {
71 | "x": 17,
72 | "y": 23.930472549734837
73 | },
74 | {
75 | "x": 18,
76 | "y": 48.17435412111004
77 | },
78 | {
79 | "x": 19,
80 | "y": 15.87766924443319
81 | },
82 | {
83 | "x": 20,
84 | "y": 75.3447958777112
85 | },
86 | {
87 | "x": 21,
88 | "y": 15.759655838367625
89 | },
90 | {
91 | "x": 22,
92 | "y": 98.4290012747485
93 | },
94 | {
95 | "x": 23,
96 | "y": 37.9983866071876
97 | },
98 | {
99 | "x": 24,
100 | "y": 23.070307155267678
101 | },
102 | {
103 | "x": 25,
104 | "y": 80.59082720899946
105 | },
106 | {
107 | "x": 26,
108 | "y": 77.83023353585077
109 | },
110 | {
111 | "x": 27,
112 | "y": 85.58448341035671
113 | },
114 | {
115 | "x": 28,
116 | "y": 44.01333837503019
117 | },
118 | {
119 | "x": 29,
120 | "y": 43.31288064529644
121 | },
122 | {
123 | "x": 30,
124 | "y": 52.513463428867155
125 | },
126 | {
127 | "x": 31,
128 | "y": 62.653139648789335
129 | },
130 | {
131 | "x": 32,
132 | "y": 65.20603303326563
133 | },
134 | {
135 | "x": 33,
136 | "y": 93.53359835133628
137 | },
138 | {
139 | "x": 34,
140 | "y": 11.134986072653795
141 | },
142 | {
143 | "x": 35,
144 | "y": 27.148267186723206
145 | },
146 | {
147 | "x": 36,
148 | "y": 42.87113194562071
149 | },
150 | {
151 | "x": 37,
152 | "y": 52.67505265781984
153 | },
154 | {
155 | "x": 38,
156 | "y": 78.96405613220085
157 | },
158 | {
159 | "x": 39,
160 | "y": 90.82843831157
161 | },
162 | {
163 | "x": 40,
164 | "y": 34.97670270352597
165 | },
166 | {
167 | "x": 41,
168 | "y": 76.03513540984592
169 | },
170 | {
171 | "x": 42,
172 | "y": 20.16765664109874
173 | },
174 | {
175 | "x": 43,
176 | "y": 91.92304733837278
177 | },
178 | {
179 | "x": 44,
180 | "y": 42.639931564063914
181 | },
182 | {
183 | "x": 45,
184 | "y": 13.265295678861255
185 | },
186 | {
187 | "x": 46,
188 | "y": 17.546050260687096
189 | },
190 | {
191 | "x": 47,
192 | "y": 50.34754937454555
193 | },
194 | {
195 | "x": 48,
196 | "y": 65.7982470687585
197 | },
198 | {
199 | "x": 49,
200 | "y": 76.90338996712616
201 | }
202 | ]
--------------------------------------------------------------------------------
/src/utils/jsonpenguins:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "species": "Adelie",
4 | "island": "Torgersen",
5 | "culmen_length_mm": 39.2,
6 | "culmen_depth_mm": 19.6,
7 | "flipper_length_mm": 195,
8 | "body_mass_g": 4675,
9 | "sex": "MALE"
10 | },
11 | {
12 | "species": "Gentoo",
13 | "island": "Torgersen",
14 | "culmen_length_mm": 34.1,
15 | "culmen_depth_mm": 18.1,
16 | "flipper_length_mm": 193,
17 | "body_mass_g": 3475,
18 | "sex": null
19 | },
20 | {
21 | "species": "Emperor",
22 | "island": "Torgersen",
23 | "culmen_length_mm": 42,
24 | "culmen_depth_mm": 20.2,
25 | "flipper_length_mm": 190,
26 | "body_mass_g": 4250,
27 | "sex": null
28 | },
29 | {
30 | "species": "Fairy",
31 | "island": "Torgersen",
32 | "culmen_length_mm": 37.8,
33 | "culmen_depth_mm": 17.1,
34 | "flipper_length_mm": 186,
35 | "body_mass_g": 3400,
36 | "sex": null
37 | },
38 | {
39 | "species": "Rock",
40 | "island": "Torgersen",
41 | "culmen_length_mm": 37.8,
42 | "culmen_depth_mm": 17.3,
43 | "flipper_length_mm": 180,
44 | "body_mass_g": 3700,
45 | "sex": null
46 | }
47 | ]
--------------------------------------------------------------------------------
/src/utils/observable.js:
--------------------------------------------------------------------------------
1 | /** Implementing the observer pattern for state management
2 | * (also bc i could do redux but im too lazy to implement that atm)
3 | */
4 |
5 | export const makeObservable = (target) => {
6 | let listeners = []; // initial listeners can be passed an an argument as well
7 | let value = target;
8 |
9 | function get() {
10 | return value;
11 | }
12 |
13 | function set(newValue) {
14 | if (value === newValue) return;
15 | value = newValue;
16 | listeners.forEach((l) => l(value));
17 | }
18 |
19 | function subscribe(listenerFunc) {
20 | listeners.push(listenerFunc);
21 | return () => unsubscribe(listenerFunc); // will be used inside React.useEffect
22 | }
23 |
24 | function unsubscribe(listenerFunc) {
25 | listeners = listeners.filter((l) => l !== listenerFunc);
26 | }
27 |
28 | return {
29 | get,
30 | set,
31 | subscribe
32 | };
33 | };
34 |
35 | export const userStore = makeObservable({ name: 'user', count: 0 });
36 |
37 | const useUser = () => {
38 | const [user, setUser] = React.useState(userStore.get());
39 |
40 | React.useEffect(() => {
41 | return userStore.subscribe(setUser);
42 | }, []);
43 |
44 | const actions = React.useMemo(() => {
45 | return {
46 | setName: (name) => userStore.set({ ...user, name }),
47 | incrementCount: () => userStore.set({ ...user, count: user.count + 1 }),
48 | decrementCount: () => userStore.set({ ...user, count: user.count - 1 })
49 | };
50 | }, [user]);
51 |
52 | return {
53 | state: user,
54 | actions
55 | };
56 | };
57 |
--------------------------------------------------------------------------------
/src/utils/parseData.js:
--------------------------------------------------------------------------------
1 | import * as d3 from 'd3';
2 |
3 | const randomAroundMean = (mean, deviation) =>
4 | mean + boxMullerRandom() * deviation;
5 | const boxMullerRandom = () =>
6 | Math.sqrt(-2.0 * Math.log(Math.random())) *
7 | Math.cos(2.0 * Math.PI * Math.random());
8 |
9 | const today = new Date();
10 |
11 | // TYPE: DATE
12 | const formatDate = d3.timeFormat('%m/%d/%Y');
13 | export const getTimelineData = (length = 100) => {
14 | let lastTemperature = randomAroundMean(70, 20);
15 | const firstTemperature = d3.timeDay.offset(today, -length);
16 |
17 | return new Array(length).fill(0).map((d, i) => {
18 | lastTemperature += randomAroundMean(0, 2);
19 | return {
20 | date: formatDate(d3.timeDay.offset(firstTemperature, i)),
21 | temperature: lastTemperature
22 | };
23 | });
24 | };
25 |
26 | // TYPE: NUMBER
27 | export const getScatterData = (count = 100) =>
28 | new Array(count).fill(0).map((d, i) => ({
29 | temperature: randomAroundMean(70, 20),
30 | humidity: randomAroundMean(0.5, 0.1)
31 | }));
32 |
33 | // ============================================================================================================= //
34 |
35 | import { userEnteredData, userAxisData } from './EnteredData.jsx';
36 |
37 | // TYPE: NUMBER/STRING ---- BAR CHART
38 | // export const getBarChartData = (xKey, yKey, arr) => {
39 | // // // Three Steps
40 | // // // 1. Create a Set of Unique Keys
41 | // let set = new Set()
42 | // for (const obj of arr) {
43 | // set.push(obj[xKey]);
44 | // }
45 | // // 2. Create an Array to hold the Accumulated Results
46 | // let aggs = []
47 | // // 3. Iterate over the set, and use Map and Reduce to create an obj for each xKey
48 | // for (const key of set) {
49 | // const agg = arr.map(obj => key === obj[xKey]).reduce((acc, curr) => {
50 | // return acc + curr[xKey]
51 | // }, 0)
52 | // aggs.push(agg);
53 | // }
54 | // return aggs
55 | // }
56 |
57 | // console.log(getBarChartData("species","body_mass_g",userEnteredData))
58 |
59 | // REDUCE GOD
60 | // const test = (xKey, yKey, data) => {
61 | // const arrOfObj = Array.from(
62 | // data.reduce((m, { xKey, yKey }) =>
63 | // m.set(xKey, (m.get(xKey) || 0) + yKey),
64 | // new Map()
65 | // ),
66 | // ([xKey, yKey]) => ({ xKey, yKey })
67 | // );
68 |
69 | // return arrOfObj;
70 | // }
71 |
72 | export const getBarChartData = (data, xKey, yKey) => {
73 | const arrOfObj = Array.from(
74 | data
75 | .reduce((acc, { value, ...r }) => {
76 | const key = JSON.stringify(r);
77 | // console.log('key', key);
78 | const current = acc.get(key) || { ...r };
79 | return acc.set(key, { ...current });
80 | }, new Map())
81 | .values()
82 | );
83 | return arrOfObj;
84 | };
85 |
86 | // Works for reducing an array of objects, grouped by two keys
87 | /** Ex. Group by Species and Body_mass_g
88 | * [ { species: 'Gentoo', body_mass_g: 392350 },
89 | { species: 'Adelie', body_mass_g: 10100 } ]
90 | */
91 | const sampleData = [
92 | {
93 | species: 'Adelie',
94 | island: 'Torgersen',
95 | culmen_length_mm: 39.2,
96 | culmen_depth_mm: 19.6,
97 | flipper_length_mm: 195,
98 | body_mass_g: 4675,
99 | sex: 'MALE'
100 | },
101 | {
102 | species: 'Gentoo',
103 | island: 'Torgersen',
104 | culmen_length_mm: 34.1,
105 | culmen_depth_mm: 18.1,
106 | flipper_length_mm: 193,
107 | body_mass_g: 3475,
108 | sex: null
109 | },
110 | {
111 | species: 'Emperor',
112 | island: 'Torgersen',
113 | culmen_length_mm: 42,
114 | culmen_depth_mm: 20.2,
115 | flipper_length_mm: 190,
116 | body_mass_g: 4250,
117 | sex: null
118 | },
119 | {
120 | species: 'Chinstrap',
121 | island: 'Torgersen',
122 | culmen_length_mm: 37.8,
123 | culmen_depth_mm: 17.1,
124 | flipper_length_mm: 186,
125 | body_mass_g: 3300,
126 | sex: null
127 | },
128 | {
129 | species: 'Emperor',
130 | island: 'Torgersen',
131 | culmen_length_mm: 37.8,
132 | culmen_depth_mm: 17.3,
133 | flipper_length_mm: 180,
134 | body_mass_g: 3700,
135 | sex: null
136 | }
137 | ];
138 |
139 | export const getBarChartData2 = (data, xKey, yKey) => {
140 | // const data = JSON.parse(stringifiedData);
141 | const result = [];
142 | data.reduce(function (acc, curr) {
143 | if (!acc[curr[xKey]]) {
144 | acc[curr[xKey]] = { [xKey]: curr[xKey], [yKey]: 0 };
145 | result.push(acc[curr[xKey]]);
146 | }
147 | acc[curr[xKey]][yKey] += curr[yKey];
148 | return acc;
149 | }, []);
150 | console.log(result);
151 | return result;
152 | };
153 |
154 | // console.log(getBarChartData2(sampleData, 'species', 'body_mass_g'));
155 |
156 | // console.log(
157 | // 'this is reduce BarChartData',
158 | // getBarChartData('species', 'body_mass_g', userEnteredData)
159 | // );
160 |
161 | // const penguins = Array.from(
162 | // data.reduce(
163 | // (m, { species, body_mass_g }) =>
164 | // m.set(species, (m.get(species) || 0) + body_mass_g),
165 | // new Map()
166 | // ),
167 | // ([species, body_mass_g]) => ({ species, body_mass_g })
168 | // );
169 |
170 | // console.log('arr: ', arr)
171 | // console.log('xKey ', xKey)
172 | // // console.log(' ', )
173 | // const cache = {}
174 | // const result = arr.reduce((acc, curr) => {
175 | // console.log('acc', acc)
176 | // if (curr[xKey] in cache) {
177 | // return acc[cache[xKey]][yKey] += curr[yKey]
178 | // }
179 | // // else add to cache and accumulator
180 | // // ex) curr[xKey] = "Adelie"
181 | // // cache["Adelie"] = length of cache properties ?????????
182 | // cache[curr[xKey]] = Object.keys(cache).length;
183 | // return acc.push(curr)
184 | // }, [])
185 | // return resut;
186 | // }
187 | // reached algos part of the project :upside_down_face:. Given an array of objects, how do you find the total for each unique x-value?
188 | // Result should be [{“species”: “Adelie”, “body_mass_g”: 7200}, {“species”: “Gentoo”, “body_mass_g”: 10950}]
189 | /*
190 | [
191 | {
192 | "species": "Adelie",
193 | "body_mass_g": 3750,
194 | },
195 | {
196 | "species": "Adelie",
197 | "body_mass_g": 3450,
198 | },
199 | {
200 | "species": "Gentoo",
201 | "body_mass_g": 5750,
202 | },
203 | {
204 | "species": "Gentoo",
205 | "body_mass_g": 5200,
206 | }
207 | ]
208 | */
209 |
210 | // TYPE: NUMBERS ---- LINE GRAPHS (ORDERED)
211 |
212 | export const getUONumData = (
213 | userAxis = userAxisData,
214 | count = userEnteredData.length
215 | ) => {
216 | return new Array(count).fill({}).map((d, i) => ({
217 | [userAxis['x']]: userEnteredData[i][userAxis['x']],
218 | [userAxis['y']]: userEnteredData[i][userAxis['y']]
219 | }));
220 | };
221 |
222 | // TYPE: DATE ---- TIMELINE
223 | export const getTimelineData3 = (
224 | length = userEnteredData.length,
225 | userAxis = userAxisData
226 | ) => {
227 | return new Array(length).fill({}).map((d, i) => ({
228 | [userAxis['x']]: new Date(userEnteredData[i][userAxis['x']]),
229 | [userAxis['y']]: userEnteredData[i][userAxis['y']]
230 | }));
231 | };
232 | // console.log('gettimelineData', getTimelineData3());
233 |
234 | // TYPE: NUMBERS ---- LINE GRAPHS (ORDERED)
235 | const objectComparisonCallback = (arrayItemA, arrayItemB) => {
236 | if (arrayItemA[userAxisData['x']] < arrayItemB[userAxisData['x']]) return -1;
237 | if (arrayItemA[userAxisData['x']] > arrayItemB[userAxisData['x']]) return 1;
238 | return 0;
239 | };
240 | userEnteredData.sort(objectComparisonCallback);
241 |
242 | export const getNumbersData = (
243 | userAxis = userAxisData,
244 | count = userEnteredData.length
245 | ) => {
246 | return new Array(count).fill({}).map((d, i) => ({
247 | [userAxis['x']]: userEnteredData[i][userAxis['x']],
248 | [userAxis['y']]: userEnteredData[i][userAxis['y']]
249 | }));
250 | };
251 |
--------------------------------------------------------------------------------
/src/utils/utils.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import { useEffect, useState, useRef } from 'react';
3 | import ResizeObserver from 'resize-observer-polyfill';
4 |
5 | export const accessorPropsType = PropTypes.oneOfType([
6 | PropTypes.func,
7 | PropTypes.number
8 | ]);
9 |
10 | export const useAccessor = (accessor, d, i) =>
11 | typeof accessor == 'function' ? accessor(d, i) : accessor;
12 |
13 | export const dimensionsPropsType = PropTypes.shape({
14 | height: PropTypes.number,
15 | width: PropTypes.number,
16 | marginTop: PropTypes.number,
17 | marginRight: PropTypes.number,
18 | marginBottom: PropTypes.number,
19 | marginLeft: PropTypes.number
20 | });
21 |
22 | export const combineChartDimensions = (dimensions) => {
23 | let parsedDimensions = {
24 | marginTop: 40,
25 | marginRight: 30,
26 | marginBottom: 40,
27 | marginLeft: 75,
28 | ...dimensions
29 | };
30 |
31 | // console.log(parsedDimensions);
32 |
33 | return {
34 | ...parsedDimensions,
35 | boundedHeight: Math.max(
36 | parsedDimensions.height -
37 | parsedDimensions.marginTop -
38 | parsedDimensions.marginBottom,
39 | 0
40 | ),
41 | boundedWidth: Math.max(
42 | parsedDimensions.width -
43 | parsedDimensions.marginLeft -
44 | parsedDimensions.marginRight,
45 | 0
46 | )
47 | };
48 | };
49 |
50 | export const useChartDimensions = (passedSettings) => {
51 | const ref = useRef();
52 | const dimensions = combineChartDimensions(passedSettings);
53 |
54 | if (dimensions.width && dimensions.height) return [ref, dimensions];
55 |
56 | const [width, changeWidth] = useState(0);
57 | const [height, changeHeight] = useState(0);
58 |
59 | useEffect(() => {
60 | const element = ref.current;
61 | const resizeObserver = new ResizeObserver((entries) => {
62 | if (!Array.isArray(entries)) return;
63 | if (!entries.length) return;
64 |
65 | const entry = entries[0];
66 |
67 | if (width != entry.contentRect.width)
68 | changeWidth(entry.contentRect.width);
69 | if (height != entry.contentRect.height)
70 | changeHeight(entry.contentRect.height);
71 | });
72 |
73 | resizeObserver.observe(element);
74 |
75 | return () => resizeObserver.unobserve(element);
76 | }, []);
77 |
78 | const newSettings = combineChartDimensions({
79 | ...dimensions,
80 | width: dimensions.width || width,
81 | height: dimensions.height || height
82 | });
83 |
84 | return [ref, newSettings];
85 | };
86 |
87 | let lastId = 0;
88 | export const useUniqueId = (prefix = '') => {
89 | lastId++;
90 | return [prefix, lastId].join('-');
91 | };
92 |
--------------------------------------------------------------------------------
/src/utils/utils.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | // import PropTypes from 'prop-types';
4 | import { useEffect, useState, useRef } from 'react';
5 | import ResizeObserver from 'resize-observer-polyfill';
6 |
7 | export type accessorPropsType = Function | number;
8 | // export const accessorPropsType = (
9 | // PropTypes.oneOfType([
10 | // PropTypes.func,
11 | // PropTypes.number,
12 | // ])
13 | // )
14 |
15 | export const useAccessor = (
16 | accessor: (arg0: any, arg1: any) => any,
17 | d: any,
18 | i: any
19 | ) => (typeof accessor === 'function' ? accessor(d, i) : accessor);
20 | // export const useAccessor = (accessor, d, i) => (
21 | // typeof accessor == "function" ? accessor(d, i) : accessor
22 | // )
23 |
24 | // export const dimensionsPropsType = (
25 | // PropTypes.shape({
26 | // height: PropTypes.number,
27 | // width: PropTypes.number,
28 | // marginTop: PropTypes.number,
29 | // marginRight: PropTypes.number,
30 | // marginBottom: PropTypes.number,
31 | // marginLeft: PropTypes.number,
32 | // })
33 | // )
34 |
35 | export interface DimensionsPropsType {
36 | height: number;
37 | width: number;
38 | marginTop: number;
39 | marginRight: number;
40 | marginBottom: number;
41 | marginLeft: number;
42 | boundedHeight: number;
43 | boundedWidth: number;
44 | }
45 |
46 | export const combineChartDimensions = (dimensions: any) => {
47 | let parsedDimensions = {
48 | marginTop: 40,
49 | marginRight: 30,
50 | marginBottom: 40,
51 | marginLeft: 75,
52 | ...dimensions
53 | };
54 |
55 | return {
56 | ...parsedDimensions,
57 | boundedHeight: Math.max(
58 | parsedDimensions.height -
59 | parsedDimensions.marginTop -
60 | parsedDimensions.marginBottom,
61 | 0
62 | ),
63 | boundedWidth: Math.max(
64 | parsedDimensions.width -
65 | parsedDimensions.marginLeft -
66 | parsedDimensions.marginRight,
67 | 0
68 | )
69 | };
70 | };
71 |
72 | export const useChartDimensions = (passedSettings: {
73 | marginBottom: number;
74 | }) => {
75 | const ref = useRef();
76 | const dimensions = combineChartDimensions(passedSettings);
77 |
78 | if (dimensions.width && dimensions.height) return [ref, dimensions];
79 |
80 | const [width, changeWidth] = useState(0);
81 | const [height, changeHeight] = useState(0);
82 |
83 |
84 | useEffect(() => {
85 | const element = ref.current;
86 | const resizeObserver = new ResizeObserver((entries: string | any[]) => {
87 | if (!Array.isArray(entries)) return;
88 | if (!entries.length) return;
89 |
90 | const entry = entries[0];
91 |
92 | if (width != entry.contentRect.width)
93 | changeWidth(entry.contentRect.width);
94 | if (height != entry.contentRect.height)
95 | changeHeight(entry.contentRect.height);
96 | });
97 |
98 | resizeObserver.observe(element);
99 |
100 | return () => resizeObserver.unobserve(element);
101 | }, []);
102 |
103 | const newSettings = combineChartDimensions({
104 | ...dimensions,
105 | width: dimensions.width || width,
106 | height: dimensions.height || height
107 | });
108 |
109 | return [ref, newSettings];
110 | };
111 |
112 | let lastId = 0;
113 | export const useUniqueId = (prefix = '') => {
114 | lastId++;
115 | return [prefix, lastId].join('-');
116 | };
117 |
--------------------------------------------------------------------------------
/stats.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/stats.json
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | `./index.html'`,
4 | './src/**/*.{html,js,jsx}',
5 | './node_modules/tw-elements/dist/js/**/*.js',
6 | './components/**/*.{html,jsx}',
7 |
8 | ],
9 | theme: {
10 | extend: {
11 | gridTemplateRows: {
12 | // Complex site-specific row configuration
13 | main: 'repeat(2, minmax(0, 1fr)) 50px'
14 | },
15 | maxHeight: {
16 | 'chart-container': '95vh',
17 | }
18 | }
19 | },
20 | plugins: [require('tw-elements/dist/plugin')]
21 | };
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Ensure that .d.ts files are created by tsc, but not .js files
4 | "declaration": true,
5 | "emitDeclarationOnly": true,
6 | // Ensure that Babel can safely transpile files in the TypeScript project
7 | "isolatedModules": true,
8 | "esModuleInterop": true,
9 | "jsx": "react",
10 | "target": "es6",
11 | "strict": true,
12 | "lib": ["dom"],
13 | "moduleResolution": "node"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/webpack-stats.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/ad3lie/03280e410826acb39ef923ddbedba37ea2a03fa2/webpack-stats.json
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
3 | // const BundleAnalyzerPlugin =
4 | // require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
5 | const path = require('path');
6 |
7 | module.exports = [{
8 | mode: 'development',
9 | entry: './src/index.jsx',
10 | output: {
11 | path: path.resolve(__dirname, 'dist'),
12 | filename: 'bundle.js'
13 | },
14 | // devtool: "source-map",
15 | resolve: {
16 | extensions: ['.tsx', '.ts', '.js', '.jsx', '.json', '.css', '.scss'],
17 | modules: ['src', 'node_modules'],
18 | fallback: {
19 | fs: false,
20 | path: require.resolve('path-browserify')
21 | }
22 | },
23 | optimization: {
24 | usedExports: true,
25 | },
26 | module: {
27 | rules: [
28 | {
29 | test: /\.jsx?/,
30 | exclude: /node_modules/,
31 | use: {
32 | loader: 'babel-loader',
33 | options: {
34 | presets: ['@babel/preset-env', '@babel/preset-react']
35 | }
36 | }
37 | },
38 | {
39 | test: /.js$/,
40 | exclude: /node_modules/,
41 | use: {
42 | loader: 'babel-loader',
43 | options: {
44 | presets: ['@babel/preset-env', '@babel/preset-react']
45 | }
46 | }
47 | },
48 | {
49 | test: /\.tsx?$/,
50 | exclude: /node_modules/,
51 | use: {
52 | loader: 'babel-loader',
53 | options: {
54 | presets: [
55 | '@babel/preset-env',
56 | '@babel/preset-react',
57 | '@babel/preset-typescript'
58 | ]
59 | }
60 | }
61 | },
62 |
63 | {
64 | test: /\.(png|svg|jpg|jpeg|gif)$/i,
65 | type: 'asset/resource'
66 | },
67 | {
68 | test: /.+\.css$/i,
69 | // exclude: /node_modules/,
70 | use: [
71 | 'style-loader',
72 | 'css-loader',
73 | {
74 | loader: 'postcss-loader',
75 | options: {
76 | postcssOptions: {
77 | plugins: {
78 | tailwindcss: {},
79 | autoprefixer: {}
80 | }
81 | }
82 | }
83 | }
84 | ]
85 | }
86 | ]
87 | },
88 |
89 | plugins: [
90 | new HtmlWebpackPlugin({
91 | template: './src/index.html'
92 | }),
93 | new NodePolyfillPlugin()
94 | ],
95 | devServer: {
96 | static: path.resolve(__dirname, 'dist'),
97 | port: 8080
98 | }
99 | }]
100 |
--------------------------------------------------------------------------------