├── .gitignore
├── LICENSE
├── README.md
├── lib
└── react-cloudinary-uploader.js
├── package.json
├── public
└── index.html
└── server.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Commenting this out is preferred by some people, see
24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25 | node_modules
26 |
27 | # Users Environment Variables
28 | .lock-wscript
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Domenico Solazzo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-cloudinary-uploader
2 | React File Uploader for Cloudinary.
3 | This React component wraps the Cloudinary Widget.It wraps all the properties of both the widget and the returned results in the state of the React component.
4 |
5 | # Installation
6 | ==============
7 | You need to [include the Cloudinary script](http://goo.gl/VnTE0q) at the bottom of your page, but before loading your React app.
8 |
9 | * npm install
10 | * node server.js
11 |
12 | # Configuration
13 | ===============
14 | The component accepts few properties as input. cloudName and uploadPreset are required properties for this component.
15 | * **cloudName**: the cloud name that you can find in your configuration in Cloudinary.
16 | * **uploadPreset**: The upload_preset that you can find in your settins (Upload) in Cloudinary.
17 | * **showPoweredBy** [true | false]: It shows the poweredBy logo in the widget.
18 | * **allowedFormats**: An array of allowed format. (e.g. ['jpeg', 'png'])
19 | * **maxFileSize**: If specified, perform client side validation that prevents uploading files bigger than the given bytes size (e.g. 130000)
20 | * **maxImageWidth**: If specified, client-side scale-down resizing takes place before uploading if the width of the selected file is bigger than the specified value. (e.g. 2000)
21 | * **maxImageHeight**: If specified, client-side scale-down resizing takes place before uploading if the height of the selected file is bigger than the specified value. (e.g. 2000)
22 | * **sources** ["local", "web", "web"]: List of file sources
23 | * **defaultSource**: The default selected source tab when the widget is opened.
24 | * **multiple**: Whether selecting and uploading multiple images is allowed.
25 | * **maxFiles**: The maximum number of files allowed in multiple upload mode.
26 | * **cropping** ["server" | null]: Whether to enable interactive cropping of images before uploading.
27 | * **croppingAspectRatio**: If specified, enforce the given aspect ratio on selected region when performing interactive cropping. (e.g. 0.5)
28 | * **publicId**: Custom public ID to assign to a single uploaded image.
29 | * **folder**: Folder name for all uploaded images. Acts as the prefix of assigned public IDs.
30 | * **tags**: One or more tags to assign to the uploaded images.
31 | * **resourceType** ["auto", "image", "raw"]: The resource type of the uploaded files.
32 | * **contextAlt**: Additional context metadata to attach to the uploaded images.
33 | * **contextCaption**: Additional context metadata to attach to the uploaded images.
34 | * **buttonClass**: Allows overriding the default CSS class name of the upload button added to your site.
35 | * **buttonCaption**: Allows overriding the default caption of the upload button added to your site.
36 |
37 | # State
38 | =======
39 | The React component keeps track of the information returned from the upload.
40 | * **bytes**: Bytes of the image
41 | * **created_at**: Last modification date of the image
42 | * **etag**: Etag of the image
43 | * **format**: Format of the image
44 | * **height**: Height of the image
45 | * **width**: Width of the image,
46 | * **path**: Image path,
47 | * **public_id**: Image public id,
48 | * **resource_type**: Resource type,
49 | * **secure_url**: Secure URL for the image,
50 | * **signature**: Signature for the image,
51 | * **tags**: List of tags connected to the image
52 | * **thumbnail_url**: Thumbnail url
53 | * **type**: Image type
54 | * **url**: Image URL
55 | * **version**: Version of the image
56 | * **isError**: True if there was an error during upload, false otherwise
57 | * **errorMessage**: The error message in case there was an error
58 | * **uuid**: Unique identifier for the widget
59 |
60 | ## WIP
61 | * Clean up some state properties
62 | * Allow multiple upload
63 | * Allow basic transformations
64 |
--------------------------------------------------------------------------------
/lib/react-cloudinary-uploader.js:
--------------------------------------------------------------------------------
1 |
2 | var ReactCloudinaryUploader = React.createClass({
3 | propTypes:{
4 | cloudName: React.PropTypes.string.isRequired,
5 | uploadPreset: React.PropTypes.string.isRequired,
6 | showPoweredBy: React.PropTypes.bool,
7 | allowedFormats: React.PropTypes.array,
8 | maxFileSize: React.PropTypes.number,
9 | maxImageWidth: React.PropTypes.number,
10 | maxImageHeight: React.PropTypes.number,
11 | sources: React.PropTypes.arrayOf(React.PropTypes.string),
12 | defaultSource: React.PropTypes.string,
13 | multiple: React.PropTypes.bool,
14 | maxFiles: React.PropTypes.number,
15 | cropping: React.PropTypes.string,
16 | croppingAspectRatio: React.PropTypes.number,
17 | publicId: React.PropTypes.string,
18 | folder: React.PropTypes.string,
19 | tags: React.PropTypes.arrayOf(React.PropTypes.string),
20 | resourceType: React.PropTypes.string,
21 | contextAlt: React.PropTypes.string,
22 | contextCaption: React.PropTypes.string,
23 | buttonClass: React.PropTypes.string,
24 | buttonCaption: React.PropTypes.string
25 | },
26 | getDefaultProps: function(){
27 | return {
28 | showPoweredBy: false,
29 | sources: ['local', 'url', 'camera'],
30 | defaultSource: 'local',
31 | multiple: false,
32 | maxFiles: null,
33 | cropping: null,
34 | croppingAspectRation: null,
35 | publicId: null,
36 | folder: null,
37 | tags: null,
38 | resourceType: 'auto',
39 | contextAlt: null,
40 | contextCaption: null,
41 | allowedFormats: ['png', 'gif', 'jpeg'],
42 | maxFileSize: null,
43 | maxImageWidth: null,
44 | maxImageHeight: null,
45 | buttonClass: 'cloudinary-button',
46 | buttonCaption: 'Upload image'
47 | }
48 | },
49 | getInitialState: function(){
50 | var initialState = {
51 | cloudName: this.props.cloudName,
52 | uploadPreset: this.props.uploadPreset,
53 | bytes: null,
54 | created_at: null,
55 | etag: null,
56 | format: null,
57 | height: null,
58 | width: null,
59 | path: null,
60 | public_id: null,
61 | resource_type: null,
62 | secure_url: null,
63 | signature: null,
64 | tags: [],
65 | thumbnail_url: null,
66 | type:null,
67 | url: null,
68 | version: null,
69 | isError: false,
70 | errorMessage: null,
71 | uuid: undefined,
72 | showPoweredBy: false,
73 | allowedFormats: null,
74 | uuid: this.uuid()
75 | };
76 | return initialState;
77 | },
78 | uuid: function(){
79 | function guid() {
80 | function s4() {
81 | return Math.floor((1 + Math.random()) * 0x10000)
82 | .toString(16)
83 | .substring(1);
84 | }
85 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
86 | s4() + '-' + s4() + s4() + s4();
87 | }
88 | return guid();
89 | },
90 | getUploadOptions: function(){
91 | var options = {
92 | cloud_name: this.state.cloudName,
93 | upload_preset: this.state.uploadPreset
94 | };
95 | options.sources = this.props.sources;
96 | options.multiple = this.props.multiple;
97 |
98 | if(this.props.maxFiles){
99 | options.max_files = this.props.maxFiles
100 | }
101 |
102 | if(this.props.cropping && this.props.cropping === 'server'){
103 | options.cropping = this.props.cropping;
104 | }
105 |
106 | if(this.croppingAspectRatio){
107 | options.cropping_aspect_ratio = this.props.croppingAspectRatio;
108 | }
109 |
110 | if(this.props.publicId){
111 | options.public_id = this.props.public_id;
112 | }
113 |
114 | if(this.props.folder){
115 | options.folder = this.props.folder;
116 | }
117 |
118 | if(this.props.tags && this.props.tags.length > 0){
119 | options.tags = this.props.tags;
120 | }
121 |
122 | if(this.props.resourceType){
123 | options.resourceType = this.props.resourceType;
124 | }
125 |
126 | if(this.props.allowedFormats){
127 | options.allowedFormats = this.props.allowedFormats
128 | }
129 |
130 | var context = {};
131 | if(this.props.contextAlt){
132 | context.alt = this.props.contextAlt;
133 | }
134 |
135 | if(this.props.contextCaption){
136 | context.caption = this.props.contextCaption;
137 | }
138 |
139 | if(Object.keys(context).length > 0){
140 | options.context = context;
141 | }
142 |
143 | return options;
144 | },
145 | setError: function(isError, errorMessage){
146 | self.setState({
147 | isError: true,
148 | errorMessage: 'No result returned from Cloudinary'
149 | });
150 | },
151 | setUploadResult: function(uploadedImage){
152 | this.setState({
153 | bytes: uploadedImage.bytes,
154 | createdAt: uploadedImage.created_at,
155 | etag: uploadedImage.etag,
156 | format: uploadedImage.format,
157 | height: uploadedImage.height,
158 | path: uploadedImage.path,
159 | publicId: uploadedImage.public_id,
160 | resourceType: uploadedImage.resource_type,
161 | secureUrl: uploadedImage.secure_url,
162 | signature: uploadedImage.signature,
163 | tags: uploadedImage.tags,
164 | thumbnailUrl: uploadedImage.thumbnail_url,
165 | type: uploadedImage.type,
166 | url: uploadedImage.url,
167 | version: uploadedImage.version,
168 | width: uploadedImage.width
169 | });
170 | },
171 | handleClick: function(ev){
172 | self = this;
173 | try{
174 | var options = this.getUploadOptions();
175 | cloudinary.openUploadWidget(
176 | options,
177 | function(error, result) {
178 | if (error){
179 | self.setError(true, error)
180 | return false;
181 | }
182 |
183 | if (!result || result.length === 0){
184 | self.setError(true, 'No result from Cloudinary');
185 | return false;
186 | }
187 | var uploadedImage = result[0];
188 | self.setUploadResult(uploadedImage);
189 | return true;
190 | }
191 | );
192 | }catch(e){
193 | self.setError(true, e);
194 | return false;
195 | }
196 |
197 | },
198 | render: function(){
199 | var uploader_id = "uploader_" + this.state.uuid;
200 | var image = this.state.thumbnailUrl ? this.state.thumbnailUrl: '#';
201 | return (
202 |
205 |