Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cloudinary-react

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cloudinary-react - npm Package Compare versions

Comparing version 1.5.1 to 1.6.0

lib/components/Placeholder/package.json

14

CHANGELOG.md

@@ -0,1 +1,15 @@

1.6.0 / 2020-07-06
==================
New functionality and features
------------------------------
* Add support for lazy loading images, placeholder and accessibility (#162)
Other Changes
-------------
* Update cloudinary-core dependency to version 2.10.1
* Refactor Image component (#167)
* Update tests to fail when component is not exported
* Add test for variable names (#164)
1.5.1 / 2020-06-21

@@ -2,0 +16,0 @@ ==================

125

lib/components/CloudinaryComponent/CloudinaryComponent.js

@@ -52,2 +52,12 @@ "use strict";

/**
* Check if given component is a Cloudinary Component with given displayName
* @param component the component to check
* @param displayName of wanted component
* @return {boolean}
*/
var isCloudinaryComponent = function isCloudinaryComponent(component, displayName) {
return !!( /*#__PURE__*/_react["default"].isValidElement(component) && component.type && component.type.displayName === displayName);
};
/**
* Return a new object containing keys and values where keys are in the keys list

@@ -59,2 +69,3 @@ * @param {object} source Object to copy values from

function only(source) {

@@ -86,3 +97,3 @@ var keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

function CloudinaryComponent(props, context) {
function CloudinaryComponent(_props, _context) {
var _this;

@@ -92,4 +103,20 @@

_this = _super.call(this, props, context);
_this.getContext = _this.getContext.bind(_assertThisInitialized(_this));
_this = _super.call(this, _props, _context);
_defineProperty(_assertThisInitialized(_this), "getContext", function () {
return _this.context || {};
});
_defineProperty(_assertThisInitialized(_this), "onIntersect", function () {
_this.setState({
isInView: true
});
});
_defineProperty(_assertThisInitialized(_this), "getExtendedProps", function () {
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.props;
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.getContext();
return CloudinaryComponent.normalizeOptions(context, props);
});
return _this;

@@ -104,5 +131,9 @@ }

}, {
key: "getContext",
value: function getContext() {
return this.context || {};
key: "getChildPlaceholder",
value: function getChildPlaceholder(children) {
if (children) {
return _react["default"].Children.toArray(children).find(function (child) {
return isCloudinaryComponent(child, "CloudinaryPlaceholder");
});
}
}

@@ -114,19 +145,10 @@ }, {

if (children === undefined || children === null) return null;
var result = children ? _react["default"].Children.toArray(children).filter(function (child) {
return isCloudinaryComponent(child, "CloudinaryTransformation");
}).map(function (child) {
var options = CloudinaryComponent.normalizeOptions(child.props, child.context);
var mapped = _react["default"].Children.map(children, function (child) {
if (! /*#__PURE__*/_react["default"].isValidElement(child)) {
// child is not an element (e.g. simple text)
return;
}
var options = {};
if (child.type && child.type.exposesProps) {
options = CloudinaryComponent.normalizeOptions(child.props, child.context);
}
var childOptions = _this2.getChildTransformations(child.props.children);
if (childOptions !== undefined && childOptions !== null) {
if (childOptions) {
options.transformation = childOptions;

@@ -136,9 +158,4 @@ }

return options;
});
if (mapped != null) {
return mapped.filter(function (o) {
return !_cloudinaryCore.Util.isEmpty(o);
});
} else return null;
}) : [];
return result.length ? result : null;
}

