├── stats.json ├── webpack-stats.json ├── .eslintignore ├── .babelrc ├── logo.png ├── export.gif ├── src ├── topy.png ├── dashboard.png ├── babel.config.js ├── babel-plugin-macros.config.js ├── Archives │ ├── utilities │ │ ├── Archive │ │ │ ├── useAccessor.jsx │ │ │ ├── useUniqueId.js │ │ │ ├── accessorPropsType.jsx │ │ │ ├── dimensionsPropsType.jsx │ │ │ ├── Chart.jsx │ │ │ ├── combineChartDimensions.jsx │ │ │ ├── Bars.jsx │ │ │ ├── createFiles.js │ │ │ ├── Chart_comments.jsx │ │ │ ├── utils.js │ │ │ ├── Axis.jsx │ │ │ ├── AxisVertical.js │ │ │ ├── AxisHorizontal.js │ │ │ ├── CodePreview.js │ │ │ ├── useChartDimensions_comments.jsx │ │ │ ├── exportButton.js │ │ │ └── Axis_comments.jsx │ │ └── exportProject.js │ ├── charts │ │ ├── LineChartCodePreview.jsx │ │ ├── Histogram │ │ │ ├── HistogramCodePreview.jsx │ │ │ └── Histogram.jsx │ │ ├── Scatterplot │ │ │ └── ScatterPlotCodePreview.jsx │ │ ├── BarChart │ │ │ ├── BarChart.jsx │ │ │ └── BarChartCodePreview.jsx │ │ └── LineChartForm.jsx │ └── reducers │ │ ├── xKeySlice.js │ │ ├── yKeySlice.js │ │ ├── nameSlice.js │ │ ├── barPaddingSlice.js │ │ ├── thresholdsSlice.js │ │ ├── xAxisLabelSlice.js │ │ ├── yAxisLabelSlice.js │ │ ├── radiusSlice.js │ │ ├── widthSlice.js │ │ ├── heightSlice.js │ │ └── dataSlice.js ├── components │ ├── HelloWorldcopy.js │ ├── pages │ │ ├── Homepage.jsx │ │ ├── TheCarousel.jsx │ │ ├── chart3.svg │ │ ├── chart1.svg │ │ ├── chart4.svg │ │ ├── chart5.svg │ │ └── ChartCards.jsx │ ├── ChartComponents │ │ ├── JSX │ │ │ ├── Arc.jsx │ │ │ ├── Gradient.jsx │ │ │ ├── Rectangle.jsx │ │ │ ├── Line.jsx │ │ │ ├── Circles.jsx │ │ │ ├── Chart.jsx │ │ │ ├── ErrorBoundary.jsx │ │ │ ├── Bars.jsx │ │ │ ├── ExportDataButton.jsx │ │ │ ├── ExportCodeButton.jsx │ │ │ ├── Chart.css │ │ │ ├── ChartWithDimensions.jsx │ │ │ ├── Pie.jsx │ │ │ ├── Axis.jsx │ │ │ ├── CodeRender.jsx │ │ │ ├── Container.jsx │ │ │ └── Form.jsx │ │ ├── Chart.css │ │ └── chartstyles.css │ ├── NavBar.tsx │ └── Charts │ │ ├── PieChart │ │ └── JSX │ │ │ └── PieChart.jsx │ │ ├── LineChart │ │ └── JSX │ │ │ └── LineChart.jsx │ │ ├── ScatterPlot │ │ └── JSX │ │ │ └── ScatterPlot.jsx │ │ ├── Histogram │ │ └── JSX │ │ │ └── Histogram.jsx │ │ └── BarChart │ │ └── JSX │ │ └── BarChart.jsx ├── AppRoutes.jsx ├── utils │ ├── jsonFruits │ ├── dummyTimelineData.js │ ├── dummyfruitsdata.js │ ├── hooks.js │ ├── jsonpenguins │ ├── ExportData.js │ ├── observable.js │ ├── dummypenguinsdata.js │ ├── handlers.js │ ├── utils.js │ ├── utils.ts │ ├── jsonLine │ ├── CodePreview.js │ └── parseData.js ├── app │ ├── store.js │ ├── dictionary.js │ └── preloadedState.js ├── Dropdown │ ├── DropdownFunctionality.jsx │ ├── Menu.Item.jsx │ ├── MenuButton.jsx │ └── Dropdown.jsx ├── index.jsx ├── AllRoutes.jsx ├── index.html ├── mockup.html ├── features │ └── chart │ │ ├── chartsSlice.js │ │ └── propsSlice.js └── styles.css ├── axes-size.gif ├── choose-chart.gif ├── penguin-icon.png ├── penguin-icon-old.png ├── select-properties.gif ├── .prettierrc ├── .gitignore ├── npm_package_entry.js ├── tsconfig.json ├── tailwind.config.js ├── .eslintrc ├── LICENSE ├── electron-scripts └── preload.js ├── main.js ├── webpack.config.js ├── README.md └── package.json /stats.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webpack-stats.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src/* 3 | build/* 4 | 5 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["syntax-dynamic-import"] 3 | } 4 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/logo.png -------------------------------------------------------------------------------- /export.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/export.gif -------------------------------------------------------------------------------- /src/topy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/src/topy.png -------------------------------------------------------------------------------- /axes-size.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/axes-size.gif -------------------------------------------------------------------------------- /choose-chart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/choose-chart.gif -------------------------------------------------------------------------------- /penguin-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/penguin-icon.png -------------------------------------------------------------------------------- /src/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/src/dashboard.png -------------------------------------------------------------------------------- /penguin-icon-old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/penguin-icon-old.png -------------------------------------------------------------------------------- /select-properties.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/ad3lie/HEAD/select-properties.gif -------------------------------------------------------------------------------- /src/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | return { 3 | plugins: ['macros'], 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/babel-plugin-macros.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'fontawesome-svg-core': { 3 | 'license': 'free' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "none", 4 | "singleQuote": true, 5 | "printWidth": 80 6 | } -------------------------------------------------------------------------------- /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/useUniqueId.js: -------------------------------------------------------------------------------- 1 | let lastId = 0 2 | export const useUniqueId = (prefix="") => { 3 | lastId++ 4 | return [prefix, lastId].join("-") 5 | } -------------------------------------------------------------------------------- /src/components/HelloWorldcopy.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const HelloWorld = () => { 4 | return ( 5 |
)
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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------