Skip to content

Commit f9ef299

Browse files
author
Amir Tocker
committed
Create CloudinaryComponent.
1 parent 2cdb534 commit f9ef299

File tree

5 files changed

+152
-61
lines changed

5 files changed

+152
-61
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import React, {Component, PropTypes} from 'react';
2+
import cloudinary from 'cloudinary-core';
3+
4+
const camelCase = cloudinary.Util.camelCase;
5+
const snakeCase = cloudinary.Util.snakeCase;
6+
7+
export default class CloudinaryComponent extends Component {
8+
constructor(props, context) {
9+
super(props, context);
10+
}
11+
12+
getChildContext() {
13+
return {};
14+
}
15+
16+
componentWillReceiveProps(nextProps) {
17+
}
18+
19+
componentWillMount() {
20+
}
21+
22+
componentDidMount() {
23+
}
24+
25+
componentWillUnmount() {
26+
}
27+
28+
componentWillUpdate(nextProps, nextState) {
29+
}
30+
31+
componentDidUpdate(prevProps, prevState) {
32+
}
33+
34+
shouldComponentUpdate(nextProps, nextState) {
35+
return true;
36+
}
37+
38+
render() {
39+
return null;
40+
}
41+
42+
/**
43+
* Combine props and context to create an option Object that can be passed to Cloudinary methods.<br>
44+
* All names are converted to snake_case.
45+
* @param props
46+
* @param context
47+
* @returns {{}}
48+
*/
49+
getOptions(props, context) {
50+
var options = {};
51+
52+
for(let key in context) {
53+
let value = context[key];
54+
if(value !== null && value !== undefined) {
55+
options[snakeCase(key)] = value;
56+
}
57+
}
58+
for(let key in props) {
59+
let value = props[key];
60+
if(value !== null && value !== undefined) {
61+
options[snakeCase(key)] = value;
62+
}
63+
}
64+
return options;
65+
}
66+
}
67+
CloudinaryComponent.VALID_OPTIONS = cloudinary.Configuration.CONFIG_PARAMS.concat(cloudinary.Transformation.new().PARAM_NAMES).map( camelCase);
68+
CloudinaryComponent.contextTypes = typesFrom(CloudinaryComponent.VALID_OPTIONS);
69+
70+
CloudinaryComponent.propTypes = CloudinaryComponent.contextTypes ;
71+
72+
CloudinaryComponent.childContextTypes = {};
73+
74+
/**
75+
* Create a React type definition object. All items are PropTypes.string.
76+
* @param {Array} configparams a list of parameter names
77+
* @returns {Object}
78+
*/
79+
function typesFrom(configparams) {
80+
configparams = configparams || [];
81+
const types = {};
82+
for (let key of configparams) {
83+
types[camelCase(key)] = PropTypes.string;
84+
}
85+
return types;
86+
}
87+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "cloudinarycomponent",
3+
"version": "1.0.0",
4+
"main": "./CloudinaryComponent.js",
5+
"private": true
6+
}

