pageflow-react 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,11 @@
1
+ import {Component} from 'react';
2
+
3
+ export default class extends Component {
4
+ render() {
5
+ return (
6
+ <div className="backgroundArea">
7
+ {this.props.children}
8
+ </div>
9
+ );
10
+ }
11
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+
3
+ import LazyBackgroundImage from './lazy_background_image.jsx';
4
+
5
+ export default class PageBackgroundImage extends React.Component {
6
+ render() {
7
+ return (
8
+ <LazyBackgroundImage imageFileId={this.props.page.backgroundImageId}
9
+ position={[this.props.page.backgroundImageX, this.props.page.backgroundImageY]}
10
+ className="background background_image" />
11
+ );
12
+ }
13
+ };
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+
3
+ import Scroller from './scroller';
4
+ import createPageComponent from '../create_page_component.jsx';
5
+
6
+ export default class PageContent extends React.Component {
7
+ static childContextTypes = {
8
+ pageScroller: React.PropTypes.object
9
+ }
10
+
11
+ getChildContext() {
12
+ this._pageScroller = this._pageScroller || {
13
+ disable: () => {
14
+ this.refs.scroller.disable();
15
+ },
16
+ enable: () => {
17
+ this.refs.scroller.enable();
18
+ }
19
+ };
20
+
21
+ return {
22
+ pageScroller: this._pageScroller,
23
+ };
24
+ }
25
+
26
+ render() {
27
+ return (
28
+ <div className="content">
29
+ <Scroller ref="scroller">
30
+ <div className="contentWrapper">
31
+ {this.props.children}
32
+ </div>
33
+ </Scroller>
34
+ </div>
35
+ );
36
+ }
37
+
38
+ pageWillActivate(options) {
39
+ this.refs.scroller.resetPosition({position: options.position})
40
+ }
41
+
42
+ pageDidActivate() {
43
+ this.refs.scroller.enable()
44
+ }
45
+
46
+ pageWillDeactivate() {
47
+ this.refs.scroller.disable()
48
+ }
49
+ };
50
+
51
+ export default createPageComponent(PageContent);
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+
3
+ export default class extends React.Component {
4
+ render() {
5
+ return (
6
+ <div className="page_header">
7
+ <h2>
8
+ <span className="tagline">{this.props.page.tagline}</span>
9
+ <span className="title">{this.props.page.title}</span>
10
+ <span className="subtitle">{this.props.page.subtitle}</span>
11
+ </h2>
12
+ </div>
13
+ );
14
+ }
15
+ };
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+
3
+ export default class PageLink extends React.Component {
4
+ render() {
5
+ return (
6
+ <a href={this._href()}
7
+ className={this.props.className}
8
+ onClick={this._handleClick.bind(this)}>
9
+ {this.props.children}
10
+ </a>
11
+ );
12
+ }
13
+
14
+ _href() {
15
+ if (this._targetPage()) {
16
+ return '#' + this._targetPage().permaId;
17
+ }
18
+ else {
19
+ return '#missing';
20
+ }
21
+ }
22
+
23
+ _handleClick(event) {
24
+ if (this._targetPage()) {
25
+ pageflow.slides.goToByPermaId(this._targetPage().permaId, {
26
+ transition: this.props.pageLink.pageTransition
27
+ });
28
+ }
29
+ event.preventDefault();
30
+ }
31
+
32
+ _targetPage() {
33
+ return this.props.pageLink.targetPage;
34
+ }
35
+ };
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+
3
+ export default class PageShadow extends React.Component {
4
+ render() {
5
+ return (
6
+ <div className="shadow_wrapper">
7
+ <div className="shadow" style={this.style()} />
8
+ </div>
9
+ );
10
+ }
11
+
12
+ style() {
13
+ if ('gradientOpacity' in this.props.page) {
14
+ return {
15
+ opacity: this.props.page.gradientOpacity / 100
16
+ };
17
+ }
18
+ }
19
+ };
@@ -0,0 +1,17 @@
1
+ import {Component} from 'react';
2
+
3
+ export default class PageText extends Component {
4
+ render() {
5
+ return (
6
+ <div className="contentText">
7
+ <p dangerouslySetInnerHTML={this.text()} />
8
+ </div>
9
+ );
10
+ }
11
+
12
+ text() {
13
+ return {__html: this.props.page.text};
14
+ }
15
+ };
16
+
17
+ export default PageText;
@@ -0,0 +1,86 @@
1
+ import classNames from 'classnames';
2
+
3
+ import createContainer from '../create_container.jsx';
4
+ import resolve from '../resolve';
5
+ import camelize from '../utils/camelize';
6
+
7
+ export function PageThumbnail(props) {
8
+ return (
9
+ <div className={className(props)} />
10
+ );
11
+ };
12
+
13
+ PageThumbnail.defaultProps = {
14
+ imageStyle: 'navigation_thumbnail_large'
15
+ };
16
+
17
+ function className(props) {
18
+ return classNames(
19
+ {load_image: props.lazy && props.loaded},
20
+ props.className,
21
+ typeClassName(props.page),
22
+ thumbnailClassName(props)
23
+ );
24
+ }
25
+
26
+ function typeClassName(page) {
27
+ return page ? `is_${page.type.name}` : 'is_dangling';
28
+ }
29
+
30
+ function thumbnailClassName(props) {
31
+ var candidate = firstPresentCandidate(props);
32
+
33
+ if (candidate) {
34
+ return thumbnailCandidateClassName(props, candidate);
35
+ }
36
+ }
37
+
38
+ function firstPresentCandidate(props) {
39
+ return thumbnailCandidates(props).find(candidate => {
40
+ var id = thumbnailCandidateId(props, candidate);
41
+ var ids = props.fileIds[candidate.collectionName] || [];
42
+
43
+ return (id && ids.includes(id));
44
+ });
45
+ }
46
+
47
+ function thumbnailCandidates(props) {
48
+ return [
49
+ customThumbnailCandidate(props),
50
+ ...pageTypeCandidates(props.page)
51
+ ];
52
+ }
53
+
54
+ function customThumbnailCandidate(props) {
55
+ return {
56
+ id: props.customThumbnailId,
57
+ cssClassPrefix: 'pageflow_image_file',
58
+ collectionName: 'image_files'
59
+ }
60
+ }
61
+
62
+ function pageTypeCandidates(page) {
63
+ return page ? page.type.thumbnailCandidates : [];
64
+ }
65
+
66
+ function thumbnailCandidateClassName(props, candidate) {
67
+ return [
68
+ props.lazy ? 'lazy' : null,
69
+ candidate.cssClassPrefix,
70
+ props.imageStyle,
71
+ thumbnailCandidateId(props, candidate)
72
+ ].filter(Boolean).join('_');
73
+ }
74
+
75
+ function thumbnailCandidateId(props, candidate) {
76
+ return 'id' in candidate ? candidate.id : props.page[camelize(candidate.attribute)];
77
+ }
78
+
79
+ export default createContainer(PageThumbnail, {
80
+ fragments: {
81
+ page: {
82
+ type: resolve('pageType')
83
+ },
84
+ fileIds: resolve('fileIds')
85
+ }
86
+ });
@@ -0,0 +1,11 @@
1
+ import {Component} from 'react';
2
+
3
+ export default class extends Component {
4
+ render() {
5
+ return (
6
+ <div className="content_and_background">
7
+ {this.props.children}
8
+ </div>
9
+ );
10
+ }
11
+ };
@@ -0,0 +1,43 @@
1
+ import {Component} from 'react';
2
+ import ReactDom from 'react-dom';
3
+
4
+ /** @api private */
5
+ class Scroller extends Component {
6
+ render() {
7
+ return (
8
+ <div ref="wrapper" className="scroller"><div>{this.props.children}</div></div>
9
+ );
10
+ }
11
+
12
+ componentDidMount() {
13
+ if (typeof jQuery !== 'undefined') {
14
+ var element = jQuery(ReactDOM.findDOMNode(this.refs.wrapper));
15
+
16
+ element.scroller();
17
+ window.sss = this.scroller = element.scroller('instance');
18
+ }
19
+ }
20
+
21
+ componentDidUpdate() {
22
+ this.scroller.refresh();
23
+ }
24
+
25
+ enable() {
26
+ this.scroller.enable();
27
+ this.scroller.afterAnimationHook();
28
+ }
29
+
30
+ disable() {
31
+ this.scroller.disable();
32
+ }
33
+
34
+ resetPosition(options) {
35
+ this.scroller.resetPosition(options);
36
+ }
37
+
38
+ resetPosition(options) {
39
+ this.scroller.resetPosition(options);
40
+ }
41
+ };
42
+
43
+ export default Scroller;
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+
3
+ import ObjectResolver from './resolvers/object_resolver';
4
+
5
+ export default function(Component, options) {
6
+ options = options || {};
7
+
8
+ if (options.editorOnly && !PAGEFLOW_EDITOR) {
9
+ return class extends React.Component {
10
+ render() {
11
+ return false;
12
+ }
13
+ };
14
+ }
15
+
16
+ return class extends React.Component {
17
+ static contextTypes = {
18
+ resolverSeed: React.PropTypes.object
19
+ }
20
+
21
+ constructor(props, context) {
22
+ super(props, context);
23
+
24
+ this._resolver = new ObjectResolver(
25
+ options.fragments,
26
+ this._handleChange.bind(this)
27
+ );
28
+
29
+ this.state = this._resolve(props);
30
+ }
31
+
32
+ componentWillReceiveProps(nextProps) {
33
+ this.setState(this._resolve(nextProps));
34
+ }
35
+
36
+ componentWillUnmount() {
37
+ this._resolver.dispose();
38
+ }
39
+
40
+ _handleChange() {
41
+ this.setState(this._resolve(this.props));
42
+ }
43
+
44
+ _resolve(props) {
45
+ return this._resolver.get(props, this.context.resolverSeed);
46
+ }
47
+
48
+ render() {
49
+ return (<Component {...this.state} />);
50
+ }
51
+ };
52
+
53
+ };
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+
3
+ import createContainer from './create_container.jsx';
4
+ import createResolverRoot from './create_resolver_root.jsx';
5
+ import resolve from './resolve';
6
+
7
+ export default function(Component, options = {}) {
8
+ const ContainerComponent = createContainer(Component, {
9
+ fragments: {
10
+ page: resolve('page', {
11
+ property: 'pageId'
12
+ }),
13
+ ...(options.fragments || {})
14
+ },
15
+ });
16
+
17
+ class Page extends React.Component {
18
+ static childContextTypes = {
19
+ pageHooks: React.PropTypes.object,
20
+ pageIsPreloaded: React.PropTypes.bool
21
+ }
22
+
23
+ getChildContext() {
24
+ return {
25
+ pageHooks: this.props.pageHooks,
26
+ pageIsPreloaded: this.props.isPreloaded
27
+ };
28
+ }
29
+
30
+ render() {
31
+ return (
32
+ <ContainerComponent pageId={this.props.pageId} />
33
+ );
34
+ }
35
+ };
36
+
37
+ return createResolverRoot(Page);
38
+ };
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+
3
+ export default function(Component) {
4
+ return class extends React.Component {
5
+ static contextTypes = {
6
+ pageHooks: React.PropTypes.object
7
+ }
8
+
9
+ componentDidMount() {
10
+ var component = this.refs.component;
11
+
12
+ this.context.pageHooks.on({
13
+ activating: (options) => {
14
+ trigger('pageWillActivate', options);
15
+ },
16
+
17
+ activated: () => {
18
+ trigger('pageDidActivate');
19
+ },
20
+
21
+ deactivating: () => {
22
+ trigger('pageDidDeactivate');
23
+ },
24
+
25
+ resize: () => {
26
+ trigger('pageDidResize');
27
+ },
28
+ }, this);
29
+
30
+ function trigger(name, options) {
31
+ if (typeof component[name] === 'function') {
32
+ component[name].call(component, options);
33
+ }
34
+ }
35
+ }
36
+
37
+ componentWillUnmount() {
38
+ this.context.pageHooks.off(null, null, this);
39
+ }
40
+
41
+ render() {
42
+ return (<Component {...this.props} ref="component" />);
43
+ }
44
+ }
45
+ };