add Bootstrap working example

• re-organize file structure with one directory per dialog
• add index.html file per dialog
• add bootstrap styling and dynamic cdn loading
• clean up react code and stylesheet

npm setup script update

adds force flag
"setup": "rm -f .clasp.json ..."

Update README.md

Move clasp to devDependencies

update dynamic cdn urls

Add repository field
This commit is contained in:
Elisha Nuchi
2020-06-29 02:08:18 -04:00
parent f4351f1a49
commit f2a259ce66
32 changed files with 845 additions and 399 deletions
@@ -0,0 +1,42 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
const FormInput = ({ submitNewSheet }) => {
const [inputValue, setInputValue] = useState('');
const handleChange = event => setInputValue(event.target.value);
const handleSubmit = event => {
event.preventDefault();
if (inputValue.length === 0) return;
submitNewSheet(inputValue);
setInputValue('');
};
return (
<div className="formBlock">
<form onSubmit={handleSubmit}>
<div>
<span>Add a sheet</span>
</div>
<div>
<input
onChange={handleChange}
value={inputValue}
placeholder="New sheet name"
/>
<button className="submit" type="submit">
Add
</button>
</div>
</form>
</div>
);
};
export default FormInput;
FormInput.propTypes = {
submitNewSheet: PropTypes.func,
};
@@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
const SheetButton = ({ sheetDetails, deleteSheet, setActiveSheet }) => {
const { index, name, isActive } = sheetDetails;
return (
<div className="sheetLine">
<button className="delete" onClick={() => deleteSheet(index)}>
&times;
</button>
<button className="basicButton" onClick={() => setActiveSheet(name)}>
<span className={`sheetNameText ${isActive ? 'active-sheet' : ''}`}>
{name}
</span>
</button>
</div>
);
};
export default SheetButton;
SheetButton.propTypes = {
sheetDetails: PropTypes.shape({
index: PropTypes.number,
name: PropTypes.string,
isActive: PropTypes.bool,
}),
deleteSheet: PropTypes.func,
setActiveSheet: PropTypes.func,
};
@@ -0,0 +1,68 @@
import React, { useState, useEffect } from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import FormInput from './FormInput';
import SheetButton from './SheetButton';
// This is a wrapper for google.script.run that lets us use promises.
import server from '../../utils/server';
const SheetEditor = () => {
const [names, setNames] = useState([]);
useEffect(() => {
// Call a server global function here and handle the response with .then() and .catch()
server
.getSheetsData()
.then(setNames)
.catch(alert);
}, []);
const deleteSheet = sheetIndex => {
server
.deleteSheet(sheetIndex)
.then(setNames)
.catch(alert);
};
const setActiveSheet = sheetName => {
server
.setActiveSheet(sheetName)
.then(setNames)
.catch(alert);
};
// You can also use async/await notation for server calls with our server wrapper.
// (This does the same thing as .then().catch() in the above handlers.)
const submitNewSheet = async newSheetName => {
try {
const response = await server.addSheet(newSheetName);
setNames(response);
} catch (error) {
alert(error);
}
};
return (
<div>
<FormInput submitNewSheet={submitNewSheet} />
<TransitionGroup className="sheet-list">
{names.length > 0 &&
names.map(name => (
<CSSTransition
classNames="sheetNames"
timeout={500}
key={name.name}
>
<SheetButton
sheetDetails={name}
deleteSheet={deleteSheet}
setActiveSheet={setActiveSheet}
/>
</CSSTransition>
))}
</TransitionGroup>
</div>
);
};
export default SheetEditor;
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<base target="_top" />
<!-- Add any external scripts and stylesheets here -->
<link
href="https://fonts.googleapis.com/css2?family=Nunito"
rel="stylesheet"
/>
</head>
<body>
<section id="index">
<!-- bundled js and css will get inlined here during build -->
</section>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import SheetEditor from './components/SheetEditor';
import './styles.css';
ReactDOM.render(<SheetEditor />, document.getElementById('index'));
+137
View File
@@ -0,0 +1,137 @@
input {
font-family: 'Nunito', sans-serif;
width: 150px;
height: 28px;
border: 1px solid #6b6b6b;
border-radius: 4px;
}
input:focus {
outline: 0 none;
}
button {
cursor: pointer;
font-family: 'Nunito', sans-serif;
}
button.submit {
background: rgb(234, 234, 255);
margin: 0px 10px;
padding-top: 6px;
padding-bottom: 6px;
border: 1px solid #6b6b6b;
border-radius: 4px;
transition: background 300ms ease-in;
}
button.submit:hover {
background: rgb(234, 234, 255, 0.4);
}
button.delete {
width: 17px;
-webkit-transition-duration: 0.4s; /* Safari */
background-color: #ffffff;
border: 1px solid #f44336;
color: #676767;
display: inline-block;
font-size: 11px;
height: 17px;
line-height: 15px;
margin-left: 3px;
margin-right: 11px;
padding: 1px;
text-transform: uppercase;
transition-duration: 0.6s;
vertical-align: middle;
}
button.delete:hover {
background-color: #f44336;
color: #ffffff;
}
button.delete:active {
background-color: #f44336;
transform: translateY(1px);
transition-duration: 0.3s;
}
button.delete:focus {
outline: none;
}
.formBlock {
display: -webkit-box;
font-family: 'Nunito', sans-serif;
font-weight: 700;
padding-bottom: 30px;
}
.sheetLine {
padding: 10px 0px;
border-bottom: 1px solid #eaeaea;
}
button.basicButton {
padding: 0;
border: none;
outline: none;
font: inherit;
color: inherit;
background: none;
}
span.sheetNameText {
cursor: pointer;
font-size: 14px;
font-family: 'Nunito', sans-serif;
border-bottom-color: rgba(51, 130, 54, 0);
border-bottom-width: 3px solid none;
border-bottom-style: solid;
transition: border-bottom-color 300ms ease-in;
}
span.sheetNameText:hover {
border-bottom-color: rgba(51, 130, 54, 0.2);
}
span.sheetNameText:active {
border-bottom-color: rgba(51, 130, 54, 0.2);
}
span.sheetNameText.active-sheet {
border-bottom-color: rgb(51, 130, 54);
}
/*
CSSTransitionGroup styling
*/
.sheetNames-enter {
opacity: 0;
}
.sheetNames-enter.sheetNames-enter-active {
opacity: 1;
transition: opacity 300ms ease-in;
}
.sheetNames-exit {
opacity: 1;
}
.sheetNames-exit.sheetNames-exit-active {
opacity: 0;
transition: opacity 300ms ease-in;
}
.sheetNames-appear {
opacity: 0;
}
.sheetNames-appear.sheetNames-appear-active {
opacity: 1;
transition: opacity 0.5ms ease-in;
}