src/components/CloudinaryContext/CloudinaryContext.js

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
1-
import React, {Component, PropTypes} from 'react';
2-
import cloudinary from 'cloudinary-core';
3-
4-
export default class CloudinaryContext extends React.Component {
1+
import React from 'react';
2+
import CloudinaryComponent from '../CloudinaryComponent';
3+
4+
/**
5+
* Provides a container for Cloudinary components. Any option set in CloudinaryContext will be passed to the children.
6+
* @example
7+
* <CloudinaryContext cloudName="mycloud" dpr="auto">
8+
* <!-- other tags -->
9+
* <Image publicId={id}/>
10+
* </CloudinaryContext>
11+
*
12+
*/
13+
export default class CloudinaryContext extends CloudinaryComponent {
514
constructor(props, context) {
615
super(props, context);
716
this.state = {};
817
}
918

1019
getChildContext() {
1120
let {children, ...otherProps} = this.props;
12-
return Object.assign({}, this.context, otherProps);
21+
let context = {};
22+
// only pass valid Cloudinary options
23+
CloudinaryComponent.VALID_OPTIONS.forEach(key => {
24+
let val = otherProps[key] || this.context[key];
25+
if(val !== null && val !== undefined){
26+
context[key] = val;
27+
}
28+
});
29+
return context;
1330
}
1431

1532
componentWillReceiveProps(nextProps, nextContext) {
@@ -40,19 +57,6 @@ export default class CloudinaryContext extends React.Component {
4057
);
4158
}
4259
}
43-
CloudinaryContext.propTypes = {};
60+
CloudinaryContext.propTypes = CloudinaryComponent.propTypes;
4461
CloudinaryContext.defaultProps = {};
45-
CloudinaryContext.contextTypes = {
46-
cloudName: PropTypes.string.isRequired
47-
};
48-
CloudinaryContext.childContextTypes = {
49-
cloudName: PropTypes.string.isRequired
50-
};
51-
for (let key of cloudinary.Configuration.CONFIG_PARAMS) {
52-
CloudinaryContext.childContextTypes[cloudinary.Util.camelCase(key)] = PropTypes.string;
53-
}
54-
for (let key of cloudinary.Transformation.new().PARAM_NAMES) {
55-
CloudinaryContext.childContextTypes[cloudinary.Util.camelCase(key)] = PropTypes.string;
56-
}
57-
58-
console.log(CloudinaryContext.childContextTypes);
62+
CloudinaryContext.childContextTypes = CloudinaryComponent.contextTypes;

src/components/Image/Image.js

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,27 @@
11
import React, {Component, PropTypes} from 'react';
22
import cloudinary from 'cloudinary-core';
3+
import CloudinaryComponent from '../CloudinaryComponent';
34

4-
export default class Image extends React.Component {
5+
export default class Image extends CloudinaryComponent {
56
constructor(props, context) {
67
super(props, context);
7-
var options = Object.assign({}, context, props);
8-
var options2 = this.snakeCase(options);
8+
var options = this.getOptions(props, context);
9+
var options2 = options;
910
let cl = cloudinary.Cloudinary.new(options2);
10-
// let tr = cloudinary.Transformation.new(options);
11-
//
12-
console.log("trying url", props, options2);
1311
var url = cl.url(props.publicId, options2);
14-
console.log(url);
1512
this.state = {url: url};
16-
// this.foo = this.foo.bind(this);
17-
}
18-
19-
snakeCase(options) {
20-
let res = {};
21-
for(let key of Object.keys(options)) {
22-
res[cloudinary.Util.snakeCase(key)] = options[key];
23-
}
24-
return res;
2513
}
2614

2715
componentWillReceiveProps(nextProps, nextContext) {
28-
var options = Object.assign({}, nextContext, nextProps);
16+
var options = this.getOptions(nextProps, nextContext);
2917

3018
let cl = cloudinary.Cloudinary.new(options);
31-
let tr = cloudinary.Transformation.new(options);
3219
var url = cl.url(this.props.publicId, options);
33-
console.log(url);
34-
var transformation = tr.toString();
35-
console.log(transformation);
36-
this.setState({
37-
url: url,
38-
transformation: transformation
39-
})
20+
if(url != this.state.url){
21+
this.setState({
22+
url: url
23+
})
24+
}
4025
}
4126

4227
componentWillMount() {
@@ -59,7 +44,7 @@ export default class Image extends React.Component {
5944
}
6045

6146
render() {
62-
var options = Object.assign({}, this.context, this.props);
47+
var options = this.getOptions(this.props, this.context);
6348
var {publicId, transformation, ...other} = this.props;
6449
var attributes = cloudinary.Transformation.new(options).toHtmlAttributes();
6550
return (
@@ -85,7 +70,4 @@ Image.propTypes = {
8570

8671
};
8772
Image.defaultProps = {};
88-
Image.contextTypes = {
89-
cloudName: PropTypes.string,
90-
angle: PropTypes.string
91-
};
73+
Image.contextTypes = CloudinaryComponent.contextTypes;

stories/index.js

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import CloudinaryContext from '../src/components/CloudinaryContext';
66

77
let CLImage = Image;
88

9-
storiesOf('Cloudinary', module)
9+
storiesOf('Image', module)
1010
.add('image', ()=> {
1111
let t = { width: 0.5, crop: "scale"};
1212
return (
@@ -18,35 +18,47 @@ storiesOf('Cloudinary', module)
1818
<Image cloudName="demo" publicId="does-not-exist" alt="This image is intentionally missing" transformation={t}/>
1919
)
2020
})
21+
.add('image with html attributes', ()=> {
22+
return(
23+
<Image cloudName="demo" publicId="sample" html_width="100"/>
24+
)
25+
})
2126
.add('image with style', ()=> {
2227
let t = { width: 0.5, crop: "scale"};
2328
return(
2429
<Image cloudName="demo" publicId="sample" style={{border: "20px solid"}} transformation={t}/>
2530
)
26-
})
31+
});
32+
storiesOf('Video', module)
2733
.add('Video', ()=> {
2834
return (
2935
<Video />
30-
)})
36+
)});
37+
storiesOf('CloudinaryContext', module)
3138
.add('CloudinaryContext', ()=> {
3239
let t = { width: 0.5, crop: "scale"};
3340
return (
3441
<CloudinaryContext cloudName="demo" >
35-
<ul><li>
42+
<div><span>Inside a div: </span>
3643
<Image publicId="sample" transformation={t} width="50"/>
37-
</li></ul>
44+
</div>
3845

3946
<Image publicId="sample" transformation={t}/>
4047
</CloudinaryContext>
4148
)})
42-
.add('CloudinaryContext - nested', ()=> {
49+
.add('Nested Context', ()=> {
4350
let t = { width: 0.5, crop: "scale"};
4451
return (
45-
<CloudinaryContext cloudName="demo" angle="30">
46-
<Image publicId="sample" width="50"/>
47-
<CloudinaryContext cloudName="demo" angle="20" width="100">
48-
<Image publicId="sample" transformation={t}/>
49-
</CloudinaryContext>
52+
<CloudinaryContext cloudName="demo" width="50" crop="scale">
53+
<Image publicId="sample"/>
54+
<Image publicId="sample" radius="100"/>
55+
<Image publicId="sample" angle="0"/>
56+
<CloudinaryContext angle="20" width="100" style={{border: "2px solid"}}>
57+
<span>Inner Context</span>
58+
<Image publicId="sample"/>
59+
<Image publicId="sample" radius="100"/>
60+
<Image publicId="sample" angle="0"/>
61+
</CloudinaryContext>
5062
</CloudinaryContext>
5163
)})
5264
;

0 commit comments

Comments
 (0)