diff --git a/README.md b/README.md index 7f46362..1271e7c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +Usage: +``` +cd ~/.meteor-kitchen +~/.meteor-kitchen $ mv templates templates-old +~/.meteor-kitchen $ git clone https://github.com/ljack/kitchen-templates.git templates +``` + Kitchen Templates ================= diff --git a/react/code/collection_shared_schema.js b/react/code/collection_shared_schema.js new file mode 100644 index 0000000..0450d11 --- /dev/null +++ b/react/code/collection_shared_schema.js @@ -0,0 +1,10 @@ +Schemas = this.Schemas || {}; + + +Schemas.COLLECTION_VAR_string = SIMPLE_SCHEMA; +Schemas.COLLECTION_VAR = new SimpleSchema(SIMPLE_SCHEMA); + +/* Schemas.COLLECTION_VAR = SIMPLE_SCHEMA; +*/ +// for now dont attach the schema +//COLLECTION_VAR.attachSchema(Schemas.COLLECTION_VAR); diff --git a/react/example-genereated-files/client.js b/react/example-genereated-files/client.js new file mode 100644 index 0000000..a23a624 --- /dev/null +++ b/react/example-genereated-files/client.js @@ -0,0 +1,20 @@ +import {Users} from "meteor-user-roles"; +import {Apimoons} from "/lib/collections/apimoons.js"; + +Meteor.startup(function() { + + console.log("Client startup running at "+new Date().toISOString()); + // read environment variables from Meteor.settings + if(Meteor.settings && Meteor.settings.env && _.isObject(Meteor.settings.env)) { + for(var variableName in Meteor.settings.env) { + process.env[variableName] = Meteor.settings.env[variableName]; + } + } + +// A hack to create Collections global which can be accessed on the browser easily +if (Meteor.isDevelopment) { + Collections = require('/lib/collections/apimoons.js'); +} + + +}); diff --git a/react/example-genereated-files/columns-apimoon.jsx b/react/example-genereated-files/columns-apimoon.jsx new file mode 100644 index 0000000..e2d7386 --- /dev/null +++ b/react/example-genereated-files/columns-apimoon.jsx @@ -0,0 +1,202 @@ +import React from "react"; +import { + Apimoons +} +from "/lib/collections/apimoons.js"; +import FormSchema from '/lib/collections/schema-apimoon.js'; + +// this file should be generated from the application JSON + +// used by the the Griddle table +const ApimoonColumns = ["name", "left", "middle", "right", "rowButtons"]; + +// used with ColumnMeta by the Griddle Table to format certain columns +// used to display object.name name part of nested object. +const DotComponent = React.createClass({ + render: function() { + //console.log("DotComponent: render this=", this); + return ( + + {this.props.data.name} + + ); + } +}); + +// Component which groups row buttons +const ButtonsComponent = React.createClass({ + render: function() { + console.log("ButtonsComponent: render this=", this); + const paddingStyle = { + leftPadding: "5px" + }; + + return ( +
+ ); + } +}); + +// row button to delete the selected row +const DeleteComponent = React.createClass({ + + delete(evt) { + console.log("DeleteComponent, evt=", evt); + evt.preventDefault(); + evt.stopPropagation(); + var id = this.props.rowData._id; + console.log("DeleteComponent, deleting with id=", id); + // eh, confirm here ? ;) + let res = confirm("Really delete"); + if (res == true) { + DeleteObject(id); + } + else { + // not deleted, huh + } + return false; + }, + render: function() { + console.log("DeleteComponent: render this=", this); + return ( + + ); + } +}); + +// Row button to "edit" the current row +const EditComponent = React.createClass({ + edit(evt) { + // console.log("EditComponent, evt=", evt); + // evt.preventDefault(); + // evt.stopPropagation(); + // var id = this.props.rowData._id; + // const rowData = this.props.rowData; + // alert("Editing " + JSON.stringify((rowData))); + // return false; + }, + render: function() { + console.log("EditComponent: render this=", this); + return ( + + ); + } +}); + +// + + +const ListComponent = React.createClass({ + render: function() { + const value = this.props.data; + return ( + + { value.toString() } + + ); + } +}); + +// now register the DotComponent as customComponent for the specified fields +const ColumnMeta = [{ + "columnName": "left", + "customComponent": DotComponent + }, { + "columnName": "middle", + "customComponent": DotComponent + }, { + "columnName": "right", + "customComponent": DotComponent + }, + + { + + "columnName": "rowButtons", + "customComponent": ButtonsComponent, + "displayName": "" + } + +]; + +// Used by tcomb-form to specify certain things about how the forms looks like +// https://github.com/gcanti/tcomb-form/blob/master/GUIDE.md#list-with-dynamic-items-different-structs-based-on-selected-value +const FormOptions = { + fields: { + _id: { + type: "static", + label: "ID" + }, + left: { + fields: { + code: { + type: "textarea" + } + } + }, + middle: { + fields: { + code: { + type: "textarea" + } + } + }, + right: { + fields: { + code: { + type: "textarea" + } + } + }, + name: { + type: "text" + } + } +}; + +function DeleteObject(id) { + + console.log("Deleting with id=", id); + let collection = Apimoons; + let selector = { + _id: id + }; + collection.remove(selector); +} + + +// this is pretty nasty, and totally different for meteor 1.3 with the client stubs and stuff.. +function SaveCollection(document) { + + document = JSON.parse(JSON.stringify(document)); + console.log("Saving values=", document); + let collection = Apimoons; + let selector = { + _id: document._id + }; + + + + if (collection.findOne(selector) != null) { + delete document._id; + collection.update(selector, { + $set: document + }); + } + else { + delete document._id; + collection.insert(document); + } + +} + + +// These are needed by the data_view.jsx +export { + ApimoonColumns as TableColumns, ColumnMeta as ColumnMeta, FormOptions as FormOptions, + SaveCollection, DeleteObject +}; diff --git a/react/example-genereated-files/columns-todo.jsx b/react/example-genereated-files/columns-todo.jsx new file mode 100644 index 0000000..3350b2a --- /dev/null +++ b/react/example-genereated-files/columns-todo.jsx @@ -0,0 +1,219 @@ +import React from "react"; +import { + Todos as MyCollection +} +from "/lib/collections/todos.js"; +import FormSchema from '/lib/collections/schema-todo.js'; + +// this file should be generated from the application JSON + +// used by the the Griddle table +const TableColumns = ["name", "done", "owner", "sharedTo", "rowButtons"]; + +// used with ColumnMeta by the Griddle Table to format certain columns +// used to display object.name name part of nested object. +const DotComponent = React.createClass({ + render: function() { + //console.log("DotComponent: render this=", this); + return ( + + {this.props.data.name} + + ); + } +}); + +const ListComponent = React.createClass({ + render: function() { + // console.log("ListComponent: render this=", this); + return ( + + {this.props.data.toString()} + + ); + } +}); + +const BooleanComponent = React.createClass({ + getInitialState: function() { + return { + checked: this.props.data || false, + inlineEdit: this.props.inlineEdit || true + }; + }, + render: function() { + if (this.state.inlineEdit) { + return ( + ); + } + else { + return (this.props.data); + } + + }, + handleClick: function(e) { + // e.preventDefault(); + e.stopPropagation(); + const value = e.target.checked; + this.setState({ + checked: value + }); + + const id = this.props.rowData._id; + const fieldName = this.props.metadata.columnName; + + let values = {}; + values[fieldName] = value; + MyCollection.update({ + _id: id + }, { + $set: values + }) + } +}); + + + +const ButtonsComponent = React.createClass({ + render: function() { + // console.log("ButtonsComponent: render this=", this); + const paddingStyle = { + leftPadding: "5px" + }; + + return ( + + ); + } +}); + +const DeleteComponent = React.createClass({ + + delete(evt) { + // console.log("DeleteComponent, evt=", evt); + evt.preventDefault(); + evt.stopPropagation(); + var id = this.props.rowData._id; + console.log("DeleteComponent, deleting with id=", id); + // eh, confirm here ? ;) + let res = confirm("Really delete"); + if (res == true) { + DeleteObject(id); + } + else { + // not deleted, huh + } + return false; + }, + render: function() { + // console.log("DeleteComponent: render this=", this); + return ( + + ); + } +}); +const EditComponent = React.createClass({ + edit(evt) { + // console.log("EditComponent, evt=", evt); + //evt.preventDefault(); + //evt.stopPropagation(); + //var id = this.props.rowData._id; + //const rowData = this.props.rowData; + //alert("Editing " + JSON.stringify((rowData))); + + //return false; + }, + render: function() { + // console.log("EditComponent: render this=", this); + + return ( + + ); + } +}); + +// now register the DotComponent as customComponent for the specified fields +const ColumnMeta = [{ + "columnName": "done", + "customComponent": BooleanComponent + }, { + "columnName": "sharedTo", + "customComponent": ListComponent + }, { + "columnName": "rowButtons", + "customComponent": ButtonsComponent, + "displayName": "" + } + +]; + +// Used by tcomb-form to specify certain things about how the forms looks like +// https://github.com/gcanti/tcomb-form/blob/master/GUIDE.md#list-with-dynamic-items-different-structs-based-on-selected-value +const FormOptions = { + fields: { + _id: { + type: "static", + label: "ID" + }, + name: { + type: "text" + }, + sharedTo: { + type: "text" + } + } +}; + +function DeleteObject(id) { + + console.log("Deleting with id=", id); + let collection = MyCollection; + let selector = { + _id: id + }; + collection.remove(selector); +} + + +// this is pretty nasty, and totally different for meteor 1.3 with the client stubs and stuff.. +function SaveCollection(document) { + document = JSON.parse(JSON.stringify(document)); + console.log("Saving values=", document); + let collection = MyCollection; + let selector = { + _id: document._id + }; + + + + if (collection.findOne(selector) != null) { + delete document._id; + collection.update(selector, { + $set: document + }); + } + else { + delete document._id; + collection.insert(document); + } + +} + + +// These are needed by the data_view.jsx +export { + TableColumns as TableColumns, ColumnMeta as ColumnMeta, FormOptions as FormOptions, + SaveCollection, DeleteObject +}; diff --git a/react/example-genereated-files/griddle.css b/react/example-genereated-files/griddle.css new file mode 100644 index 0000000..f31cdc8 --- /dev/null +++ b/react/example-genereated-files/griddle.css @@ -0,0 +1,85 @@ +.griddle-container{ + border:1px solid #DDD; +} + +.griddle .top-section{ + clear:both; + display:table; + width:100%; +} + +.griddle .griddle-filter{ + float:left; + width:50%; + text-align:left; + color:#222; + min-height:1px; +} + +.griddle .griddle-settings-toggle{ + float:left; + width:50%; + text-align:right; +} + +.griddle .griddle-settings{ + background-color:#FFF; + border:1px solid #DDD; + color:#222; + padding:10px; + margin-bottom:10px; +} + +.griddle .griddle-settings .griddle-columns{ + clear:both; + display:table; + width:100%; + border-bottom:1px solid #EDEDED; + margin-bottom:10px; +} + +.griddle .griddle-settings .griddle-column-selection{ + float:left; + width:20%; +} +.griddle table{ + width:100%;table-layout:fixed; +} + +.griddle th{ + background-color:#EDEDEF; + border:0px; + border-bottom:1px solid #DDD; + color:#222; + padding:5px; +} + +.griddle td{ + padding:5px; +/* edited out by ljack to get table.striped and table.hoover work + background-color:#FFF; + border-top-color:#DDD; + color:#222;*/ +} + +.griddle .footer-container{ + padding:0px; + background-color:#EDEDED; + border:0px; + color:#222; +} + +.griddle .griddle-previous, .griddle .griddle-page, .griddle .griddle-next{ + float:left; + width:33%; + min-height:1px; + margin-top:5px; +} + +.griddle .griddle-page{ + text-align:center; +} + +.griddle .griddle-next{ + text-align:right; +} \ No newline at end of file diff --git a/react/example-genereated-files/left-code.js b/react/example-genereated-files/left-code.js new file mode 100644 index 0000000..db71ac1 --- /dev/null +++ b/react/example-genereated-files/left-code.js @@ -0,0 +1,13 @@ +let customerID = httpRequest.param.id; +var query = ` +query getCustomerInfo { customer( id: "customerID" ) { + id + name + } +} `; + +graphql(schema, query).then(result => { + //send response back to the HTTP caller + // some additional mapping from result -> result can be made here. E.g. when the caller expects certain kind of JSON or non-JSON format result (XML). + httpResponse.send( result ); +}); \ No newline at end of file diff --git a/react/example-genereated-files/middle-code.js b/react/example-genereated-files/middle-code.js new file mode 100644 index 0000000..2f65948 --- /dev/null +++ b/react/example-genereated-files/middle-code.js @@ -0,0 +1,26 @@ +// root-query-type.js +import { + GraphQLObjectType, + GraphQLNonNull, + GraphQLString +} from 'graphql'; + +import customerType from "./customerType"; + +export default new GraphQLObjectType({ + name: "Query", + description:"The root query object", + fields: () => ({ + customer: { + type: customerType, + args: { + id: { + type: new GraphQLNonNull(GraphQLString) + } + }, + resolve: (_, { id }, { rootValue: { ctx: { backend } } }) => ( + backend.getModel("Customer").load(id) + ) + } + }) +}); diff --git a/react/example-genereated-files/right-code.js b/react/example-genereated-files/right-code.js new file mode 100644 index 0000000..5275a4d --- /dev/null +++ b/react/example-genereated-files/right-code.js @@ -0,0 +1,27 @@ +// customerType.js +import { + GraphQLString, + GraphQLArray, + GraphQLObjectType, +} from 'graphql'; + +import invoiceType from "./invoiceType"; + +export default new GraphQLObjectType({ + name: 'Customer', + description: 'A Customer', + fields: () => ({ + id: { + type: GraphQLString, + resolve: it => it.uuid + }, + + name: { type: GraphQLString }, + + invoices: { + type: new GraphQLArray(invoiceType), + resolve: it => it.invoices() + } + }) +}); + diff --git a/react/example-genereated-files/schema-apimoon.js b/react/example-genereated-files/schema-apimoon.js new file mode 100644 index 0000000..4cd3574 --- /dev/null +++ b/react/example-genereated-files/schema-apimoon.js @@ -0,0 +1,30 @@ +import t from 'tcomb-form' + +// this file should be generated from the application JSON + +// see server.js for a example Apimoons.insert clauses which should match with this schema + +// https://github.com/gcanti/tcomb-form/blob/master/GUIDE.md + + + +// Import with import FormSchema from "schema-apimoon.js"; +export default function() { + const ApimoonSchema = t.struct({ + _id: t.maybe(t.String), + name: t.String, // a required string + left: t.struct({ + name: t.String, + code: t.String + }), + middle: t.struct({ + name: t.String, + code: t.String + }), + right: t.struct({ + name: t.String, + code: t.String + }), + }); + return ApimoonSchema; +} \ No newline at end of file diff --git a/react/example-genereated-files/schema-todo.js b/react/example-genereated-files/schema-todo.js new file mode 100644 index 0000000..689cd88 --- /dev/null +++ b/react/example-genereated-files/schema-todo.js @@ -0,0 +1,36 @@ +import t from 'tcomb-form' +import { + Todos as MyCollection +} +from "/lib/collections/todos.js"; +// this file should be generated from the application JSON + +// see server.js for a example Apimoons.insert clauses which should match with this schema + +// https://github.com/gcanti/tcomb-form/blob/master/GUIDE.md + + + +// Import with import FormSchema from "schema-apimoon.js"; +export default function() { + console.log("inside default function this=", this); + var users = Meteor.users.find().fetch(); + var values = {}; + users.map((user) => { + values[user._id] = user.profile.name; + }); + + var Users = t.enums(values, "Users"); + + + var Schema = t.struct({ + _id: t.maybe(t.String), + name: t.String, // a required string + owner: t.maybe(t.String), + sharedTo: t.list(Users), + done: t.Boolean + + }); + + return Schema; +} diff --git a/react/example-genereated-files/server.js b/react/example-genereated-files/server.js new file mode 100644 index 0000000..7656d60 --- /dev/null +++ b/react/example-genereated-files/server.js @@ -0,0 +1,49 @@ +import { + Users +} +from "meteor-user-roles"; +import { Apimoons } from "/lib/collections/apimoons.js"; + +Meteor.publish("users", () => { + console.log("in publish users"); + return Meteor.users.find( {}, {fields: {"profile.name":1} }); +}); + +Meteor.startup(function() { + // read environment variables from Meteor.settings + if (Meteor.settings && Meteor.settings.env && _.isObject(Meteor.settings.env)) { + for (var variableName in Meteor.settings.env) { + process.env[variableName] = Meteor.settings.env[variableName]; + } + } + + + console.log("In server startup.."); + Apimoons.remove({}); + let leftCode = Assets.getText("left-code.js"); + let middleCode = Assets.getText("middle-code.js"); + let rightCode = Assets.getText("right-code.js"); + + for (let i = 0; i < 10; i++) { + + Apimoons.insert({ + name: "API " + i, + "left": { + name: "HTTP REST INPUT", + code: leftCode + + }, + middle: { + name: "GraphQL Schema", + code: middleCode + + }, + right: { + name: "GraphQL Resolver to HTTP REST", + code: rightCode + + }, + }); + } + +}); diff --git a/react/ui/bootstrap3/components/data_view.json b/react/ui/bootstrap3/components/data_view.json new file mode 100644 index 0000000..647bfd9 --- /dev/null +++ b/react/ui/bootstrap3/components/data_view.json @@ -0,0 +1,25 @@ +{ + "packages": { + "meteor": [], + "npm": [ + "griddle-react", + "react-dom", + "tcomb", + "tcomb-form", + "react-modal", + "react-highlight" + + ], + "mrt": [] + }, + "imports": [ + "import Dataview from 'griddle-react';", + "import ReactDOM from 'react-dom';", + "import t from 'tcomb-form'", + "import Modal from 'react-modal';", + "import {convertArrayOfObjects} from '/lib/utils/object_utils.js'", + "import {downloadLocalResource} from '/client/lib/http_utils.js';", + + ], + "copy_files": [] +} diff --git a/react/ui/bootstrap3/components/data_view.jsx b/react/ui/bootstrap3/components/data_view.jsx new file mode 100644 index 0000000..5b2c4fe --- /dev/null +++ b/react/ui/bootstrap3/components/data_view.jsx @@ -0,0 +1,241 @@ +// This is the data_view.jsx +// Notice that in order for this to work the generator must supply 3 imports: FormSchema, TableColumns, ColumnMeta which are collection / page specific +// For example see: +// import FormSchema from '/lib/collections/schema-apimoon.js'; +// import {TableColumns, ColumnMeta} from '/lib/collections/columns-apimoon.jsx'; + +// just finding out which strings are replaced ;) +const collection = COLLECTION_VAR; + +// replace not working for EXPORT_FIELDS.. @perak fixit? ;) +function EXPORT_FUNCTION(a, b) { + var exportFields = [ /*EXPORT_FIELDS*/ ]; + let x=-1; + if(x<000) { + return; + } +} +export const TEMPLATE_NAME = React.createClass({ + + onRowClick(gridRow, event) { + console.log("Clicked on row! gridRow=", gridRow); + console.log(" event=", event); + + + let json = {}; + // this is the hack so we can use this came component to create new items + if (gridRow && gridRow.props && gridRow.props.data) { + json = gridRow.props.data; + } + else { + // yes, a hack, lazy hack. + event = gridRow; + gridRow = null; + } + event.preventDefault(); + console.log("Editing ", json); + + + const EditJSONForm = React.createClass({ + getInitialState: function() { + return { + modalIsOpen: true + }; + }, + openModal: function() { + this.setState({ + modalIsOpen: true + }); + }, + + closeModal: function(evt) { + if (evt) + evt.preventDefault(); + console.log("Closing modal, this=", this); + this.setState({ + modalIsOpen: false + }); + + }, + onSubmit(evt) { + console.log("onSubmit evt=", evt); + console.log("onSubmit this=", this); + evt.preventDefault(); + const value = this.refs.form.getValue(); + + console.log("onSubmit value=", value); + if (value) { + SaveCollection(value); + this.closeModal(); + } + else { + console.log("Validation error on form:"); + console.log(this.refs.form); + } + return; + }, + deleteObject(evt) { + evt.preventDefault(); + console.log("deleteObject, evt=", evt); + // note that this doesn't allow delete if validation is failing, fixing this later ;) + const value = this.refs.form.getValue(); + if (value) { + let id = value._id; + DeleteObject(id); + this.closeModal(); + } + else { + console.log("Unable to delete.. maybe validation is failing ;)"); + } + + }, + + render() { + // get FormSchema for current page + console.log("json=",json); + const fs = FormSchema(); + // console.log( "fs=",fs); + return (