pageflow-react 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.jshintrc +15 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +4 -0
- data/app/assets/javascripts/pageflow/react.js +8975 -0
- data/app/assets/javascripts/pageflow/react/components.js +4 -0
- data/app/views/pageflow/react/_widget.html.erb +1 -0
- data/app/views/pageflow/react/page.html.erb +7 -0
- data/js/.eslintrc +33 -0
- data/js/.gitignore +1 -0
- data/js/karma.conf.js +61 -0
- data/js/package.json +43 -0
- data/js/spec/.eslintrc +8 -0
- data/js/spec/components/background_image_spec.js +47 -0
- data/js/spec/components/page_thumbnail_spec.js +213 -0
- data/js/spec/create_container_spec.js +82 -0
- data/js/spec/resolve_spec.js +3 -0
- data/js/spec/resolvers/backbone_model_resolver_spec.js +256 -0
- data/js/spec/resolvers/create_recursive_resolver_spec.js +120 -0
- data/js/spec/resolvers/editor_file_ids_resolver_spec.js +49 -0
- data/js/spec/resolvers/i18n_resolver_spec.js +20 -0
- data/js/spec/resolvers/object_resolver_spec.js +165 -0
- data/js/spec/resolvers/page_type_resolver_spec.js +23 -0
- data/js/spec/resolvers/seed_resolver_spec.js +128 -0
- data/js/spec/stub_spec.js +16 -0
- data/js/spec/support/render_component.js +7 -0
- data/js/src/components/background_image.jsx +60 -0
- data/js/src/components/lazy_background_image.jsx +23 -0
- data/js/src/components/lazy_loaded_page_thumbnail.jsx +19 -0
- data/js/src/components/page_background.jsx +11 -0
- data/js/src/components/page_background_image.jsx +13 -0
- data/js/src/components/page_content.jsx +51 -0
- data/js/src/components/page_header.jsx +15 -0
- data/js/src/components/page_link.jsx +35 -0
- data/js/src/components/page_shadow.jsx +19 -0
- data/js/src/components/page_text.jsx +17 -0
- data/js/src/components/page_thumbnail.jsx +86 -0
- data/js/src/components/page_wrapper.jsx +11 -0
- data/js/src/components/scroller.js +43 -0
- data/js/src/create_container.jsx +53 -0
- data/js/src/create_page.jsx +38 -0
- data/js/src/create_page_component.jsx +45 -0
- data/js/src/create_page_type.js +57 -0
- data/js/src/create_resolver_root.jsx +21 -0
- data/js/src/create_widget.jsx +3 -0
- data/js/src/create_widget_type.js +12 -0
- data/js/src/index.js +69 -0
- data/js/src/mutate.js +17 -0
- data/js/src/mutations/mutation.js +5 -0
- data/js/src/mutations/update_page_link_mutation.js +30 -0
- data/js/src/mutations/update_page_mutation.js +19 -0
- data/js/src/resolve.js +45 -0
- data/js/src/resolvers/backbone_model_resolver.js +118 -0
- data/js/src/resolvers/create_recursive_resolver.js +20 -0
- data/js/src/resolvers/current_parent_page_resolver.js +38 -0
- data/js/src/resolvers/editor_chapter_resolver.js +10 -0
- data/js/src/resolvers/editor_file_ids_resolver.js +30 -0
- data/js/src/resolvers/editor_page_resolver.js +11 -0
- data/js/src/resolvers/i18n_resolver.js +11 -0
- data/js/src/resolvers/object_resolver.js +58 -0
- data/js/src/resolvers/page_type_resolver.js +12 -0
- data/js/src/resolvers/resolver.js +16 -0
- data/js/src/resolvers/seed_chapter_resolver.js +10 -0
- data/js/src/resolvers/seed_file_ids_resolver.js +11 -0
- data/js/src/resolvers/seed_page_resolver.js +11 -0
- data/js/src/resolvers/seed_resolver.js +75 -0
- data/js/src/utils/camelize.js +29 -0
- data/js/webpack.config.js +31 -0
- data/lib/pageflow-react.rb +13 -0
- data/lib/pageflow/react/engine.rb +15 -0
- data/lib/pageflow/react/page_type.rb +16 -0
- data/lib/pageflow/react/version.rb +5 -0
- data/lib/pageflow/react/widget_type.rb +21 -0
- data/pageflow-react.gemspec +29 -0
- metadata +205 -0
@@ -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,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
|
+
};
|