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,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
+ };