diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "cosmiconfig": "^3.0.1", "dot-prop": "^4.2.0", "escape-html": "^1.0.3", + "events": "^2.0.0", "expand-hash": "^0.2.2", "express": "^4.14.0", "gm": "^1.23.0", diff --git a/react-components/index.js b/react-components/index.js --- a/react-components/index.js +++ b/react-components/index.js @@ -1,3 +1,6 @@ module.exports = { ResourceSelect: require("./lib/ResourceSelect.jsx"), + Collection: require("./lib/collection.jsx"), + Resource: require("./lib/resource.jsx"), + QueryStores: require("./lib/query-stores/query-store.js"), }; diff --git a/react-components/lib/collection.jsx b/react-components/lib/collection.jsx new file mode 100644 --- /dev/null +++ b/react-components/lib/collection.jsx @@ -0,0 +1,36 @@ +const React = require("react"); +const CachedHttp = require("./cached-http.js"); +const KeyValueStore = require("./stores/key-value-store.js"); +const ConnectWithKeyValueStore = require("./stores/connect-with-key-value-store.jsx"); + +function Collection({ collection, query_store }, component) { + return class Component extends React.Component { + constructor() { + super(); + this.state = { + loading: true, + resources: [], + }; + } + componentDidMount() { + this.setState({ loading: true }); + CachedHttp.get( + `/api/v1/collections/${collection}`, + query_store.getQuery() + ).then(resources => { + console.log(resources); + this.setState({ resources, loading: false }); + }); + } + render() { + return React.createElement(component, { + collection, + query_store, + resources: this.state.resources, + loading: this.state.loading, + }); + } + }; +} + +module.exports = Collection; diff --git a/react-components/lib/query-stores/query-store.js b/react-components/lib/query-stores/query-store.js new file mode 100644 --- /dev/null +++ b/react-components/lib/query-stores/query-store.js @@ -0,0 +1,20 @@ +const EventEmitter = require("events"); + +class QueryStore extends EventEmitter { + constructor() { + super(); + } + init() { + this.store.on("change", () => this.emit("change")); + } + setFilter(filter) { + this.store.set("filter", filter); + this.store.set("pagination.page", 1); + } + getQuery() { + return this.store.getStore(); + } +} +module.exports = QueryStore; + +QueryStore.Stateful = require("./stateful-query-store.js"); diff --git a/react-components/lib/query-stores/stateful-query-store.js b/react-components/lib/query-stores/stateful-query-store.js new file mode 100644 --- /dev/null +++ b/react-components/lib/query-stores/stateful-query-store.js @@ -0,0 +1,13 @@ +const KeyValueStore = require("./../stores/key-value-store.js"); +const QueryStore = require("./query-store.js"); + +module.exports = class StatefulQueryStore extends QueryStore { + constructor() { + super(); + this.store = new KeyValueStore({ + filter: {}, + pagination: { page: 1, items: 12 }, + }); + this.init(); + } +}; diff --git a/react-components/lib/resource.jsx b/react-components/lib/resource.jsx new file mode 100644 --- /dev/null +++ b/react-components/lib/resource.jsx @@ -0,0 +1,27 @@ +const React = require("react"); +const CachedHttp = require("./cached-http.js"); + +module.exports = ({ collection }, component) => + class Resource extends React.Component { + constructor() { + super(); + this.state = { + loading: true, + resource: null, + }; + } + componentDidMount() { + CachedHttp.get(`/api/v1/collections/${collection}/${this.props.id}`).then( + resource => this.setState({ loading: false, resource }) + ); + } + render() { + if (!this.props.id) { + throw Error("Please provide the resource id as an 'id' prop"); + } + return React.createElement(component, { + resource: this.state.resource, + loading: this.state.loading, + }); + } + }; diff --git a/react-components/lib/stores/connect-with-key-value-store.jsx b/react-components/lib/stores/connect-with-key-value-store.jsx new file mode 100755 --- /dev/null +++ b/react-components/lib/stores/connect-with-key-value-store.jsx @@ -0,0 +1,33 @@ +const React = require("react"); +const merge = require("merge"); + +module.exports = function(store, store_prop_name, component) { + return React.createClass({ + getInitialState: function() { + return { [store_prop_name]: {} }; + }, + componentDidMount: function() { + const self = this; + const listener = function(value) { + self.setState({ [store_prop_name]: value }); + }; + store.on("change", listener); + self.setState({ + listener: listener, + [store_prop_name]: store.getStore(), + }); + }, + componentWillUnmount: function() { + const self = this; + store.off("change", self.state.listener); + }, + render: function() { + return React.createElement( + component, + merge(true, this.props, { + [store_prop_name]: this.state[store_prop_name], + }) + ); + }, + }); +}; diff --git a/react-components/lib/stores/key-value-store.js b/react-components/lib/stores/key-value-store.js new file mode 100755 --- /dev/null +++ b/react-components/lib/stores/key-value-store.js @@ -0,0 +1,51 @@ +const React = require("react"); +const EventEmitter = require("event-emitter"); + +const KeyValueStore = function(initial_values) { + const self = this; + var ee = new EventEmitter(); + this.on = ee.on.bind(ee); + this.off = ee.off.bind(ee); + this.once = ee.once.bind(ee); + this.emit = ee.emit.bind(ee); + + let store = initial_values || {}; + + this.set = function(key, value) { + const key_elements = key.split(".").reverse(); + let current_pointer = store; + while (key_elements.length > 1) { + let current_key = key_elements.pop(); + if (current_pointer[current_key] === undefined) { + current_pointer[current_key] = {}; + } + current_pointer = current_pointer[current_key]; + } + current_pointer[key_elements.pop()] = value; + ee.emit("change", store); + }; + + this.setters = {}; + this.change_handlers = {}; + for (const field_name in initial_values) { + this.change_handlers[field_name] = function(e) { + self.set(field_name, e.target.value); + }; + this.setters[field_name] = this.set.bind(field_name); + } + + this.get = function(key) { + return store[key]; + }; + + this.replaceStore = function(value) { + store = value; + ee.emit("change", store); + }; + + this.getStore = function() { + return store; + }; +}; + +module.exports = KeyValueStore;