first commit

This commit is contained in:
Elisha Nuchi
2018-09-12 21:13:28 -04:00
parent 0dc06b79b8
commit a18779850f
18 changed files with 10302 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"presets": ["env", "react"]
}
+1
View File
@@ -0,0 +1 @@
dist/main.js
View File
+16
View File
@@ -0,0 +1,16 @@
{
"root": true,
"parser": "babel-eslint",
"extends": ["eslint:recommended", "google", "plugin:prettier/recommended"],
"plugins": ["prettier", "googleappsscript"],
"env": {
"googleappsscript/googleappsscript": true
},
"globals": {
},
"rules": {
"prettier/prettier": "error",
"import/prefer-default-export": 0,
"no-console": 0
}
}
+1
View File
@@ -0,0 +1 @@
node_modules
+4
View File
@@ -0,0 +1,4 @@
{
"printWidth": 120,
"singleQuote": true
}
+9
View File
@@ -0,0 +1,9 @@
{
"timeZone": "America/New_York",
"dependencies": {
},
"oauthScopes": [
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/spreadsheets"
]
}
+9
View File
@@ -0,0 +1,9 @@
{
"timeZone": "America/New_York",
"dependencies": {
},
"oauthScopes": [
"https://www.googleapis.com/auth/script.container.ui",
"https://www.googleapis.com/auth/spreadsheets"
]
}
+181
View File
@@ -0,0 +1,181 @@
function onOpen() {
}
function openDialog() {
}
function getSheetsData() {
}
function addSheet() {
}
function deleteSheet() {
}/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {
// Use ES6/7 code
var onOpen = function onOpen() {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Dialog').addItem('Add sheets', 'openDialog').addToUi();
};
var openDialog = function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('dialog');
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Sheet Editor');
};
var getSheets = function getSheets() {
return SpreadsheetApp.getActive().getSheets();
};
var getActiveSheetName = function getActiveSheetName() {
return SpreadsheetApp.getActive().getSheetName();
};
var getSheetsData = function getSheetsData() {
var activeSheetName = getActiveSheetName();
return getSheets().map(function (sheet, index) {
var sheetName = sheet.getName();
return {
text: sheetName,
sheetIndex: index,
isActive: sheetName === activeSheetName
};
});
};
var addSheet = function addSheet(sheetTitle) {
SpreadsheetApp.getActive().insertSheet(sheetTitle);
return getSheetsData();
};
var deleteSheet = function deleteSheet(sheetIndex) {
var sheets = getSheets();
SpreadsheetApp.getActive().deleteSheet(sheets[sheetIndex]);
return getSheetsData();
};
//Must expose public functions
global.onOpen = onOpen;
global.openDialog = openDialog;
global.getSheetsData = getSheetsData;
global.addSheet = addSheet;
global.deleteSheet = deleteSheet;
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(1)))
/***/ }),
/* 1 */
/***/ (function(module, exports) {
var g;
// This works in non-strict mode
g = (function() {
return this;
})();
try {
// This works if eval is allowed (see CSP)
g = g || Function("return this")() || (1, eval)("this");
} catch (e) {
// This works if the window reference is available
if (typeof window === "object") g = window;
}
// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}
module.exports = g;
/***/ })
/******/ ]);
+36
View File
File diff suppressed because one or more lines are too long
+22
View File
File diff suppressed because one or more lines are too long
+9568
View File
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
{
"name": "react-google-apps-script",
"version": "1.0.0",
"description": "Boilerplate sample project for using React with Google Apps Scipt",
"scripts": {
"test": "test",
"start": "webpack --mode development",
"build": "webpack --mode production",
"deploy": "clasp push"
},
"keywords": [
"react",
"google",
"apps",
"script",
"sheets"
],
"author": "Elisha Nuchi",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"@google/clasp": "^1.5.0",
"@types/google-apps-script": "0.0.26",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.6",
"babel-loader": "^7.1.5",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.22.0",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.2",
"css-loader": "^1.0.0",
"eslint": "^5.3.0",
"eslint-config-airbnb": "^17.0.0",
"eslint-config-airbnb-base": "^13.0.0",
"eslint-config-google": "^0.9.1",
"eslint-config-prettier": "^2.9.0",
"eslint-config-standard": "^11.0.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-googleappsscript": "^1.0.1",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-prettier": "^2.6.2",
"eslint-plugin-promise": "^3.8.0",
"eslint-plugin-react": "^7.10.0",
"eslint-plugin-standard": "^3.1.0",
"gas-lib": "^2.0.2",
"gas-webpack-plugin": "^0.3.0",
"html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.2.0",
"prettier": "^1.13.7",
"react": "^16.4.2",
"react-addons-css-transition-group": "^15.6.2",
"react-dom": "^16.4.2",
"react-transition-group": "^2.4.0",
"style-loader": "^0.22.1",
"uglifyjs-webpack-plugin": "^1.2.7",
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0"
}
}
+52
View File
@@ -0,0 +1,52 @@
// Use ES6/7 code
const onOpen = () => {
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.createMenu('Dialog')
.addItem('Add sheets', 'openDialog')
.addToUi();
}
const openDialog = () => {
var html = HtmlService.createHtmlOutputFromFile('dialog');
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showModalDialog(html, 'Sheet Editor');
}
const getSheets = () => SpreadsheetApp
.getActive()
.getSheets();
const getActiveSheetName = () => SpreadsheetApp
.getActive()
.getSheetName();
const getSheetsData = () => {
let activeSheetName = getActiveSheetName();
return getSheets()
.map((sheet,index) => {
let sheetName = sheet.getName();
return {
text: sheetName,
sheetIndex: index,
isActive: sheetName === activeSheetName
}
});
}
const addSheet = (sheetTitle) => {
SpreadsheetApp.getActive().insertSheet(sheetTitle);
return getSheetsData();
}
const deleteSheet = (sheetIndex) => {
var sheets = getSheets();
SpreadsheetApp.getActive().deleteSheet(sheets[sheetIndex]);
return getSheetsData();
}
//Must expose public functions
global.onOpen = onOpen;
global.openDialog = openDialog;
global.getSheetsData = getSheetsData;
global.addSheet = addSheet;
global.deleteSheet = deleteSheet;
+15
View File
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<!-- remove/comment out these two script tags and uncomment the react imports from sidebar.html if you don't want to load react from a cdn -->
<!-- <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> -->
<section id="index"></section>
</body>
</html>
+121
View File
@@ -0,0 +1,121 @@
import React from "react";
import ReactDOM from "react-dom";
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import css from "./styles.css";
class FormInput extends React.Component {
constructor(props) {
super(props);
this.state = {text: ""};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (!this.state.text.length) return;
this.props.newSheetFormHandler(e, this.state.text)
this.setState(prevState => ({
text: ""
}));
}
render() {
return (
<div className="formBlock">
<span>Add a sheet: </span>
<form onSubmit={(e) => this.handleSubmit(e)}>
<input
onChange={this.handleChange}
value={this.state.text}
/>
</form>
</div>
);
}
}
class SheetEditor extends React.Component {
constructor(props) {
super(props);
this.state = {names: []};
this.deleteButtonHandler = this.deleteButtonHandler.bind(this);
this.newSheetFormHandler = this.newSheetFormHandler.bind(this);
}
componentDidMount() {
google.script.run
.withSuccessHandler((data) => this.setState({names: data}))
.withFailureHandler((error) => alert(error))
.getSheetsData()
}
deleteButtonHandler(e, sheetIndex) {
return google.script.run
.withSuccessHandler((data) => this.setState({names: data}))
.withFailureHandler((error) => alert(error))
.deleteSheet(sheetIndex)
}
newSheetFormHandler(e, newSheetTitle) {
google.script.run
.withSuccessHandler((data) => this.setState({names: data}))
.withFailureHandler((error) => alert(error))
.addSheet(newSheetTitle);
}
render() {
let names = this.state.names
return (
<div>
<FormInput newSheetFormHandler={this.newSheetFormHandler}/>
<ReactCSSTransitionGroup
transitionName="sheetNames"
transitionAppear={true}
transitionEnterTimeout={100}
transitionLeaveTimeout={100}>
{
names.length ?
names.map((name) => {
return (
<SheetButton name={name} deleteButtonHandler={this.deleteButtonHandler}/>
)
})
: null
}
</ReactCSSTransitionGroup>
</div>
);
}
}
class SheetButton extends React.Component {
render() {
let sheetIndex = this.props.name.sheetIndex;
let sheetName = this.props.name.text;
let isActiveSheet = this.props.name.isActive;
return (
<div className="sheetLine">
<button
onClick={(e) => this.props.deleteButtonHandler(e,sheetIndex)}
>
X
</button>
<span className={"sheetNameText " + (isActiveSheet ? "active-sheet" : "")}>
{sheetName}
</span>
</div>
)
}
}
ReactDOM.render(
<SheetEditor />,
document.getElementById("index")
);
+93
View File
@@ -0,0 +1,93 @@
@import url('https://fonts.googleapis.com/css?family=Lora|Mukta');
input {
width: 130px;
margin-left: 8px;
}
button {
-webkit-transition-duration: 0.4s; /* Safari */
background-color: #f44336;
border: 1px solid #f44336;
color: white;
display: inline-block;
font-family: 'Mukta', sans-serif;
font-size: 11px;
height: 20px;
line-height: 15px;
margin-left: 3px;
margin-right: 11px;
padding: 3px;
text-transform: uppercase;
transition-duration: 0.6s;
vertical-align: middle;
width: 20px;
}
button:hover {
background-color: white;
color: black;
}
button:active {
background-color: #fafafa;
transform: translateY(1px);
transition-duration: 0.3s;
}
button:focus {
outline: none;
}
.formBlock {
display: -webkit-box;
font-family: 'Lora', serif;
font-weight: 700;
}
.sheetLine {
line-height: 3;
height: 30px;
}
.sheetNameText {
font-size: 14px;
font-family: 'Lora', serif;
}
.sheetNameText.active-sheet {
border-bottom: 3px solid #338236;
}
/*
ReactCSSTransitionGroup styling
*/
.sheetNames-enter {
opacity: 0.01;
}
.sheetNames-enter.sheetNames-enter-active {
opacity: 1;
transition: opacity 800ms ease-in;
}
.sheetNames-leave {
opacity: 1;
}
.sheetNames-leave.sheetNames-leave-active {
opacity: 0.01;
transition: opacity 100ms ease-in;
}
.sheetNames-appear {
opacity: 0.01;
}
.sheetNames-appear.sheetNames-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
+103
View File
@@ -0,0 +1,103 @@
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const GasPlugin = require('gas-webpack-plugin');
// const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const destination = 'dist';
const htmlPlugin = new HtmlWebpackPlugin({
template: "./src/dialog.html",
filename: "./dialog.html",
inlineSource: '.(js|css)$' // embed all javascript and css inline
});
const htmlWebpackInlineSourcePlugin = new HtmlWebpackInlineSourcePlugin();
const config = {
optimization: {
// Set to true to minimize code (e.g. for production).
minimize: true
},
module: {},
};
const appsscriptConfig = {
name: "appsscript copy json",
entry: "./appsscript.json",
plugins: [
new CleanWebpackPlugin([destination]),
new CopyWebpackPlugin([
{
from: './appsscript.json'
// to: path.resolve(__dirname, destination)
}
])
]
};
const clientConfig = Object.assign({}, config, {
name: "CLIENT",
entry: "./src/index.jsx",
output: {
path: path.resolve(__dirname, destination),
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
],
},
plugins: [
htmlPlugin,
new HtmlWebpackInlineSourcePlugin()
]
});
const serverConfig = Object.assign({}, config, {
name: "SERVER",
entry: "./src/code.gs",
output: {
filename: 'code.gs',
path: path.resolve(__dirname, destination),
},
module: {
rules: [
{
test: /\.gs$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
],
},
optimization: {
//don't minimize .gs server code
minimize: false
},
plugins: [
new GasPlugin()
]
});
// Return Array of Configurations
module.exports = [
// initialConfig,
appsscriptConfig,
clientConfig,
serverConfig,
];