pageflow-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.jshintrc +15 -0
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +40 -0
  8. data/Rakefile +4 -0
  9. data/app/assets/javascripts/pageflow/react.js +8975 -0
  10. data/app/assets/javascripts/pageflow/react/components.js +4 -0
  11. data/app/views/pageflow/react/_widget.html.erb +1 -0
  12. data/app/views/pageflow/react/page.html.erb +7 -0
  13. data/js/.eslintrc +33 -0
  14. data/js/.gitignore +1 -0
  15. data/js/karma.conf.js +61 -0
  16. data/js/package.json +43 -0
  17. data/js/spec/.eslintrc +8 -0
  18. data/js/spec/components/background_image_spec.js +47 -0
  19. data/js/spec/components/page_thumbnail_spec.js +213 -0
  20. data/js/spec/create_container_spec.js +82 -0
  21. data/js/spec/resolve_spec.js +3 -0
  22. data/js/spec/resolvers/backbone_model_resolver_spec.js +256 -0
  23. data/js/spec/resolvers/create_recursive_resolver_spec.js +120 -0
  24. data/js/spec/resolvers/editor_file_ids_resolver_spec.js +49 -0
  25. data/js/spec/resolvers/i18n_resolver_spec.js +20 -0
  26. data/js/spec/resolvers/object_resolver_spec.js +165 -0
  27. data/js/spec/resolvers/page_type_resolver_spec.js +23 -0
  28. data/js/spec/resolvers/seed_resolver_spec.js +128 -0
  29. data/js/spec/stub_spec.js +16 -0
  30. data/js/spec/support/render_component.js +7 -0
  31. data/js/src/components/background_image.jsx +60 -0
  32. data/js/src/components/lazy_background_image.jsx +23 -0
  33. data/js/src/components/lazy_loaded_page_thumbnail.jsx +19 -0
  34. data/js/src/components/page_background.jsx +11 -0
  35. data/js/src/components/page_background_image.jsx +13 -0
  36. data/js/src/components/page_content.jsx +51 -0
  37. data/js/src/components/page_header.jsx +15 -0
  38. data/js/src/components/page_link.jsx +35 -0
  39. data/js/src/components/page_shadow.jsx +19 -0
  40. data/js/src/components/page_text.jsx +17 -0
  41. data/js/src/components/page_thumbnail.jsx +86 -0
  42. data/js/src/components/page_wrapper.jsx +11 -0
  43. data/js/src/components/scroller.js +43 -0
  44. data/js/src/create_container.jsx +53 -0
  45. data/js/src/create_page.jsx +38 -0
  46. data/js/src/create_page_component.jsx +45 -0
  47. data/js/src/create_page_type.js +57 -0
  48. data/js/src/create_resolver_root.jsx +21 -0
  49. data/js/src/create_widget.jsx +3 -0
  50. data/js/src/create_widget_type.js +12 -0
  51. data/js/src/index.js +69 -0
  52. data/js/src/mutate.js +17 -0
  53. data/js/src/mutations/mutation.js +5 -0
  54. data/js/src/mutations/update_page_link_mutation.js +30 -0
  55. data/js/src/mutations/update_page_mutation.js +19 -0
  56. data/js/src/resolve.js +45 -0
  57. data/js/src/resolvers/backbone_model_resolver.js +118 -0
  58. data/js/src/resolvers/create_recursive_resolver.js +20 -0
  59. data/js/src/resolvers/current_parent_page_resolver.js +38 -0
  60. data/js/src/resolvers/editor_chapter_resolver.js +10 -0
  61. data/js/src/resolvers/editor_file_ids_resolver.js +30 -0
  62. data/js/src/resolvers/editor_page_resolver.js +11 -0
  63. data/js/src/resolvers/i18n_resolver.js +11 -0
  64. data/js/src/resolvers/object_resolver.js +58 -0
  65. data/js/src/resolvers/page_type_resolver.js +12 -0
  66. data/js/src/resolvers/resolver.js +16 -0
  67. data/js/src/resolvers/seed_chapter_resolver.js +10 -0
  68. data/js/src/resolvers/seed_file_ids_resolver.js +11 -0
  69. data/js/src/resolvers/seed_page_resolver.js +11 -0
  70. data/js/src/resolvers/seed_resolver.js +75 -0
  71. data/js/src/utils/camelize.js +29 -0
  72. data/js/webpack.config.js +31 -0
  73. data/lib/pageflow-react.rb +13 -0
  74. data/lib/pageflow/react/engine.rb +15 -0
  75. data/lib/pageflow/react/page_type.rb +16 -0
  76. data/lib/pageflow/react/version.rb +5 -0
  77. data/lib/pageflow/react/widget_type.rb +21 -0
  78. data/pageflow-react.gemspec +29 -0
  79. metadata +205 -0
