Skip to content

Commit 34aa011

Browse files
authored
Merge pull request #42 from oslabs-beta/css-editor
CSS Editor fully functional
2 parents 30e92c4 + 5b44858 commit 34aa011

File tree

11 files changed

+175
-146
lines changed

11 files changed

+175
-146
lines changed

app/src/components/ContextAPIManager/CreateTab/CreateContainer.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,19 @@ const CreateContainer = () => {
5454

5555
const triggerError = (type: String) => {
5656
setErrorStatus(true);
57-
if (type === 'empty') {
58-
setErrorMsg('Context name cannot be blank.');
59-
} else if (type === 'dupe') {
60-
setErrorMsg('Context name already exists.');
61-
} else if (type === 'letters') {
62-
setErrorMsg('Context name must start with a letter.');
63-
} else if (type === 'symbolsDetected') {
64-
setErrorMsg('Context name must not contain symbols.');
57+
switch (type) {
58+
case 'empty':
59+
setErrorMsg('Context name cannot be blank.');
60+
break;
61+
case 'dupe':
62+
setErrorMsg('Context name already exists.');
63+
break;
64+
case 'letters':
65+
setErrorMsg('Context name must start with a letter.');
66+
break;
67+
case 'symbolsDetected':
68+
setErrorMsg('Context name must not contain symbols.');
69+
break;
6570
}
6671
};
6772

app/src/components/bottom/BottomTabs.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,7 @@ const useStyles = makeStyles((theme) => ({
170170
textTransform: 'initial',
171171
minWidth: 40,
172172
// fontWeight: theme.typography.fontWeightRegular,
173-
marginRight: '16px',
174-
marginLeft: '16px',
173+
margin: '0 16px',
175174

176175
fontFamily: [
177176
'-apple-system',
@@ -198,12 +197,12 @@ const useStyles = makeStyles((theme) => ({
198197
}
199198
},
200199
tabSelected: {},
201-
// typography: {
202-
// padding: theme.spacing(3)
203-
// // },
204-
// padding: {
205-
// padding: `0 ${theme.spacing(2)}`
206-
// },
200+
typography: {
201+
padding: '24px'
202+
},
203+
padding: {
204+
padding: `0 16px`
205+
},
207206
switch: {
208207
marginRight: '10px',
209208
marginTop: '2px'

app/src/components/bottom/StylesEditor.tsx

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useRef, useEffect } from 'react';
1+
import React, { useState, useRef } from 'react';
22
import AceEditor from 'react-ace';
33
import 'ace-builds/src-noconflict/mode-css';
44
import 'ace-builds/src-noconflict/theme-monokai';
@@ -10,71 +10,42 @@ import 'ace-builds/src-noconflict/theme-monokai';
1010
import 'ace-builds/src-min-noconflict/ext-searchbox';
1111
import Fab from '@mui/material/Fab';
1212
import SaveIcon from '@mui/icons-material/Save';
13-
import cssRefresher from '../../helperFunctions/cssRefresh';
14-
15-
//This was being used for the demo
16-
// const serverURL = 'https://reactype-caret.herokuapp.com';
13+
import { updateStylesheet } from '../../redux/reducers/slice/appStateSlice';
14+
import { useDispatch, useSelector } from 'react-redux';
15+
import { RootState } from '../../redux/store';
1716

1817
const StylesEditor: React.FC<{
1918
theme: string | null;
2019
setTheme: any | null;
2120
}> = ({ theme, setTheme }) => {
2221
const wrapper = useRef();
23-
const [css, setCss] = useState();
24-
//now using variable and storing CSS in localStorage to retain CSS upon dismount of the component
25-
let currentCss = localStorage.getItem('css');
26-
27-
//This was being used for the demo
28-
29-
// useEffect(() => {
30-
// loadFile();
31-
// }, []);
32-
33-
// const loadFile = () => {
34-
// const myHeaders = new Headers({
35-
// 'Content-Type': 'text/css',
36-
// Accept: 'text/css',
37-
// });
38-
// fetch(`${serverURL}/demoRender`, {
39-
// headers: myHeaders,
40-
// })
41-
// .then(response => response.text())
42-
// .then((data) => {
43-
// setCss(data);
44-
// });
45-
// }
22+
const stylesheet = useSelector(
23+
(state: RootState) => state.appState.stylesheet
24+
);
25+
//sets state for what text is currently in the csseditor
26+
const [css, setCss] = useState(stylesheet);
4627

47-
// const saveFile = () => {
48-
// fetch(`${serverURL}/user-styles/save`, {
49-
// method: 'POST',
50-
// headers: { 'Content-Type': 'application/json' },
51-
// body: JSON.stringify({ data: css }),
52-
// })
53-
// .then(response => response.text())
54-
// .then((data) => {
55-
// // Removes old link to css and creates a new stylesheet link on demo render
56-
// cssRefresher();
57-
// });
58-
// }
28+
const dispatch = useDispatch();
5929

60-
//refactored this function to store the css on local storage rather than invoke saveFile()
30+
//on save, updates the state based on above hook and rerenders the demo
6131
const saveCss = (e) => {
6232
e.preventDefault();
63-
localStorage.setItem('css', currentCss)
64-
}
33+
dispatch(updateStylesheet(css));
34+
};
6535

36+
//handles changes in the ace editor
6637
const handleChange = (text) => {
67-
currentCss = text;
68-
}
38+
setCss(text);
39+
};
6940

7041
return (
7142
<div
72-
className='text-editor'
43+
className="text-editor"
7344
ref={wrapper}
7445
style={{
7546
height: '100%',
7647
maxWidth: '100%',
77-
justifyContent: 'center',
48+
justifyContent: 'center'
7849
}}
7950
>
8051
<AceEditor
@@ -83,7 +54,7 @@ const StylesEditor: React.FC<{
8354
width="100%"
8455
height="100%"
8556
onChange={handleChange}
86-
value={currentCss}
57+
value={css}
8758
name="Css_div"
8859
fontSize={16}
8960
tabSize={2}
@@ -93,7 +64,12 @@ const StylesEditor: React.FC<{
9364
enableLiveAutocompletion: true
9465
}}
9566
/>
96-
<Fab className='bttn' onClick={saveCss} color="secondary" aria-label="add">
67+
<Fab
68+
className="bttn"
69+
onClick={saveCss}
70+
color="secondary"
71+
aria-label="add"
72+
>
9773
<SaveIcon />
9874
</Fab>
9975
</div>

app/src/components/main/DemoRender.tsx

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { useEffect, useRef } from 'react';
22
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
33
import Box from '@mui/material/Box';
4-
import cssRefresher from '../../helperFunctions/cssRefresh';
54
import { Component } from '../../interfaces/Interfaces';
65
import ReactDOMServer from 'react-dom/server';
76
import { useDispatch, useSelector } from 'react-redux';
@@ -11,11 +10,11 @@ import { RootState } from '../../redux/store';
1110
// DemoRender is the full sandbox demo of our user's custom built React components. DemoRender references the design specifications stored in state to construct
1211
// real react components that utilize hot module reloading to depict the user's prototype application.
1312
const DemoRender = (): JSX.Element => {
14-
const state = useSelector((store:RootState) => store.appState);
15-
const dispatch = useDispatch();
16-
let currentComponent = state.components.find(
17-
(elem: Component) => elem.id === state.canvasFocus.componentId
13+
const state = useSelector((store: RootState) => store.appState);
14+
const stylesheet = useSelector(
15+
(store: RootState) => store.appState.stylesheet
1816
);
17+
const dispatch = useDispatch();
1918

2019
// Create React ref to inject transpiled code in inframe
2120
const iframe = useRef<any>();
@@ -32,7 +31,6 @@ const DemoRender = (): JSX.Element => {
3231
</head>
3332
<body>
3433
<div id="app">
35-
3634
</div>
3735
<script>
3836
window.addEventListener('message', (event) => {
@@ -42,7 +40,6 @@ const DemoRender = (): JSX.Element => {
4240
element.addEventListener('click', (event) => {
4341
event.preventDefault();
4442
window.top.postMessage(event.currentTarget.href, '*');
45-
//document.body.style.backgroundColor = 'orange';
4643
}, true)
4744
});
4845
} catch (err) {
@@ -64,9 +61,7 @@ const DemoRender = (): JSX.Element => {
6461
state.components?.find((el) => {
6562
return el.name.toLowerCase() === component.toLowerCase();
6663
}).id;
67-
componentId &&
68-
dispatch(changeFocus({ componentId, childId: null}));
69-
64+
componentId && dispatch(changeFocus({ componentId, childId: null }));
7065
};
7166

7267
// This function is the heart of DemoRender it will take the array of components stored in state and dynamically construct the desired React component for the live demo
@@ -163,11 +158,15 @@ const DemoRender = (): JSX.Element => {
163158
return componentsToRender;
164159
};
165160

161+
//initializes our 'code' which will be whats actually in the iframe in the demo render
162+
//this will reset every time we make a change
166163
let code = '';
164+
167165
const currComponent = state.components.find(
168166
(element) => element.id === state.canvasFocus.componentId
169167
);
170168

169+
//writes each component to the code
171170
componentBuilder(currComponent.children).forEach((element) => {
172171
try {
173172
code += ReactDOMServer.renderToString(element);
@@ -176,24 +175,26 @@ const DemoRender = (): JSX.Element => {
176175
}
177176
});
178177

179-
// useEffect(() => {
180-
// cssRefresher();
181-
// }, []);
178+
//writes our stylesheet from state to the code
179+
code += `<style>${stylesheet}</style>`;
182180

181+
//adds the code into the iframe
183182
useEffect(() => {
184183
iframe.current.contentWindow.postMessage(code, '*');
185184
}, [code]);
186185

187186
return (
188-
<div id={'renderFocus'} style={demoContainerStyle}>
189-
<iframe
190-
ref={iframe}
191-
sandbox="allow-scripts"
192-
srcDoc={html}
193-
width="100%"
194-
height="100%"
195-
/>
196-
</div>
187+
<>
188+
<div id={'renderFocus'} style={demoContainerStyle}>
189+
<iframe
190+
ref={iframe}
191+
sandbox="allow-scripts"
192+
srcDoc={html}
193+
width="100%"
194+
height="100%"
195+
/>
196+
</div>
197+
</>
197198
);
198199
};
199200

app/src/components/right/ExportButton.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ export default function ExportButton() {
1515
const [modal, setModal] = useState(null);
1616
const state = useSelector((store: RootState) => store.appState);
1717

18-
const genOptions: string[] = [
19-
'Export components'
20-
];
18+
const genOptions: string[] = ['Export components'];
2119

2220
// Closes out the open modal
2321
const closeModal = () => setModal('');
@@ -27,6 +25,7 @@ export default function ExportButton() {
2725
<List className="export-preference">
2826
{genOptions.map((option: string, i: number) => (
2927
<ListItem
28+
id="export-modal"
3029
key={i}
3130
onClick={() => chooseGenOptions()}
3231
style={{
@@ -60,7 +59,7 @@ export default function ExportButton() {
6059
);
6160
};
6261

63-
const exportKeyBind = useCallback(e => {
62+
const exportKeyBind = useCallback((e) => {
6463
//Export Project
6564
(e.key === 'e' && e.metaKey) || (e.key === 'e' && e.ctrlKey)
6665
? showGenerateAppModal()
@@ -75,10 +74,18 @@ export default function ExportButton() {
7574
}, []);
7675
return (
7776
<div>
78-
<button onClick={showGenerateAppModal}>Export Project
79-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" className="bi bi-download" viewBox="0 0 16 16">
80-
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
81-
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
77+
<button onClick={showGenerateAppModal}>
78+
Export Project
79+
<svg
80+
xmlns="http://www.w3.org/2000/svg"
81+
width="16"
82+
height="16"
83+
fill="currentColor"
84+
className="bi bi-download"
85+
viewBox="0 0 16 16"
86+
>
87+
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
88+
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" />
8289
</svg>
8390
</button>
8491
{modal}
@@ -90,7 +97,6 @@ export default function ExportButton() {
9097
//The below code is exclusive to ReacType's Electron App
9198
//If you would like to deploy the app, please comment out the exportButton function above and uncomment the code below
9299

93-
94100
// export default function ExportButton() {
95101
// const [modal, setModal] = useState(null);
96102
// const state = useSelector(store => store.appState)
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// Removes old link to css and creates a new stylesheet link on demo render
22
// this is not currently being used for the website version
33
const cssRefresher = () => {
4-
const oldStylesheet = document.getElementById('Render Stylesheet')
4+
const oldStylesheet = document.getElementById('stylesheet');
55
console.log(oldStylesheet);
66
if (oldStylesheet !== null) oldStylesheet.remove();
7-
const rando = Math.random() * 100000;
8-
const newStylesheet = document.createElement("LINK");
9-
newStylesheet.setAttribute("rel", "stylesheet")
10-
newStylesheet.setAttribute("type", "text/css");
11-
newStylesheet.setAttribute("href", `https://reactype-caret.herokuapp.com/demoRender?${rando}`);
12-
newStylesheet.setAttribute("id", 'Render Stylesheet');
7+
// const rando = Math.random() * 100000;
8+
const newStylesheet = document.createElement('LINK');
9+
newStylesheet.setAttribute('rel', 'stylesheet');
10+
newStylesheet.setAttribute('type', 'text/css');
11+
newStylesheet.setAttribute('href', 'fake.css');
12+
newStylesheet.setAttribute('id', 'stylesheet');
1313
document.getElementById('renderFocus').append(newStylesheet);
14-
console.log(newStylesheet)
15-
}
16-
export default cssRefresher;
14+
console.log(newStylesheet);
15+
};
16+
export default cssRefresher;

app/src/helperFunctions/zipFiles.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ const zipFiles = (state) => {
1414
for (let i in state.components){
1515
componentFolder.file(`${state.components[i].name}.jsx`, state.components[i].code);
1616
}
17-
//writes our css file if we have a css file stored in local storage
18-
if(localStorage.getItem('css')){
19-
reacTypeApp.file('style.css', localStorage.getItem('css'));
20-
}
17+
//writes our css file
18+
reacTypeApp.file('style.css', state.stylesheet);
2119
//zips the file and saves to local machine
2220
zip.generateAsync({type:"blob"})
2321
.then(function(content) {

app/src/interfaces/Interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface State {
1515
nextChildId: number;
1616
HTMLTypes: HTMLType[];
1717
tailwind: boolean;
18+
stylesheet: string
1819
}
1920

2021
export interface ChildElement {

0 commit comments

Comments
 (0)