@@ -157,3 +174,5 @@ /**

var children = extendedProps.children,
rest = _objectWithoutProperties(extendedProps, ["children"]);
accessibility = extendedProps.accessibility,
placeholder = extendedProps.placeholder,
rest = _objectWithoutProperties(extendedProps, ["children", "accessibility", "placeholder"]);

@@ -165,4 +184,14 @@ var ownTransformation = only(_cloudinaryCore.Util.withCamelCaseKeys(rest), _cloudinaryCore.Transformation.methods) || {};

ownTransformation.transformation = childrenOptions;
}
} //Append placeholder and accessibility if exists
var advancedTransformations = {
accessibility: accessibility,
placeholder: placeholder
};
Object.keys(advancedTransformations).filter(function (k) {
return advancedTransformations[k];
}).map(function (k) {
ownTransformation[k] = advancedTransformations[k];
});
return ownTransformation;

@@ -179,5 +208,15 @@ }

}, {
key: "getUrl",
key: "getConfiguredCloudinary",
/**
* Generated a configured Cloudinary object.
* @param extendedProps React props combined with custom Cloudinary configuration options
* @return {Cloudinary} configured using extendedProps
*/
value: function getConfiguredCloudinary(extendedProps) {
var options = _cloudinaryCore.Util.extractUrlParams(_cloudinaryCore.Util.withSnakeCaseKeys(extendedProps));
return Cloudinary["new"](options);
}
/**
* Generate a Cloudinary resource URL based on the options provided and child Transformation elements

@@ -188,11 +227,17 @@ * @param extendedProps React props combined with custom Cloudinary configuration options

*/
}, {
key: "getUrl",
value: function getUrl(extendedProps) {
var transformation = this.getTransformation(extendedProps);
var publicId = extendedProps.publicId;
var cl = getConfiguredCloudinary(extendedProps);
return cl.url(publicId, this.getTransformation(extendedProps));
}
/**
* Merges context & props
* @param props of this component
* @param context of this component
* @return {Object}
*/
var options = _cloudinaryCore.Util.extractUrlParams(_cloudinaryCore.Util.withSnakeCaseKeys(extendedProps));
var cl = _cloudinaryCore.Cloudinary["new"](options);
return cl.url(extendedProps.publicId, transformation);
}
}], [{

@@ -206,3 +251,3 @@ key: "normalizeOptions",

return options.reduce(function (left, right) {
for (var key in right) {
Object.keys(right || {}).forEach(function (key) {
var value = right[key];

@@ -213,4 +258,3 @@

}
}
});
return left;

@@ -228,3 +272,2 @@ }, {});

CloudinaryComponent.propTypes.publicId = _propTypes["default"].string;
CloudinaryComponent.propTypes.responsive = _propTypes["default"].bool;
/**

@@ -231,0 +274,0 @@ * Create a React type definition object. All items are PropTypes.string or [string] or object or [object].

@@ -8,6 +8,4 @@ "use strict";

var _react = _interopRequireDefault(require("react"));
var _react = _interopRequireWildcard(require("react"));
var _cloudinaryCore = _interopRequireWildcard(require("cloudinary-core"));
var _CloudinaryComponent2 = _interopRequireDefault(require("../CloudinaryComponent"));

@@ -17,2 +15,8 @@

var _cloudinaryCore = require("cloudinary-core");
var _propTypes = _interopRequireDefault(require("prop-types"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }

@@ -22,4 +26,2 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

@@ -29,6 +31,2 @@

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

@@ -38,4 +36,6 @@

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

@@ -61,11 +61,7 @@

var defaultBreakpoints = function defaultBreakpoints(width) {
var steps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100;
return steps * Math.ceil(width / steps);
};
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* A component representing a Cloudinary served image
*/
var Image = /*#__PURE__*/function (_CloudinaryComponent) {

@@ -82,270 +78,166 @@ _inherits(Image, _CloudinaryComponent);

_this = _super.call(this, props, context);
_this.handleResize = _this.handleResize.bind(_assertThisInitialized(_this));
_this.attachRef = _this.attachRef.bind(_assertThisInitialized(_this));
var state = {
responsive: false,
url: undefined,
breakpoints: defaultBreakpoints
};
_this.state = _objectSpread(_objectSpread({}, state), _this.prepareState(props, context));
return _this;
}
/**
* Retrieve the window or default view of the current element
* @returns {DocumentView|*}
* @private
*/
_defineProperty(_assertThisInitialized(_this), "isResponsive", function () {
return _this.props.responsive && _this.imgElement && _this.imgElement.current;
});
_createClass(Image, [{
key: "prepareState",
value: function prepareState() {
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props;
var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.getContext();
_defineProperty(_assertThisInitialized(_this), "getOptions", function () {
var extendedProps = _this.getExtendedProps();
var extendedProps = _CloudinaryComponent2["default"].normalizeOptions(context, props);
var _extendedProps$_this$ = _objectSpread(_objectSpread({}, extendedProps), _this.getTransformation(extendedProps)),
children = _extendedProps$_this$.children,
innerRef = _extendedProps$_this$.innerRef,
options = _objectWithoutProperties(_extendedProps$_this$, ["children", "innerRef"]);
var url = this.getUrl(extendedProps);
var state = {};
var updatedOptions = {};
return options;
});
if (extendedProps.breakpoints !== undefined) {
state.breakpoints = extendedProps.breakpoints;
}
_defineProperty(_assertThisInitialized(_this), "getAttributes", function () {
var additionalOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var isInView = _this.state.isInView;
var placeholder = additionalOptions.placeholder;
if (extendedProps.responsive) {
state.responsive = true;
updatedOptions = this.cloudinaryUpdate(url, state);
url = updatedOptions.url;
}
var options = _objectSpread(_objectSpread({}, _this.getOptions()), additionalOptions);
var currentState = this.state || {};
state.width = updatedOptions.width;
var _extractCloudinaryPro = (0, _Util.extractCloudinaryProps)(options),
nonCloudinaryProps = _extractCloudinaryPro.nonCloudinaryProps;
if (!_cloudinaryCore.Util.isEmpty(url) && url !== currentState.url) {
state.url = url;
}
var attributes = _objectSpread(_objectSpread({}, (0, _Util.getImageTag)(options).attributes()), nonCloudinaryProps); // Set placeholder Id
return state;
}
}, {
key: "attachRef",
value: function attachRef(element) {
this.element = element;
var innerRef = this.props.innerRef;
if (innerRef) {
if (innerRef instanceof Function) {
innerRef(element);
} else {
innerRef.current = element;
}
}
}
}, {
key: "handleResize",
value: function handleResize() {
var _this2 = this;
if (placeholder && attributes.id) {
attributes.id = attributes.id + '-cld-placeholder';
} // Remove unneeded attributes,
if (!this.props.responsive || this.rqf) return;
this.rqf = (0, _Util.requestAnimationFrame)(function () {
_this2.rqf = null;
var newState = _this2.prepareState();
["src", "accessibility", "placeholder"].forEach(function (attr) {
delete attributes[attr];
}); // Set src or data-src attribute
if (!_cloudinaryCore.Util.isEmpty(newState.url)) {
_this2.setState(newState);
}
});
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
// now that we have a this.element, we need to calculate the URL
this.handleResize();
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.element = undefined;
var srcAttrName = isInView || !_this.shouldLazyLoad(options) ? "src" : "data-src";
attributes[srcAttrName] = (0, _Util.getConfiguredCloudinary)(options).url(options.publicId, options);
return attributes;
});
if (this.listener) {
this.listener.cancel();
this.window && this.window.removeEventListener('resize', this.listener);
_defineProperty(_assertThisInitialized(_this), "update", function () {
if (_this.isResponsive()) {
var removeListener = (0, _Util.makeElementResponsive)(_this.imgElement.current, _this.getOptions());
_this.listenerRemovers.push(removeListener);
}
this.listener = undefined;
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
this.setState(this.prepareState());
if (_this.shouldLazyLoad(_this.getExtendedProps())) {
_cloudinaryCore.Util.detectIntersection(_this.imgElement.current, _this.onIntersect);
}
});
if (this.state.responsive) {
var wait = (0, _Util.firstDefined)(this.props.responsiveDebounce, this.getContext().responsiveDebounce, 100);
_defineProperty(_assertThisInitialized(_this), "attachRef", function (imgElement) {
var innerRef = _this.props.innerRef;
_this.imgElement.current = imgElement;
if (this.listener) {
this.window && this.window.removeEventListener('resize', this.listener);
if (innerRef) {
if (innerRef instanceof Function) {
innerRef(imgElement);
} else {
innerRef.current = imgElement;
}
this.listener = (0, _Util.debounce)(this.handleResize, wait);
this.window && this.window.addEventListener('resize', this.listener);
}
}
}, {
key: "render",
value: function render() {
var _CloudinaryComponent$ = _CloudinaryComponent2["default"].normalizeOptions(this.props, this.getContext()),
publicId = _CloudinaryComponent$.publicId,
responsive = _CloudinaryComponent$.responsive,
responsiveDebounce = _CloudinaryComponent$.responsiveDebounce,
children = _CloudinaryComponent$.children,
innerRef = _CloudinaryComponent$.innerRef,
options = _objectWithoutProperties(_CloudinaryComponent$, ["publicId", "responsive", "responsiveDebounce", "children", "innerRef"]);
});
var attributes = _cloudinaryCore["default"].Transformation["new"](options).toHtmlAttributes();
_defineProperty(_assertThisInitialized(_this), "shouldLazyLoad", function (_ref) {
var loading = _ref.loading;
return loading === "lazy" || loading === "auto";
});
var url = this.state.url;
return /*#__PURE__*/_react["default"].createElement("img", _extends({}, attributes, {
src: url,
ref: this.attachRef
}));
} // Methods from cloudinary_js
_defineProperty(_assertThisInitialized(_this), "handleImageLoaded", function () {
var onLoad = _this.props.onLoad;
}, {
key: "findContainerWidth",
value: function findContainerWidth() {
var containerWidth, style;
containerWidth = 0;
var element = this.element;
while ((0, _Util.isElement)(element = element != null ? element.parentNode : void 0) && !containerWidth) {
style = this.window ? this.window.getComputedStyle(element) : '';
if (!/^inline/.test(style.display)) {
containerWidth = _cloudinaryCore.Util.width(element);
_this.setState({
isLoaded: true
}, function () {
if (onLoad) {
onLoad();
}
}
});
});
return Math.round(containerWidth);
}
}, {
key: "applyBreakpoints",
value: function applyBreakpoints(width, steps, options) {
options = _CloudinaryComponent2["default"].normalizeOptions(this.getContext(), this.props, options);
var responsiveUseBreakpoints = options.responsiveUseBreakpoints;
_this.imgElement = /*#__PURE__*/(0, _react.createRef)();
_this.state = {
isLoaded: false
};
_this.listenerRemovers = [];
return _this;
}
/**
* @return true when this image element should be made responsive, false otherwise.
*/
if (!responsiveUseBreakpoints || responsiveUseBreakpoints === 'resize' && !options.resizing) {
return width;
} else {
return this.calc_breakpoint(width, steps);
}
}
}, {
key: "calc_breakpoint",
value: function calc_breakpoint(width, steps) {
var breakpoints, point;
breakpoints = this.state && this.state.breakpoints || defaultBreakpoints;
if (_cloudinaryCore.Util.isFunction(breakpoints)) {
return breakpoints(width, steps);
} else {
if (_cloudinaryCore.Util.isString(breakpoints)) {
breakpoints = function () {
var j, len, ref, results;
ref = breakpoints.split(',');
results = [];
_createClass(Image, [{
key: "componentDidMount",
for (j = 0, len = ref.length; j < len; j++) {
point = ref[j];
results.push(parseInt(point));
}
return results;
}().sort(function (a, b) {
return a - b;
});
}
return (0, _Util.closestAbove)(breakpoints, width);
}
/**
* Invoked immediately after a component is mounted (inserted into the tree)
*/
value: function componentDidMount() {
this.update();
}
}, {
key: "device_pixel_ratio",
value: function device_pixel_ratio() {
var roundDpr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var dpr, dprString;
dpr = (typeof this.window !== "undefined" && this.window !== null ? this.window.devicePixelRatio : void 0) || 1;
/**
* Invoked immediately after updating occurs. This method is not called for the initial render.
*/
if (roundDpr) {
dpr = Math.ceil(dpr);
}
if (dpr <= 0 || isNaN(dpr)) {
dpr = 1;
}
dprString = dpr.toString();
if (dprString.match(/^\d+$/)) {
dprString += '.0';
}
return dprString;
}
}, {
key: "updateDpr",
value: function updateDpr(dataSrc, roundDpr) {
return dataSrc.replace(/\bdpr_(1\.0|auto)\b/g, 'dpr_' + this.device_pixel_ratio(roundDpr));
key: "componentDidUpdate",
value: function componentDidUpdate() {
this.update();
}
}, {
key: "maxWidth",
value: function maxWidth(requiredWidth) {
return Math.max(this.state && this.state.width || 0, requiredWidth);
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.listenerRemovers.forEach(function (removeListener) {
removeListener();
});
}
/**
* Updates this Image's isLoaded state,
* And fires props.onLoad if exists.
*/
}, {
key: "cloudinaryUpdate",
value: function cloudinaryUpdate(url) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var requiredWidth;
var match;
var resultUrl = this.updateDpr(url, options.roundDpr);
key: "render",
value: function render() {
var attachRef = this.attachRef,
getAttributes = this.getAttributes,
handleImageLoaded = this.handleImageLoaded;
if (options.responsive || this.state && this.state.responsive) {
var containerWidth = this.findContainerWidth();
var _this$getExtendedProp = this.getExtendedProps(),
children = _this$getExtendedProp.children;
if (containerWidth !== 0) {
if (/w_auto:breakpoints/.test(resultUrl)) {
requiredWidth = this.maxWidth(containerWidth, this.element);
resultUrl = resultUrl.replace(/w_auto:breakpoints([_0-9]*)(:[0-9]+)?/, "w_auto:breakpoints$1:" + requiredWidth);
} else {
match = /w_auto(:(\d+))?/.exec(resultUrl);
var placeholder = this.getChildPlaceholder(children);
var isLoaded = this.state.isLoaded;
var attributes = getAttributes(); //If image wasn't loaded and there's a placeholder then we render it alongside the image.
if (match) {
requiredWidth = this.applyBreakpoints(containerWidth, match[2], options);
requiredWidth = this.maxWidth(requiredWidth, this.element);
resultUrl = resultUrl.replace(/w_auto[^,\/]*/g, "w_" + requiredWidth);
}
}
} else {
resultUrl = "";
}
if (!isLoaded && placeholder) {
var placeholderStyle = {
display: isLoaded ? 'none' : 'inline'
};
attributes.style = _objectSpread(_objectSpread({}, attributes.style || {}), {}, {
opacity: 0,
position: 'absolute'
});
attributes.onLoad = handleImageLoaded;
var placeholderAttributes = getAttributes({
placeholder: placeholder.props.type
});
return /*#__PURE__*/_react["default"].createElement(_react.Fragment, null, /*#__PURE__*/_react["default"].createElement("img", _extends({
ref: attachRef
}, attributes)), /*#__PURE__*/_react["default"].createElement("div", {
style: placeholderStyle
}, /*#__PURE__*/_react["default"].createElement("img", placeholderAttributes)));
}
return {
url: resultUrl,
width: requiredWidth
};
return /*#__PURE__*/_react["default"].createElement("img", _extends({
ref: attachRef
}, attributes));
}
}, {
key: "window",
get: function get() {
var windowRef = null;
if (typeof window !== "undefined") {
windowRef = window;
}
return this.element && this.element.ownerDocument ? this.element.ownerDocument.defaultView || windowRef : windowRef;
}
}]);

@@ -358,3 +250,6 @@

Image.propTypes = _CloudinaryComponent2["default"].propTypes;
Image.propTypes.responsive = _propTypes["default"].bool;
Image.propTypes.loading = _propTypes["default"].string;
Image.propTypes.accessibility = _propTypes["default"].string;
var _default = Image;
exports["default"] = _default;

@@ -63,3 +63,4 @@ "use strict";

Transformation.exposesProps = true;
Transformation.displayName = "CloudinaryTransformation";
var _default = Transformation;
exports["default"] = _default;

@@ -36,2 +36,8 @@ "use strict";

});
Object.defineProperty(exports, "Placeholder", {
enumerable: true,
get: function get() {
return _Placeholder["default"];
}
});

@@ -50,2 +56,4 @@ var _react = _interopRequireDefault(require("react"));

var _Placeholder = _interopRequireDefault(require("./components/Placeholder"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

@@ -18,3 +18,3 @@ "use strict";

var CLOUDINARY_PROPS = _cloudinaryCore.Transformation.PARAM_NAMES.map(_cloudinaryCore.Util.camelCase).reduce(function (accumulator, cloudinaryPropName) {
var CLOUDINARY_PROPS = _cloudinaryCore.Transformation.PARAM_NAMES.map(_cloudinaryCore.Util.camelCase).concat(['publicId', 'breakpoints']).reduce(function (accumulator, cloudinaryPropName) {
accumulator[cloudinaryPropName] = true;

@@ -21,0 +21,0 @@ return accumulator;

@@ -6,38 +6,5 @@ "use strict";

});
Object.defineProperty(exports, "debounce", {
enumerable: true,
get: function get() {
return _debounce2["default"];
}
});
Object.defineProperty(exports, "firstDefined", {
enumerable: true,
get: function get() {
return _firstDefined2["default"];
}
});
Object.defineProperty(exports, "closestAbove", {
enumerable: true,
get: function get() {
return _closestAbove2["default"];
}
});
Object.defineProperty(exports, "requestAnimationFrame", {
enumerable: true,
get: function get() {
return _requestAnimationFrame.requestAnimationFrame;
}
});
Object.defineProperty(exports, "cancelAnimationFrame", {
enumerable: true,
get: function get() {
return _requestAnimationFrame.cancelAnimationFrame;
}
});
Object.defineProperty(exports, "isElement", {
enumerable: true,
get: function get() {
return _isElement2["default"];
}
});
var _exportNames = {
extractCloudinaryProps: true
};
Object.defineProperty(exports, "extractCloudinaryProps", {

@@ -50,14 +17,17 @@ enumerable: true,

var _debounce2 = _interopRequireDefault(require("./debounce"));
var _extractCloudinaryProps2 = _interopRequireDefault(require("./extractCloudinaryProps"));
var _firstDefined2 = _interopRequireDefault(require("./firstDefined"));
var _cloudinaryCoreUtils = require("./cloudinaryCoreUtils");
var _closestAbove2 = _interopRequireDefault(require("./closestAbove"));
Object.keys(_cloudinaryCoreUtils).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _cloudinaryCoreUtils[key];
}
});
});
var _requestAnimationFrame = require("./requestAnimationFrame");
var _isElement2 = _interopRequireDefault(require("./isElement"));
var _extractCloudinaryProps2 = _interopRequireDefault(require("./extractCloudinaryProps"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
{
"name": "cloudinary-react",
"version": "1.5.1",
"version": "1.6.0",
"description": "Present Cloudinary images and videos using React components",

@@ -8,7 +8,9 @@ "main": "lib/index.js",

"test": "node_modules/.bin/mocha --require @babel/register test/.setup.js --recursive test",
"test:all": "run-s build test test-dist test-lib",
"test:all": "run-s build test test-dist test-lib test:e2e",
"test-dist": "TEST_SUBJECT=dist node_modules/.bin/mocha --require @babel/register test/.setup.js --recursive test",
"test-lib": "TEST_SUBJECT=lib node_modules/.bin/mocha --require @babel/register test/.setup.js --recursive test",
"pretest:e2e": "npm run build && npm pack && cpy cloudinary-react-*.tgz e2e-test --rename=cloudinary-react.tgz",
"test:e2e": "cd ./e2e-test && npm run test",
"prebuild": "node_modules/.bin/babel src --out-dir lib --copy-files ",
"build": "node_modules/.bin/webpack && npm run build-storybook && npm run bundlewatch",
"build": "node_modules/.bin/webpack && npm run bundlewatch",
"bundlewatch": "bundlewatch --config ./bundlewatch.config.js",

@@ -47,2 +49,3 @@ "storybook": "start-storybook -p 6006",

"chai-string": "^1.4.0",
"cpy-cli": "^3.1.1",
"del-cli": "^3.0.0",

@@ -52,3 +55,3 @@ "enzyme": "^3.10.0",

"jsdom": "^11.12.0",
"mocha": "^4.0.1",
"mocha": "^8.0.1",
"npm-run-all": "^4.1.5",

@@ -60,3 +63,3 @@ "react-dom": "^16.3.3",

"dependencies": {
"cloudinary-core": "^2.9.0",
"cloudinary-core": "^2.10.1",
"prop-types": "^15.6.2"

@@ -66,3 +69,8 @@ },

"react": "^16.3.3"
}
},
"files": [
"src",
"lib",
"dist"
]
}
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Cloudinary, Transformation, Util} from 'cloudinary-core';
import {Transformation, Util} from 'cloudinary-core';
import {CloudinaryContextType} from '../CloudinaryContext/CloudinaryContextType';
const camelCase = Util.camelCase;
const {camelCase} = Util;
/**
* Check if given component is a Cloudinary Component with given displayName
* @param component the component to check
* @param displayName of wanted component
* @return {boolean}
*/
const isCloudinaryComponent = (component, displayName) => {
return !!(React.isValidElement(component)
&& component.type
&& component.type.displayName === displayName);
};
/**
* Return a new object containing keys and values where keys are in the keys list

@@ -15,3 +27,3 @@ * @param {object} source Object to copy values from

function only(source, keys = []) {
if(!source){
if (!source) {
return source;

@@ -35,3 +47,2 @@ }

super(props, context);
this.getContext = this.getContext.bind(this);
}

@@ -43,26 +54,33 @@

getContext(){
getContext = () => {
return this.context || {};
}
/**
* React function: Called when this element is in view
*/
onIntersect = () =>{
this.setState({isInView: true})
}
getChildPlaceholder(children){
if (children) {
return React.Children.toArray(children)
.find(child => isCloudinaryComponent(child, "CloudinaryPlaceholder"));
}
}
getChildTransformations(children) {
if(children === undefined || children === null) return null;
let mapped = React.Children.map(children, child =>{
if (!React.isValidElement(child)) {
// child is not an element (e.g. simple text)
return;
}
let options = {};
if (child.type && child.type.exposesProps){
options = CloudinaryComponent.normalizeOptions(child.props, child.context);
}
let childOptions = this.getChildTransformations(child.props.children);
if(childOptions !== undefined && childOptions !== null){
options.transformation = childOptions;
}
return options;
});
if(mapped != null){
return mapped.filter(o=>!Util.isEmpty(o));
} else return null;
let result = children ? React.Children.toArray(children)
.filter(child => isCloudinaryComponent(child, "CloudinaryTransformation"))
.map(child => {
const options = CloudinaryComponent.normalizeOptions(child.props, child.context);
const childOptions = this.getChildTransformations(child.props.children);
if (childOptions) {
options.transformation = childOptions;
}
return options;
}) : [];
return result.length ? result : null;
}

@@ -78,3 +96,3 @@

getTransformation(extendedProps) {
let {children, ...rest} = extendedProps;
let {children, accessibility, placeholder, ...rest} = extendedProps;
let ownTransformation = only(Util.withCamelCaseKeys(rest), Transformation.methods) || {};

@@ -85,2 +103,9 @@ let childrenOptions = this.getChildTransformations(children);

}
//Append placeholder and accessibility if exists
const advancedTransformations = {accessibility, placeholder};
Object.keys(advancedTransformations).filter(k=>advancedTransformations[k]).map(k=>{
ownTransformation[k] = advancedTransformations[k];
});
return ownTransformation;

@@ -97,4 +122,4 @@ }

static normalizeOptions(...options) {
return options.reduce((left, right)=> {
for (let key in right) {
return options.reduce((left, right) => {
Object.keys(right || {}).forEach(key => {
let value = right[key];

@@ -104,3 +129,3 @@ if (value !== null && value !== undefined) {

}
}
});
return left;

@@ -112,2 +137,12 @@ }

/**
* Generated a configured Cloudinary object.
* @param extendedProps React props combined with custom Cloudinary configuration options
* @return {Cloudinary} configured using extendedProps
*/
getConfiguredCloudinary(extendedProps){
const options = Util.extractUrlParams(Util.withSnakeCaseKeys(extendedProps));
return Cloudinary.new(options);
}
/**
* Generate a Cloudinary resource URL based on the options provided and child Transformation elements

@@ -119,8 +154,17 @@ * @param extendedProps React props combined with custom Cloudinary configuration options

getUrl(extendedProps) {
let transformation = this.getTransformation(extendedProps);
let options = Util.extractUrlParams(Util.withSnakeCaseKeys(extendedProps));
let cl = Cloudinary.new(options);
return cl.url(extendedProps.publicId, transformation);
const {publicId} = extendedProps;
const cl = getConfiguredCloudinary(extendedProps);
return cl.url(publicId, this.getTransformation(extendedProps));
}
/**
* Merges context & props
* @param props of this component
* @param context of this component
* @return {Object}
*/
getExtendedProps = (props = this.props, context = this.getContext()) => {
return CloudinaryComponent.normalizeOptions(context, props);
};
static contextType = CloudinaryContextType;

@@ -131,3 +175,2 @@ }

CloudinaryComponent.propTypes.publicId = PropTypes.string;
CloudinaryComponent.propTypes.responsive = PropTypes.bool;

@@ -143,3 +186,3 @@ /**

const types = {};
for (let i =0; i < configParams.length; i++) {
for (let i = 0; i < configParams.length; i++) {
const key = configParams[i];

@@ -146,0 +189,0 @@ types[camelCase(key)] = PropTypes.any;

@@ -1,10 +0,7 @@

import React from 'react';
import cloudinary, {Util} from 'cloudinary-core';
import React, {createRef, Fragment} from 'react';
import CloudinaryComponent from '../CloudinaryComponent';
import {debounce, firstDefined, closestAbove, requestAnimationFrame, isElement} from '../../Util';
import {extractCloudinaryProps, getImageTag, makeElementResponsive, getConfiguredCloudinary} from "../../Util";
import {Util} from "cloudinary-core";
import PropTypes from "prop-types";
const defaultBreakpoints = (width, steps = 100) => {
return steps * Math.ceil(width / steps);
};
/**

@@ -16,205 +13,147 @@ * A component representing a Cloudinary served image

super(props, context);
this.handleResize = this.handleResize.bind(this);
this.attachRef = this.attachRef.bind(this);
this.imgElement = createRef();
this.state = {isLoaded: false}
this.listenerRemovers = [];
}
let state = {responsive: false, url: undefined, breakpoints: defaultBreakpoints};
this.state = {...state, ...this.prepareState(props, context)};
/**
* @return true when this image element should be made responsive, false otherwise.
*/
isResponsive = () => {
return this.props.responsive && this.imgElement && this.imgElement.current;
}
/**
* Retrieve the window or default view of the current element
* @returns {DocumentView|*}
* @private
* @return merged props & context with aggregated transformation, excluding children and innerRef.
*/
get window() {
let windowRef = null;
if (typeof window !== "undefined") {
windowRef = window
}
return (this.element && this.element.ownerDocument) ? (this.element.ownerDocument.defaultView || windowRef) : windowRef;
getOptions = () => {
const extendedProps = this.getExtendedProps();
const {children, innerRef, ...options} = {...extendedProps, ...this.getTransformation(extendedProps)};
return options;
}
prepareState(props = this.props, context = this.getContext()) {
let extendedProps = CloudinaryComponent.normalizeOptions(context, props);
let url = this.getUrl(extendedProps);
let state = {};
let updatedOptions = {};
/**
* @param additionalOptions - extra options to pass to cloudinary.url(), for example: placeholder
* @return attributes for the underlying <img> element.
*/
getAttributes = (additionalOptions = {}) => {
const {isInView} = this.state;
const {placeholder} = additionalOptions;
const options = {...this.getOptions(), ...additionalOptions};
const {nonCloudinaryProps} = extractCloudinaryProps(options);
if (extendedProps.breakpoints !== undefined) {
state.breakpoints = extendedProps.breakpoints;
let attributes = {...getImageTag(options).attributes(), ...nonCloudinaryProps};
// Set placeholder Id
if (placeholder && attributes.id) {
attributes.id = attributes.id + '-cld-placeholder';
}
if (extendedProps.responsive) {
state.responsive = true;
updatedOptions = this.cloudinaryUpdate(url, state);
url = updatedOptions.url;
}
let currentState = this.state || {};
// Remove unneeded attributes,
["src", "accessibility", "placeholder"].forEach(attr => {
delete attributes[attr];
});
state.width = updatedOptions.width;
// Set src or data-src attribute
const srcAttrName = isInView || !this.shouldLazyLoad(options) ? "src" : "data-src";
attributes[srcAttrName] = getConfiguredCloudinary(options).url(options.publicId, options);
if (!Util.isEmpty(url) && url !== currentState.url) {
state.url = url;
return attributes;
}
/**
* Update this image using cloudinary-core
*/
update = () => {
if (this.isResponsive()) {
const removeListener = makeElementResponsive(this.imgElement.current, this.getOptions());
this.listenerRemovers.push(removeListener);
}
return state;
if (this.shouldLazyLoad(this.getExtendedProps())) {
Util.detectIntersection(this.imgElement.current, this.onIntersect);
}
}
attachRef(element) {
this.element = element;
/**
* Attach both this.imgElement and props.innerRef as ref to the given element
* @param imgElement - the element to attach a ref to
*/
attachRef = (imgElement) => {
const {innerRef} = this.props;
this.imgElement.current = imgElement;
if (innerRef) {
if (innerRef instanceof Function) {
innerRef(element);
innerRef(imgElement);
} else {
innerRef.current = element;
innerRef.current = imgElement;
}
}
}
};
handleResize() {
if (!this.props.responsive || this.rqf) return;
this.rqf = requestAnimationFrame(() => {
this.rqf = null;
let newState = this.prepareState();
if (!Util.isEmpty(newState.url)) {
this.setState(newState);
}
});
shouldLazyLoad = ({loading}) => {
return loading === "lazy" || loading === "auto";
}
/**
* Invoked immediately after a component is mounted (inserted into the tree)
*/
componentDidMount() {
// now that we have a this.element, we need to calculate the URL
this.handleResize();
this.update();
}
/**
* Invoked immediately after updating occurs. This method is not called for the initial render.
*/
componentDidUpdate() {
this.update();
}
componentWillUnmount() {
this.element = undefined;
if (this.listener) {
this.listener.cancel();
this.window && this.window.removeEventListener('resize', this.listener);
}
this.listener = undefined;
this.listenerRemovers.forEach(removeListener => {
removeListener();
});
}
componentDidUpdate(prevProps) {
this.setState(this.prepareState());
if (this.state.responsive) {
const wait = firstDefined(this.props.responsiveDebounce, this.getContext().responsiveDebounce, 100);
if (this.listener) {
this.window && this.window.removeEventListener('resize', this.listener);
/**
* Updates this Image's isLoaded state,
* And fires props.onLoad if exists.
*/
handleImageLoaded = () => {
const {onLoad} = this.props;
this.setState({isLoaded: true}, () => {
if (onLoad) {
onLoad();
}
this.listener = debounce(this.handleResize, wait);
this.window && this.window.addEventListener('resize', this.listener);
}
}
});
};
render() {
const {publicId, responsive, responsiveDebounce, children, innerRef, ...options} =
CloudinaryComponent.normalizeOptions(this.props, this.getContext());
const attributes = cloudinary.Transformation.new(options).toHtmlAttributes();
const {url} = this.state;
return (
<img {...attributes} src={url} ref={this.attachRef}/>
);
}
const {attachRef, getAttributes, handleImageLoaded} = this;
const {children} = this.getExtendedProps();
const placeholder = this.getChildPlaceholder(children);
const {isLoaded} = this.state;
// Methods from cloudinary_js
const attributes = getAttributes();
findContainerWidth() {
var containerWidth, style;
containerWidth = 0;
let element = this.element;
while (isElement((element = element != null ? element.parentNode : void 0)) && !containerWidth) {
style = this.window ? this.window.getComputedStyle(element) : '';
if (!/^inline/.test(style.display)) {
containerWidth = Util.width(element);
}
}
return Math.round(containerWidth);
};
//If image wasn't loaded and there's a placeholder then we render it alongside the image.
if (!isLoaded && placeholder) {
const placeholderStyle = {display: isLoaded ? 'none' : 'inline'}
attributes.style = {...(attributes.style || {}), opacity: 0, position: 'absolute'}
attributes.onLoad = handleImageLoaded;
const placeholderAttributes = getAttributes({placeholder: placeholder.props.type});
applyBreakpoints(width, steps, options) {
options = CloudinaryComponent.normalizeOptions(this.getContext(), this.props, options);
let responsiveUseBreakpoints = options.responsiveUseBreakpoints;
if ((!responsiveUseBreakpoints) || (responsiveUseBreakpoints === 'resize' && !options.resizing)) {
return width;
} else {
return this.calc_breakpoint(width, steps);
return (
<Fragment>
<img ref={attachRef} {...attributes} />
<div style={placeholderStyle}>
<img {...placeholderAttributes}/>
</div>
</Fragment>
);
}
};
calc_breakpoint(width, steps) {
var breakpoints, point;
breakpoints = (this.state && this.state.breakpoints) || defaultBreakpoints;
if (Util.isFunction(breakpoints)) {
return breakpoints(width, steps);
} else {
if (Util.isString(breakpoints)) {
breakpoints = ((function () {
var j, len, ref, results;
ref = breakpoints.split(',');
results = [];
for (j = 0, len = ref.length; j < len; j++) {
point = ref[j];
results.push(parseInt(point));
}
return results;
})()).sort(function (a, b) {
return a - b;
});
}
return closestAbove(breakpoints, width);
}
};
device_pixel_ratio(roundDpr = true) {
var dpr, dprString;
dpr = (typeof this.window !== "undefined" && this.window !== null ? this.window.devicePixelRatio : void 0) || 1;
if (roundDpr) {
dpr = Math.ceil(dpr);
}
if (dpr <= 0 || isNaN(dpr)) {
dpr = 1;
}
dprString = dpr.toString();
if (dprString.match(/^\d+$/)) {
dprString += '.0';
}
return dprString;
};
updateDpr(dataSrc, roundDpr) {
return dataSrc.replace(/\bdpr_(1\.0|auto)\b/g, 'dpr_' + this.device_pixel_ratio(roundDpr));
};
maxWidth(requiredWidth) {
return Math.max((this.state && this.state.width) || 0, requiredWidth);
};
cloudinaryUpdate(url, options = {}) {
var requiredWidth;
var match;
let resultUrl = this.updateDpr(url, options.roundDpr);
if (options.responsive || this.state && this.state.responsive) {
let containerWidth = this.findContainerWidth();
if (containerWidth !== 0) {
if (/w_auto:breakpoints/.test(resultUrl)) {
requiredWidth = this.maxWidth(containerWidth, this.element);
resultUrl = resultUrl.replace(/w_auto:breakpoints([_0-9]*)(:[0-9]+)?/,
"w_auto:breakpoints$1:" + requiredWidth);
} else {
match = /w_auto(:(\d+))?/.exec(resultUrl);
if (match) {
requiredWidth = this.applyBreakpoints(containerWidth, match[2], options);
requiredWidth = this.maxWidth(requiredWidth, this.element);
resultUrl = resultUrl.replace(/w_auto[^,\/]*/g, "w_" + requiredWidth);
}
}
} else {
resultUrl = "";
}
}
return {url: resultUrl, width: requiredWidth};
return <img ref={attachRef} {...attributes}/>
}

@@ -225,3 +164,6 @@ }

Image.propTypes = CloudinaryComponent.propTypes;
Image.propTypes.responsive = PropTypes.bool;
Image.propTypes.loading = PropTypes.string;
Image.propTypes.accessibility = PropTypes.string;
export default Image;

@@ -20,3 +20,4 @@ import React from 'react';

Transformation.exposesProps = true;
Transformation.displayName = "CloudinaryTransformation";
export default Transformation;

@@ -7,3 +7,4 @@ import React from 'react';

import Audio from './components/Audio';
import Placeholder from './components/Placeholder';
export { CloudinaryContext, Image, Transformation, Video, Audio};
export { CloudinaryContext, Image, Transformation, Video, Audio, Placeholder};

@@ -6,3 +6,3 @@ import {Transformation, Util} from 'cloudinary-core';

// Map Cloudinary props from array to object for efficient lookup
const CLOUDINARY_PROPS = Transformation.PARAM_NAMES.map(Util.camelCase).reduce(
const CLOUDINARY_PROPS = Transformation.PARAM_NAMES.map(Util.camelCase).concat(['publicId', 'breakpoints']).reduce(
(accumulator, cloudinaryPropName) => {

@@ -9,0 +9,0 @@ accumulator[cloudinaryPropName] = true;

@@ -1,6 +0,2 @@

export debounce from './debounce';
export firstDefined from './firstDefined';
export closestAbove from './closestAbove';
export {requestAnimationFrame, cancelAnimationFrame } from './requestAnimationFrame';
export isElement from './isElement';
export extractCloudinaryProps from './extractCloudinaryProps';
export * from './cloudinaryCoreUtils';

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc