Generated
+793
-138
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,9 @@
|
|||||||
"npm": ">=6.0.0"
|
"npm": ">=6.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.10.6",
|
||||||
|
"@emotion/styled": "^11.10.6",
|
||||||
|
"@mui/material": "^5.11.11",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.4.0",
|
"react-bootstrap": "^2.4.0",
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import React, { useState, ChangeEvent, FormEvent } from 'react';
|
||||||
|
import { Button, Grid, TextField, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
interface FormInputProps {
|
||||||
|
submitNewSheet: (sheetName: string) => {
|
||||||
|
name: string;
|
||||||
|
index: number;
|
||||||
|
isActive: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const FormInput = ({ submitNewSheet }: FormInputProps) => {
|
||||||
|
const [newSheetName, setNewSheetName] = useState('');
|
||||||
|
|
||||||
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setNewSheetName(event.target.value);
|
||||||
|
|
||||||
|
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (newSheetName.length === 0) return;
|
||||||
|
submitNewSheet(newSheetName);
|
||||||
|
setNewSheetName('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="sheet-name"
|
||||||
|
label="New Sheet Name"
|
||||||
|
variant="outlined"
|
||||||
|
value={newSheetName}
|
||||||
|
onChange={handleChange}
|
||||||
|
name="newSheetName"
|
||||||
|
fullWidth
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={2}
|
||||||
|
container
|
||||||
|
direction="row"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Button variant="contained" type="submit">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Typography variant="caption" gutterBottom>
|
||||||
|
Enter the name for your new sheet.
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
<Typography variant="caption" gutterBottom sx={{ fontStyle: 'italic' }}>
|
||||||
|
This component is written in typescript!
|
||||||
|
</Typography>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormInput;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Button, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
import FormInput from './FormInput';
|
||||||
|
import SheetTable from './SheetTable';
|
||||||
|
|
||||||
|
|
||||||
|
// This is a wrapper for google.script.run that lets us use promises.
|
||||||
|
import { serverFunctions } from '../../utils/serverFunctions';
|
||||||
|
|
||||||
|
const SheetEditor = () => {
|
||||||
|
const [names, setNames] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
serverFunctions.getSheetsData().then(setNames).catch(alert);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const deleteSheet = (sheetIndex) => {
|
||||||
|
serverFunctions.deleteSheet(sheetIndex).then(setNames).catch(alert);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActiveSheet = (sheetName) => {
|
||||||
|
serverFunctions.setActiveSheet(sheetName).then(setNames).catch(alert);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitNewSheet = async (newSheetName) => {
|
||||||
|
try {
|
||||||
|
const response = await serverFunctions.addSheet(newSheetName);
|
||||||
|
setNames(response);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: '3px', overflowX: 'hidden' }}>
|
||||||
|
<Typography variant="h4" gutterBottom>
|
||||||
|
☀️ MUI demo! ☀️
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography variant="body1" gutterBottom sx={{ marginBottom: '30px' }}>
|
||||||
|
This is a sample app that uses the <code>mui</code> library
|
||||||
|
to help us build a simple React app. Enter a name for a new sheet, hit
|
||||||
|
enter and the new sheet will be created. Click the red button next to the sheet name to
|
||||||
|
delete it.
|
||||||
|
</Typography>
|
||||||
|
<FormInput submitNewSheet={submitNewSheet} />
|
||||||
|
|
||||||
|
{names.length > 0 &&
|
||||||
|
<SheetTable rows={names.map((name) => {
|
||||||
|
return {
|
||||||
|
sheetName: name.name,
|
||||||
|
goToButton: <Button
|
||||||
|
variant="outlined"
|
||||||
|
disabled={name?.isActive}
|
||||||
|
onClick={() => setActiveSheet(name.name)}
|
||||||
|
>Go To Sheet</Button>,
|
||||||
|
deleteButton: <Button
|
||||||
|
variant="contained"
|
||||||
|
color="error"
|
||||||
|
onClick={() => deleteSheet(name.index)}
|
||||||
|
>Delete</Button>
|
||||||
|
}
|
||||||
|
})} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SheetEditor;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
|
||||||
|
|
||||||
|
type sheetRow = {
|
||||||
|
sheetName: string;
|
||||||
|
goToButton: React.ReactNode;
|
||||||
|
deleteButton: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SheetTable({ rows }: { rows: Array<sheetRow> }) {
|
||||||
|
return (
|
||||||
|
<TableContainer>
|
||||||
|
<Table aria-label="simple table">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Sheet Name</TableCell>
|
||||||
|
<TableCell align="center">Go To</TableCell>
|
||||||
|
<TableCell align="center">Delete</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{rows.map((row: sheetRow) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.sheetName}
|
||||||
|
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||||
|
>
|
||||||
|
<TableCell component="th" scope="row">
|
||||||
|
{row.sheetName}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">{row.goToButton}</TableCell>
|
||||||
|
<TableCell align="center">{row.deleteButton}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base target="_top" />
|
||||||
|
<!-- Add any external scripts and stylesheets here -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section id="index">
|
||||||
|
<!-- bundled js and css will get inlined here during build-->
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -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'));
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/* needed to make consistent test snapshots across OSs */
|
||||||
|
body {
|
||||||
|
font-family: Arial !important;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
onOpen,
|
onOpen,
|
||||||
openDialog,
|
openDialog,
|
||||||
openDialogBootstrap,
|
openDialogBootstrap,
|
||||||
|
openDialogMUI,
|
||||||
openDialogTailwindCSS,
|
openDialogTailwindCSS,
|
||||||
openAboutSidebar,
|
openAboutSidebar,
|
||||||
} from './ui';
|
} from './ui';
|
||||||
@@ -13,6 +14,7 @@ export {
|
|||||||
onOpen,
|
onOpen,
|
||||||
openDialog,
|
openDialog,
|
||||||
openDialogBootstrap,
|
openDialogBootstrap,
|
||||||
|
openDialogMUI,
|
||||||
openDialogTailwindCSS,
|
openDialogTailwindCSS,
|
||||||
openAboutSidebar,
|
openAboutSidebar,
|
||||||
getSheetsData,
|
getSheetsData,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export const onOpen = () => {
|
|||||||
.createMenu('My Sample React Project') // edit me!
|
.createMenu('My Sample React Project') // edit me!
|
||||||
.addItem('Sheet Editor', 'openDialog')
|
.addItem('Sheet Editor', 'openDialog')
|
||||||
.addItem('Sheet Editor (Bootstrap)', 'openDialogBootstrap')
|
.addItem('Sheet Editor (Bootstrap)', 'openDialogBootstrap')
|
||||||
|
.addItem('Sheet Editor (MUI)', 'openDialogMUI')
|
||||||
.addItem('Sheet Editor (Tailwind CSS)', 'openDialogTailwindCSS')
|
.addItem('Sheet Editor (Tailwind CSS)', 'openDialogTailwindCSS')
|
||||||
.addItem('About me', 'openAboutSidebar');
|
.addItem('About me', 'openAboutSidebar');
|
||||||
|
|
||||||
@@ -23,6 +24,13 @@ export const openDialogBootstrap = () => {
|
|||||||
SpreadsheetApp.getUi().showModalDialog(html, 'Sheet Editor (Bootstrap)');
|
SpreadsheetApp.getUi().showModalDialog(html, 'Sheet Editor (Bootstrap)');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const openDialogMUI = () => {
|
||||||
|
const html = HtmlService.createHtmlOutputFromFile('dialog-demo-mui')
|
||||||
|
.setWidth(600)
|
||||||
|
.setHeight(600);
|
||||||
|
SpreadsheetApp.getUi().showModalDialog(html, 'Sheet Editor (MUI)');
|
||||||
|
};
|
||||||
|
|
||||||
export const openDialogTailwindCSS = () => {
|
export const openDialogTailwindCSS = () => {
|
||||||
const html = HtmlService.createHtmlOutputFromFile('dialog-demo-tailwindcss')
|
const html = HtmlService.createHtmlOutputFromFile('dialog-demo-tailwindcss')
|
||||||
.setWidth(600)
|
.setWidth(600)
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ const clientEntrypoints = [
|
|||||||
filename: 'dialog-demo-bootstrap',
|
filename: 'dialog-demo-bootstrap',
|
||||||
template: './src/client/dialog-demo-bootstrap/index.html',
|
template: './src/client/dialog-demo-bootstrap/index.html',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'CLIENT - Dialog Demo MUI',
|
||||||
|
entry: './src/client/dialog-demo-mui/index.js',
|
||||||
|
filename: 'dialog-demo-mui',
|
||||||
|
template: './src/client/dialog-demo-mui/index.html',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'CLIENT - Dialog Demo Tailwind CSS',
|
name: 'CLIENT - Dialog Demo Tailwind CSS',
|
||||||
entry: './src/client/dialog-demo-tailwindcss/index.js',
|
entry: './src/client/dialog-demo-tailwindcss/index.js',
|
||||||
@@ -209,6 +215,29 @@ const DynamicCdnWebpackPluginConfig = {
|
|||||||
version: packageVersion,
|
version: packageVersion,
|
||||||
url: `https://unpkg.com/react-bootstrap@${packageVersion}/dist/react-bootstrap${packageSuffix}`,
|
url: `https://unpkg.com/react-bootstrap@${packageVersion}/dist/react-bootstrap${packageSuffix}`,
|
||||||
};
|
};
|
||||||
|
case '@mui/material':
|
||||||
|
return {
|
||||||
|
name: packageName,
|
||||||
|
var: 'MaterialUI',
|
||||||
|
version: packageVersion,
|
||||||
|
url: `https://unpkg.com/@mui/material@${packageVersion}/umd/material-ui.${
|
||||||
|
isProd ? 'production.min.js' : 'development.js'
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
case '@emotion/react':
|
||||||
|
return {
|
||||||
|
name: packageName,
|
||||||
|
var: 'emotionReact',
|
||||||
|
version: packageVersion,
|
||||||
|
url: `https://unpkg.com/@emotion/react@${packageVersion}/dist/emotion-react.umd.min.js`,
|
||||||
|
};
|
||||||
|
case '@emotion/styled':
|
||||||
|
return {
|
||||||
|
name: packageName,
|
||||||
|
var: 'emotionStyled',
|
||||||
|
version: packageVersion,
|
||||||
|
url: `https://unpkg.com/@emotion/styled@${packageVersion}/dist/emotion-styled.umd.min.js`,
|
||||||
|
};
|
||||||
// externalize gas-client to keep bundle size even smaller
|
// externalize gas-client to keep bundle size even smaller
|
||||||
case 'gas-client':
|
case 'gas-client':
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user