mirror of
https://github.com/vale981/Vulcan
synced 2025-03-05 09:31:43 -05:00
include forms package in repo; add cache for FR-SSR; fix modal window callback issue; fix posts.single publication and subscription bug
This commit is contained in:
parent
40d3a737bf
commit
b23b7eea73
19 changed files with 321 additions and 30 deletions
|
@ -23,6 +23,7 @@ nova:getting-started
|
|||
nova:categories
|
||||
nova:share
|
||||
nova:voting
|
||||
nova:forms
|
||||
nova:embedly
|
||||
nova:base-components
|
||||
nova:base-styles
|
||||
|
|
|
@ -88,6 +88,7 @@ nova:core@0.25.7
|
|||
nova:email@0.25.7
|
||||
nova:embedly@0.25.7
|
||||
nova:events@0.25.7
|
||||
nova:forms@0.25.7
|
||||
nova:getting-started@0.25.7
|
||||
nova:i18n@0.25.7
|
||||
nova:lib@0.25.7
|
||||
|
@ -136,6 +137,7 @@ tap:i18n@1.7.0
|
|||
templating@1.1.7
|
||||
templating-tools@1.0.2
|
||||
tmeasday:check-npm-versions@0.1.1
|
||||
tmeasday:healthcheck-handler@0.0.2
|
||||
tmeasday:publish-counts@0.7.3
|
||||
tracker@1.0.11
|
||||
twitter@1.1.7
|
||||
|
@ -143,7 +145,6 @@ ui@1.0.9
|
|||
underscore@1.0.6
|
||||
url@1.0.7
|
||||
utilities:avatar@0.9.2
|
||||
utilities:react-form-containers@0.1.7
|
||||
utilities:react-list-container@0.1.6
|
||||
utilities:smart-methods@0.1.2
|
||||
utilities:smart-publications@0.1.4
|
||||
|
|
|
@ -30,7 +30,6 @@ const CategoriesList = ({categories}, context) => {
|
|||
|
||||
};
|
||||
|
||||
|
||||
CategoriesList.contextTypes = {
|
||||
currentRoute: React.PropTypes.object
|
||||
};
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import Router from '../router.js'
|
||||
|
||||
import Core from "meteor/nova:core";
|
||||
const Messages = Core.Messages;
|
||||
const ModalTrigger = Core.ModalTrigger;
|
||||
|
||||
import ReactForms from "meteor/utilities:react-form-containers";
|
||||
import ReactForms from "meteor/nova:forms";
|
||||
const NewDocument = ReactForms.NewDocument;
|
||||
|
||||
const NewPostButton = (props, context) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import ReactForms from "meteor/utilities:react-form-containers";
|
||||
import ReactForms from "meteor/nova:forms";
|
||||
const EditDocument = ReactForms.EditDocument;
|
||||
|
||||
import Core from "meteor/nova:core";
|
||||
|
|
|
@ -47,7 +47,7 @@ Router.route('/posts/:_id/:slug?', {
|
|||
<DocumentContainer
|
||||
collection={Posts}
|
||||
publication="posts.single"
|
||||
selector={params}
|
||||
selector={{_id: params._id}}
|
||||
terms={params}
|
||||
joins={Posts.getJoins()}
|
||||
component={PostPage}
|
||||
|
|
|
@ -27,8 +27,7 @@ Package.onUse(function (api) {
|
|||
// 'studiointeract:react-accounts-ui-basic@1.0.1',
|
||||
'studiointeract:react-accounts-ui@1.0.7',
|
||||
'dburles:spacebars-tohtml@1.0.1',
|
||||
'utilities:react-list-container',
|
||||
'utilities:react-form-containers'
|
||||
'utilities:react-list-container'
|
||||
]);
|
||||
|
||||
api.addFiles([
|
||||
|
|
|
@ -38,27 +38,14 @@ class ModalTrigger extends Component {
|
|||
this.setState({modalIsOpen: false});
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
const component = this;
|
||||
return {
|
||||
closeCallback: component.closeModal
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
|
||||
// see http://stackoverflow.com/a/32371612/649299
|
||||
const childrenWithProps = React.Children.map(this.props.children, (child) => {
|
||||
|
||||
// if child component already has a successCallback, create new callback
|
||||
// that both calls original callback and also closes modal
|
||||
let successCallback;
|
||||
if (child.props.successCallback) {
|
||||
successCallback = (document) => {
|
||||
child.props.successCallback(document);
|
||||
this.closeModal();
|
||||
}
|
||||
} else {
|
||||
successCallback = this.closeModal;
|
||||
}
|
||||
|
||||
return React.cloneElement(child, { successCallback: successCallback });
|
||||
|
||||
});
|
||||
|
||||
const triggerComponent = React.cloneElement(this.props.component, { onClick: this.openModal });
|
||||
|
||||
|
@ -70,7 +57,7 @@ class ModalTrigger extends Component {
|
|||
onRequestClose={this.closeModal}
|
||||
style={customStyles}
|
||||
>
|
||||
{childrenWithProps}
|
||||
{this.props.children}
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
|
@ -81,5 +68,9 @@ ModalTrigger.propTypes = {
|
|||
component: React.PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
ModalTrigger.childContextTypes = {
|
||||
closeCallback: React.PropTypes.func
|
||||
}
|
||||
|
||||
module.exports = ModalTrigger;
|
||||
export default ModalTrigger;
|
|
@ -24,6 +24,12 @@ FlowRouter.removeFromQueryArray = function (key, value) {
|
|||
FlowRouter.setQueryParams(params);
|
||||
}
|
||||
|
||||
if(Meteor.isServer) {
|
||||
var timeInMillis = 1000 * 10; // 10 secs
|
||||
FlowRouter.setPageCacheTimeout(timeInMillis);
|
||||
FlowRouter.setDeferScriptLoading(true);
|
||||
}
|
||||
|
||||
// FlowRouter.notFound = {
|
||||
// action: function() {
|
||||
// if (Meteor.isClient) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import NoSSR from 'react-no-ssr';
|
|||
|
||||
import Core from 'meteor/nova:core';
|
||||
import SmartContainers from "meteor/utilities:react-list-container";
|
||||
import FormContainers from "meteor/utilities:react-form-containers";
|
||||
import FormContainers from "meteor/nova:forms";
|
||||
|
||||
const ModalTrigger = Core.ModalTrigger;
|
||||
const NewDocument = FormContainers.NewDocument;
|
||||
|
|
24
packages/nova-forms/README.md
Normal file
24
packages/nova-forms/README.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# React Forms
|
||||
|
||||
This package provides two components (`NewDocument` and `EditDocument`) that work with the schema extension defined in the [smart-methods](https://github.com/meteor-utilities/smart-methods) package to let you easily generate new document and edit document forms.
|
||||
|
||||
### Install
|
||||
|
||||
`meteor add utilities:react-form-containers`
|
||||
|
||||
### `NewDocument`
|
||||
|
||||
This component takes the following properties:
|
||||
|
||||
- `collection`: the collection in which to insert the new document.
|
||||
- `currentUser`: the current user.
|
||||
- `errorCallback`: a function to call on error.
|
||||
- `successCallback`: a function to call on success.
|
||||
- `methodName`: the name of the method to submit the form to.
|
||||
- `labelFunction`: a function that will be called on each field's name to get the label (for example, an internationalization function).
|
||||
|
||||
### `EditDocument`
|
||||
|
||||
This component takes the same properties as `NewDocument`, plus:
|
||||
|
||||
- `document`: the document being edited.
|
75
packages/nova-forms/lib/EditDocument.jsx
Normal file
75
packages/nova-forms/lib/EditDocument.jsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import Formsy from 'formsy-react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
|
||||
import SmartForms from "./smart-forms.jsx";
|
||||
import Utils from './utils.js';
|
||||
|
||||
const EditDocument = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
document: React.PropTypes.object.isRequired,
|
||||
collection: React.PropTypes.object.isRequired,
|
||||
currentUser: React.PropTypes.object,
|
||||
successCallback: React.PropTypes.func,
|
||||
errorCallback: React.PropTypes.func,
|
||||
methodName: React.PropTypes.string,
|
||||
labelFunction: React.PropTypes.func
|
||||
},
|
||||
|
||||
submitForm(data) {
|
||||
|
||||
console.log(data)
|
||||
|
||||
const document = this.props.document;
|
||||
const modifier = {$set: _.compactObject(Utils.flatten(data))};
|
||||
const collection = this.props.collection;
|
||||
const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit';
|
||||
|
||||
console.log(modifier)
|
||||
|
||||
Meteor.call(methodName, document._id, modifier, (error, document) => {
|
||||
if (error) {
|
||||
console.log(error)
|
||||
if (this.props.errorCallback) {
|
||||
this.props.errorCallback(document);
|
||||
}
|
||||
} else {
|
||||
if (this.props.successCallback) {
|
||||
this.props.successCallback(document);
|
||||
}
|
||||
if (this.context.closeCallback) {
|
||||
this.context.closeCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
const document = this.props.document;
|
||||
const collection = this.props.collection;
|
||||
const fields = collection.getInsertableFields(this.props.currentUser);
|
||||
|
||||
const style = {
|
||||
maxWidth: "800px",
|
||||
width: "100%"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="edit-document" style={style}>
|
||||
<Formsy.Form onSubmit={this.submitForm}>
|
||||
{fields.map(fieldName => <div key={fieldName} className={"input-"+fieldName}>{SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction, document)}</div>)}
|
||||
<Button type="submit" bsStyle="primary">Submit</Button>
|
||||
</Formsy.Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
EditDocument.contextTypes = {
|
||||
closeCallback: React.PropTypes.func
|
||||
}
|
||||
|
||||
module.exports = EditDocument;
|
||||
export default EditDocument;
|
69
packages/nova-forms/lib/NewDocument.jsx
Normal file
69
packages/nova-forms/lib/NewDocument.jsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import Formsy from 'formsy-react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
|
||||
import SmartForms from "./smart-forms.jsx";
|
||||
import Utils from './utils.js';
|
||||
|
||||
const NewDocument = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
collection: React.PropTypes.object.isRequired,
|
||||
currentUser: React.PropTypes.object,
|
||||
errorCallback: React.PropTypes.func,
|
||||
successCallback: React.PropTypes.func,
|
||||
methodName: React.PropTypes.string,
|
||||
labelFunction: React.PropTypes.func
|
||||
},
|
||||
|
||||
submitForm(data) {
|
||||
|
||||
// remove any empty properties
|
||||
const document = _.compactObject(Utils.flatten(data));
|
||||
const collection = this.props.collection;
|
||||
const methodName = this.props.methodName ? this.props.methodName : collection._name+'.create';
|
||||
|
||||
Meteor.call(methodName, document, (error, document) => {
|
||||
if (error) {
|
||||
console.log(error)
|
||||
if (this.props.errorCallback) {
|
||||
this.props.errorCallback(document);
|
||||
}
|
||||
} else {
|
||||
if (this.props.successCallback) {
|
||||
this.props.successCallback(document);
|
||||
}
|
||||
if (this.context.closeCallback) {
|
||||
this.context.closeCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
const collection = this.props.collection;
|
||||
const fields = collection.getInsertableFields(this.props.currentUser);
|
||||
|
||||
const style = {
|
||||
maxWidth: "800px",
|
||||
width: "100%"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="new-document" style={style}>
|
||||
<Formsy.Form onSubmit={this.submitForm}>
|
||||
{fields.map(fieldName => <div key={fieldName} className={"input-"+fieldName}>{SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction)}</div>)}
|
||||
<Button type="submit" bsStyle="primary">Submit</Button>
|
||||
</Formsy.Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
NewDocument.contextTypes = {
|
||||
closeCallback: React.PropTypes.func
|
||||
}
|
||||
|
||||
module.exports = NewDocument;
|
||||
export default NewDocument;
|
13
packages/nova-forms/lib/export.js
Normal file
13
packages/nova-forms/lib/export.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions';
|
||||
checkNpmVersions({
|
||||
"react": "^0.14.6",
|
||||
"formsy-react": "^0.17.0",
|
||||
"formsy-react-components": "^0.6.6",
|
||||
"react-bootstrap": "^0.28.3"
|
||||
// 'rebass': '^0.2.4',
|
||||
});
|
||||
|
||||
import NewDocument from "./NewDocument.jsx";
|
||||
import EditDocument from "./EditDocument.jsx";
|
||||
|
||||
export default {NewDocument, EditDocument};
|
51
packages/nova-forms/lib/smart-forms.jsx
Normal file
51
packages/nova-forms/lib/smart-forms.jsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
import React, { PropTypes, Component } from 'react';
|
||||
import Formsy from 'formsy-react';
|
||||
import FRC from 'formsy-react-components';
|
||||
|
||||
import Utils from './utils.js';
|
||||
|
||||
const Checkbox = FRC.Checkbox;
|
||||
const CheckboxGroup = FRC.CheckboxGroup;
|
||||
const Input = FRC.Input;
|
||||
const RadioGroup = FRC.RadioGroup;
|
||||
const Select = FRC.Select;
|
||||
const Textarea = FRC.Textarea;
|
||||
|
||||
const SmartForms = {};
|
||||
|
||||
SimpleSchema.extendOptions({
|
||||
insertableIf: Match.Optional(Function),
|
||||
editableIf: Match.Optional(Function)
|
||||
});
|
||||
|
||||
SmartForms.getComponent = (fieldName, field, labelFunction, document) => {
|
||||
|
||||
let options = [];
|
||||
if (field.autoform && field.autoform.options) {
|
||||
options = typeof field.autoform.options === "function" ? field.autoform.options() : field.autoform.options;
|
||||
}
|
||||
|
||||
const value = document && Utils.deepValue(document, fieldName) ? Utils.deepValue(document, fieldName) : "";
|
||||
const label = typeof labelFunction === "function" ? labelFunction(fieldName) : fieldName;
|
||||
|
||||
switch (field.control) {
|
||||
|
||||
case "text":
|
||||
return <Input key={fieldName} name={fieldName} value={value} label={label} type="text" />;
|
||||
case "textarea":
|
||||
return <Textarea key={fieldName} name={fieldName} value={value} label={label} />;
|
||||
case "checkbox":
|
||||
return <Checkbox key={fieldName} name={fieldName} value={value} label={label}/>;
|
||||
// note: checkboxgroup cause React refs error, so use RadioGroup for now
|
||||
case "checkboxgroup":
|
||||
return <RadioGroup key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
||||
case "radiogroup":
|
||||
return <RadioGroup key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
||||
case "select":
|
||||
return <Select key={fieldName} name={fieldName} value={value} label={label} options={options} />;
|
||||
default:
|
||||
return <Input key={fieldName} name={fieldName} value={value} label={label} type="text" />;
|
||||
}
|
||||
}
|
||||
|
||||
export default SmartForms;
|
37
packages/nova-forms/lib/utils.js
Normal file
37
packages/nova-forms/lib/utils.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
const Utils = {};
|
||||
|
||||
// add support for nested properties
|
||||
Utils.deepValue = function(obj, path){
|
||||
for (var i=0, path=path.split('.'), len=path.length; i<len; i++){
|
||||
obj = obj[path[i]];
|
||||
};
|
||||
return obj;
|
||||
};
|
||||
|
||||
// see http://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects
|
||||
Utils.flatten = function(data) {
|
||||
var result = {};
|
||||
function recurse (cur, prop) {
|
||||
|
||||
if (Object.prototype.toString.call(cur) !== "[object Object]") {
|
||||
result[prop] = cur;
|
||||
} else if (Array.isArray(cur)) {
|
||||
for(var i=0, l=cur.length; i<l; i++)
|
||||
recurse(cur[i], prop + "[" + i + "]");
|
||||
if (l == 0)
|
||||
result[prop] = [];
|
||||
} else {
|
||||
var isEmpty = true;
|
||||
for (var p in cur) {
|
||||
isEmpty = false;
|
||||
recurse(cur[p], prop ? prop+"."+p : p);
|
||||
}
|
||||
if (isEmpty && prop)
|
||||
result[prop] = {};
|
||||
}
|
||||
}
|
||||
recurse(data, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
export default Utils;
|
23
packages/nova-forms/package.js
Normal file
23
packages/nova-forms/package.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
Package.describe({
|
||||
name: "nova:forms",
|
||||
summary: "Form containers for React",
|
||||
version: "0.25.7",
|
||||
git: "https://github.com/meteor-utilities/react-form-containers.git"
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
|
||||
api.versionsFrom("METEOR@1.3");
|
||||
|
||||
api.use([
|
||||
'ecmascript',
|
||||
'check',
|
||||
'tmeasday:check-npm-versions@0.1.1',
|
||||
'aldeed:simple-schema@1.5.3',
|
||||
'aldeed:collection2@2.8.0',
|
||||
'utilities:smart-methods@0.1.2'
|
||||
]);
|
||||
|
||||
api.mainModule("lib/export.js", ["client", "server"]);
|
||||
|
||||
});
|
|
@ -67,6 +67,7 @@ Package.onUse(function (api) {
|
|||
// 'seba:minifiers-autoprefixer@0.0.1',
|
||||
'meteorhacks:unblock@1.1.0',
|
||||
// 'kadira:flow-router@2.10.1',
|
||||
// 'tmeasday:healthcheck-handler@0.0.2',
|
||||
'kadira:flow-router-ssr@3.12.2',
|
||||
'arillo:flow-router-helpers@0.5.0',
|
||||
// 'peerlibrary:reactive-publish@0.2.0',
|
||||
|
|
|
@ -94,7 +94,7 @@ Meteor.publish('posts.single', function (terms) {
|
|||
|
||||
const currentUser = Meteor.users.findOne(this.userId);
|
||||
const options = {fields: Posts.publishedFields.single};
|
||||
const posts = Posts.find(terms, options);
|
||||
const posts = Posts.find(terms._id, options);
|
||||
const post = posts.fetch()[0];
|
||||
const users = getSinglePostUsers(post);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue