@@ -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,
|
||||
openDialog,
|
||||
openDialogBootstrap,
|
||||
openDialogMUI,
|
||||
openDialogTailwindCSS,
|
||||
openAboutSidebar,
|
||||
} from './ui';
|
||||
@@ -13,6 +14,7 @@ export {
|
||||
onOpen,
|
||||
openDialog,
|
||||
openDialogBootstrap,
|
||||
openDialogMUI,
|
||||
openDialogTailwindCSS,
|
||||
openAboutSidebar,
|
||||
getSheetsData,
|
||||
|
||||
@@ -3,6 +3,7 @@ export const onOpen = () => {
|
||||
.createMenu('My Sample React Project') // edit me!
|
||||
.addItem('Sheet Editor', 'openDialog')
|
||||
.addItem('Sheet Editor (Bootstrap)', 'openDialogBootstrap')
|
||||
.addItem('Sheet Editor (MUI)', 'openDialogMUI')
|
||||
.addItem('Sheet Editor (Tailwind CSS)', 'openDialogTailwindCSS')
|
||||
.addItem('About me', 'openAboutSidebar');
|
||||
|
||||
@@ -23,6 +24,13 @@ export const openDialogBootstrap = () => {
|
||||
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 = () => {
|
||||
const html = HtmlService.createHtmlOutputFromFile('dialog-demo-tailwindcss')
|
||||
.setWidth(600)
|
||||
|
||||
Reference in New Issue
Block a user