instagram-story
Version:
Instagram story UI build in ReactJS
348 lines (295 loc) • 10.3 kB
JavaScript
;
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var PropTypes = _interopDefault(require('prop-types'));
var React = require('react');
var React__default = _interopDefault(React);
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 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;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function addStyles(selector, styles, id) {
if (styles.length) {
var _styleSheet$cssRules;
var style = document.createElement('style');
style.id = id;
document.head.appendChild(style);
var styleSheet = style.sheet;
styleSheet.insertRule("".concat(selector, " { ").concat(styles, " }"), styleSheet === null || styleSheet === void 0 ? void 0 : (_styleSheet$cssRules = styleSheet.cssRules) === null || _styleSheet$cssRules === void 0 ? void 0 : _styleSheet$cssRules.length);
}
}
function removeElement(selector) {
var element = document.querySelector(selector);
if (element) element.remove();
}
var Timeline = function Timeline(props) {
var animationIndex = props.animationIndex,
count = props.count,
align = props.align,
onCarouselIndexChange = props.onCarouselIndexChange,
delay = props.delay;
var handleAnimationEnd = function handleAnimationEnd() {
var newIndex = animationIndex + 1;
onCarouselIndexChange(newIndex);
};
React.useEffect(function () {
var delayInSecs = delay / 1000;
var timelineStyle = "\n\t\t\tcontent: '';\n\t\t\twidth: 100%;\n\t\t\theight: 2px;\n\t\t\tdisplay: block;\n\t\t\tbackground-color: #fff;\n\t\t\ttransform-origin: 0% center 0px;\n\t\t\tanimation: loader ".concat(delayInSecs, "s linear;\n\t\t");
var fillStyle = 'background-color: #fff !important;';
var loaderAnimation = " \n\t\t\t0% {\n\t\t\t\ttransform: scaleX(0);\n\t\t\t }\n\t\t\t 100% {\n\t\t\t \ttransform: scaleX(1);\n\t\t\t }\n\t\t";
addStyles('.__active::after', timelineStyle, 'js-insta-story-plugin-active');
addStyles('.__fill', fillStyle, 'js-insta-story-plugin-fill');
addStyles('@keyframes loader', loaderAnimation, 'js-insta-story-plugin-animation');
return function () {
removeElement('js-insta-story-plugin-fill');
removeElement('js-insta-story-plugin-active');
removeElement('js-insta-story-plugin-animation');
};
}, [delay]);
var renderElements = function renderElements() {
var elements = [];
for (var i = 0; i < count; i++) {
var className = '';
if (i < animationIndex) {
className = '__fill';
} else if (i === animationIndex) {
className = '__active';
} // Styling the div's
var elementStyle = {
flex: '1 0',
height: '2px',
margin: '5px',
backgroundColor: '#999'
};
var element = /*#__PURE__*/React__default.createElement("div", {
key: i,
style: elementStyle,
className: className,
"data-testid": "timeline-".concat(i),
onAnimationEnd: handleAnimationEnd
});
elements.push(element);
}
return elements;
};
var alignContainer = _objectSpread2({}, align === 'top' ? {
top: '0'
} : {
bottom: '0'
});
var timelineStyle = _objectSpread2({
width: 'inherit',
position: 'fixed'
}, alignContainer);
var timelineArticleStyle = {
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
};
return /*#__PURE__*/React__default.createElement("section", {
style: timelineStyle,
"data-testid": "timeline-root"
}, /*#__PURE__*/React__default.createElement("article", {
style: timelineArticleStyle
}, renderElements()));
};
Timeline.propTypes = {
delay: PropTypes.string.isRequired,
align: PropTypes.string.isRequired,
count: PropTypes.number.isRequired,
onCarouselIndexChange: PropTypes.func.isRequired,
animationIndex: PropTypes.number.isRequired
};
var Timeline$1 = React.memo(Timeline);
var StorySlider = function StorySlider(_ref) {
var pivot = _ref.pivot,
children = _ref.children,
imageWidth = _ref.imageWidth;
var newChildren = React__default.Children.map(children, function (child, index) {
var style = {
position: 'absolute'
};
if (index < pivot) {
style.left = "-".concat(Math.abs(index - pivot) * parseInt(imageWidth, 10), "px");
} else if (index === pivot) {
style.left = '0';
} else {
style.left = "".concat(Math.abs(index - pivot) * parseInt(imageWidth, 10), "px");
}
return React__default.cloneElement(child, {
style: style
});
});
var sliderSectionStyle = {
width: 'inherit',
overflow: 'hidden',
whiteSpace: 'nowrap'
};
return /*#__PURE__*/React__default.createElement("article", {
style: sliderSectionStyle
}, newChildren);
};
StorySlider.propTypes = {
pivot: PropTypes.number.isRequired,
children: PropTypes.node.isRequired,
imageWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired
};
var StorySlider$1 = React.memo(StorySlider);
function detectImageDimensions(imgURL) {
var img = document.createElement('img');
img.src = imgURL;
return new Promise(function (resolve) {
var poll = setInterval(function () {
if (img.naturalWidth) {
clearInterval(poll);
}
return resolve({
width: img.naturalWidth,
height: img.naturalHeight
});
}, 10);
});
}
var Story = function Story(props) {
var children = props.children,
delay = props.delay,
getCurrentSlideIndex = props.getCurrentSlideIndex,
imageWidth = props.imageWidth,
_props$timelineAlign = props.timelineAlign,
timelineAlign = _props$timelineAlign === void 0 ? 'bottom' : _props$timelineAlign;
var imageCount = React__default.Children.count(children);
var _useState = React.useState(imageWidth),
_useState2 = _slicedToArray(_useState, 2),
width = _useState2[0],
setWidth = _useState2[1];
var _useState3 = React.useState(0),
_useState4 = _slicedToArray(_useState3, 2),
idx = _useState4[0],
setIdx = _useState4[1];
React.useEffect(function () {
if (width === 0) {
detectImageDimensions(children[0].props.src).then(function (res) {
setWidth(res.width);
});
}
}, [children, width]);
var onCarouselIndexChange = function onCarouselIndexChange() {
if (idx < imageCount - 1) {
setIdx(idx + 1);
}
}; // Call `getCurrentSlideIndex` to notify updated slide index
getCurrentSlideIndex(idx);
var storyContainerStyle = {
width: 'inherit',
height: 'inherit',
overflow: 'hidden',
position: 'relative'
};
return /*#__PURE__*/React__default.createElement("section", {
style: storyContainerStyle
}, /*#__PURE__*/React__default.createElement(StorySlider$1, {
imageWidth: width,
pivot: idx
}, children), /*#__PURE__*/React__default.createElement(Timeline$1, {
delay: delay,
count: imageCount,
animationIndex: idx,
align: timelineAlign,
onCarouselIndexChange: onCarouselIndexChange
}));
};
Story.defaultProps = {
timelineAlign: 'top',
getCurrentSlideIndex: function getCurrentSlideIndex() {}
};
Story.propTypes = {
timelineAlign: PropTypes.string,
delay: PropTypes.string.isRequired,
getCurrentSlideIndex: PropTypes.func,
imageWidth: PropTypes.string.isRequired,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
};
var Story$1 = React.memo(Story);
module.exports = Story$1;