@@ -0,0 +1,10 @@
1
+ import BackboneModelResolver from './backbone_model_resolver';
2
+
3
+ export default function(options, callback) {
4
+ return new BackboneModelResolver({
5
+ collection: () => pageflow.chapters,
6
+ attributesForProps: ['id', 'title', 'position'],
7
+ property: 'chapterId',
8
+ ...options
9
+ }, callback);
10
+ };
@@ -0,0 +1,30 @@
1
+ import Resolver from './resolver';
2
+
3
+ export default class extends Resolver {
4
+ constructor(options, callback) {
5
+ super(callback);
6
+ this._options = {
7
+ collections: () => pageflow.files,
8
+ ...options
9
+ };
10
+
11
+ Object.values(this._options.collections()).forEach((collection) =>
12
+ collection.on('remove', this._handleChange, this)
13
+ );
14
+ }
15
+
16
+ get() {
17
+ var files = this._options.collections();
18
+
19
+ return Object.keys(files).reduce((result, collectionName) => {
20
+ result[collectionName] = files[collectionName].map((file) => file.id);
21
+ return result;
22
+ }, {});
23
+ }
24
+
25
+ dispose() {
26
+ Object.values(this._options.collections()).forEach((collection) =>
27
+ collection.off('remove', this._handleChange, this)
28
+ );
29
+ }
30
+ };
@@ -0,0 +1,11 @@
1
+ import BackboneModelResolver from './backbone_model_resolver';
2
+
3
+ export default function(options, callback) {
4
+ return new BackboneModelResolver({
5
+ collection: () => pageflow.pages,
6
+ idAttribute: 'perma_id',
7
+ attributesForProps: ['perma_id', ['type', 'template'], 'chapter_id'],
8
+ includeConfiguration: true,
9
+ ...options
10
+ }, callback);
11
+ };
@@ -0,0 +1,11 @@
1
+ import Resolver from './resolver';
2
+
3
+ export default class extends Resolver {
4
+ get(props, seed) {
5
+ return {
6
+ t: function(key, options) {
7
+ return I18n.t(key, {locale: seed.locale, ...options});
8
+ }
9
+ };
10
+ }
11
+ };
@@ -0,0 +1,58 @@
1
+ import Resolver from './resolver';
2
+
3
+ export default class ObjectResolver extends Resolver {
4
+ constructor(fragment, callback, propertyName) {
5
+ super(callback);
6
+ this._fragment = fragment;
7
+ this._resolvers = {};
8
+ this._propertyName = propertyName;
9
+ }
10
+
11
+ get(props, seed) {
12
+ this._updateResolvers();
13
+
14
+ if (this._propertyName) {
15
+ props = props[this._propertyName];
16
+ }
17
+
18
+ if (!props) {
19
+ return null;
20
+ }
21
+
22
+ return Object.keys(this._resolvers).reduce((result, key) => {
23
+ const resolver = this._resolvers[key];
24
+
25
+ result[key] = resolver.get(props, seed);
26
+ return result;
27
+ }, {...props});
28
+ }
29
+
30
+ dispose() {
31
+ Object.keys(this._resolvers).forEach((key) => {
32
+ this._resolvers[key].dispose();
33
+ });
34
+ }
35
+
36
+ _updateResolvers() {
37
+ var resolvers = this._resolvers;
38
+
39
+ Object.keys(this._fragment || {}).forEach((key) => {
40
+ const resolverProvider = this._fragment[key];
41
+
42
+ if (!resolvers[key]) {
43
+ resolvers[key] = this._createResolver(resolverProvider, key);
44
+ }
45
+ });
46
+ }
47
+
48
+ _createResolver(provider, key) {
49
+ var handleChange = this._handleChange.bind(this);
50
+
51
+ if (typeof provider === 'object') {
52
+ return new ObjectResolver(provider, handleChange, key);
53
+ }
54
+ else {
55
+ return provider(handleChange);
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,12 @@
1
+ import Resolver from './resolver';
2
+
3
+ import camelize from '../utils/camelize';
4
+
5
+ export default class extends Resolver {
6
+ get(props, seed) {
7
+ return {
8
+ name: props.type,
9
+ ...camelize.deep(seed.page_types[props.type])
10
+ };
11
+ }
12
+ };
@@ -0,0 +1,16 @@
1
+ export default class Resolver {
2
+ constructor(callback) {
3
+ this._callback = callback;
4
+ }
5
+
6
+ get(props) {}
7
+
8
+ dispose() {}
9
+
10
+ /** @protected */
11
+ _handleChange() {
12
+ if (this._callback) {
13
+ this._callback();
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,10 @@
1
+ import SeedResolver from './seed_resolver';
2
+
3
+ export default function(options, callback) {
4
+ return new SeedResolver({
5
+ seedProperty: 'chapters',
6
+ attributesForProps: ['id', 'title'],
7
+ property: 'chapterId',
8
+ ...options
9
+ }, callback);
10
+ };
@@ -0,0 +1,11 @@
1
+ import Resolver from './resolver';
2
+
3
+ export default class extends Resolver {
4
+ constructor(_, callback) {
5
+ super(callback);
6
+ }
7
+
8
+ get(props, seed) {
9
+ return seed.file_ids;
10
+ }
11
+ };
@@ -0,0 +1,11 @@
1
+ import SeedResolver from './seed_resolver';
2
+
3
+ export default function(options, callback) {
4
+ return new SeedResolver({
5
+ seedProperty: 'pages',
6
+ idAttribute: 'perma_id',
7
+ attributesForProps: ['perma_id', ['type', 'template'], 'chapter_id'],
8
+ includeConfiguration: true,
9
+ ...options
10
+ }, callback);
11
+ };
@@ -0,0 +1,75 @@
1
+ import Resolver from './resolver';
2
+ import createRecursiveResolver from './create_recursive_resolver';
3
+
4
+ import camelize from '../utils/camelize';
5
+
6
+ /**
7
+ * Resolves a foreign key to an object of attributes from the seed
8
+ * data.
9
+ */
10
+ class SeedResolver extends Resolver {
11
+ constructor(options, callback) {
12
+ super(callback);
13
+
14
+ this._options = {
15
+ idAttribute: 'id',
16
+ attributesForProps: ['id'],
17
+ includeConfiguration: false,
18
+ ...options
19
+ };
20
+ }
21
+
22
+ get(props, seed) {
23
+ var attributes = this._getAttributes(props, seed);
24
+ return this._getProps(attributes);
25
+ }
26
+
27
+ _getAttributes(props, seed) {
28
+ return this._getAttributesById(this._getModelId(props), seed);
29
+ }
30
+
31
+ _getAttributesById(id, seed) {
32
+ const collection = seed[this._options.seedProperty];
33
+
34
+ if (!collection) {
35
+ return null;
36
+ }
37
+
38
+ return collection.find((attributes) => {
39
+ return attributes[this._options.idAttribute] === id;
40
+ });
41
+ }
42
+
43
+ _getModelId(props) {
44
+ return props[this._options.property];
45
+ }
46
+
47
+ _getProps(attributes) {
48
+ var props;
49
+
50
+ if (!attributes) {
51
+ return null;
52
+ }
53
+
54
+ props = this._getPropsFromAttributes(attributes);
55
+
56
+ if (this._options.includeConfiguration) {
57
+ Object.assign(props, camelize.deep(attributes.configuration));
58
+ }
59
+
60
+ return props;
61
+ }
62
+
63
+ _getPropsFromAttributes(attributes) {
64
+ return this._options.attributesForProps.reduce((result, name) => {
65
+ if (typeof name === 'string') {
66
+ name = [camelize(name), name];
67
+ }
68
+
69
+ result[name[0]] = attributes[name[1]];
70
+ return result;
71
+ }, {})
72
+ }
73
+ };
74
+
75
+ export default createRecursiveResolver(SeedResolver);
@@ -0,0 +1,29 @@
1
+ function camelize(snakeCase) {
2
+ return snakeCase.replace(/_[a-z]/g, function(match) {
3
+ return match[1].toUpperCase();
4
+ });
5
+ };
6
+
7
+ camelize.keys = function(object) {
8
+ return Object.keys(object).reduce((result, key) => {
9
+ result[camelize(key)] = object[key];
10
+ return result;
11
+ }, {});
12
+ };
13
+
14
+ camelize.deep = function(object) {
15
+ if (Array.isArray(object)) {
16
+ return object.map(camelize.deep);
17
+ }
18
+ else if (typeof object === 'object' && object) {
19
+ return Object.keys(object).reduce((result, key) => {
20
+ result[camelize(key)] = camelize.deep(object[key]);
21
+ return result;
22
+ }, {});
23
+ }
24
+ else {
25
+ return object;
26
+ }
27
+ };
28
+
29
+ export default camelize;
@@ -0,0 +1,31 @@
1
+ module.exports = {
2
+ context: __dirname + '/src',
3
+ entry: [
4
+ 'babel-polyfill',
5
+ './index.js'
6
+ ],
7
+
8
+ module: {
9
+ loaders: [
10
+ {
11
+ test: /\.jsx?$/,
12
+ exclude: /node_modules/,
13
+ loaders: ['babel-loader?stage=0'],
14
+ }
15
+ ],
16
+ },
17
+
18
+ output: {
19
+ path: __dirname + '/../app/assets/javascripts/pageflow',
20
+ filename: 'react.js',
21
+
22
+ libraryTarget: 'assign',
23
+ library: ['pageflow', 'react']
24
+ },
25
+
26
+ externals: {
27
+ pageflow: 'pageflow',
28
+ react: 'React',
29
+ 'react-dom': 'ReactDOM'
30
+ }
31
+ };
@@ -0,0 +1,13 @@
1
+ require 'pageflow/react/engine'
2
+
3
+ module Pageflow
4
+ module React
5
+ def self.create_page_type(name, component_name)
6
+ Pageflow::React::PageType.new(name, component_name)
7
+ end
8
+
9
+ def self.create_widget_type(name, role, component_name)
10
+ Pageflow::React::WidgetType.new(name, role, component_name)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'react-rails'
2
+
3
+ module Pageflow
4
+ module React
5
+ class Engine < Rails::Engine
6
+ isolate_namespace Pageflow::React
7
+
8
+ config.autoload_paths << File.join(config.root, 'lib')
9
+
10
+ initializer "pageflow-react.add_watchable_files", group: :all do |app|
11
+ app.config.watchable_files.concat Dir["#{config.root}/app/assets/javascripts/**/*.jsx*"]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Pageflow
2
+ module React
3
+ class PageType < Pageflow::PageType
4
+ attr_reader :name, :component_name
5
+
6
+ def initialize(name, component_name)
7
+ @name = name
8
+ @component_name = component_name
9
+ end
10
+
11
+ def template_path
12
+ 'pageflow/react/page'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module Pageflow
2
+ module React
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module Pageflow
2
+ module React
3
+ class WidgetType < Pageflow::WidgetType
4
+ attr_reader :name, :role, :component_name
5
+
6
+ def initialize(name, role, component_name)
7
+ @name = name
8
+ @role = role
9
+ @component_name = component_name
10
+ end
11
+
12
+ def roles
13
+ [role]
14
+ end
15
+
16
+ def render(template, _)
17
+ template.render(File.join('pageflow', 'react', 'widget'), name: name)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pageflow/react/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pageflow-react"
8
+ spec.version = Pageflow::React::VERSION
9
+ spec.authors = ["Tim Fischbach"]
10
+ spec.email = ["mail@timfischbach.de"]
11
+ spec.summary = %q{Building Pageflow page types with React.js}
12
+ spec.homepage = "https://github.com/codevise/pageflow-react"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "pageflow", "~> 0.10"
21
+ spec.add_dependency "react-rails", "~> 1.6.0"
22
+ spec.add_dependency "multi_json", "~> 1.11"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+
27
+ # Semantic versioning rake tasks
28
+ spec.add_development_dependency 'semmy', '~> 0.2'
29
+ end