UNPKG

@upleveled/code-surfer

Version:

React component for scrolling, zooming and highlighting code.

243 lines (192 loc) 9.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Element = exports.Content = exports.Container = undefined; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = require("react"); var _react2 = _interopRequireDefault(_react); var _tween = require("./tween"); var _tween2 = _interopRequireDefault(_tween); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var getNewCenter = function getNewCenter(container, content, firstSelected, lastSelected) { var parentHeight = container.parentElement.offsetHeight; container.style.padding = parentHeight / 2 + "px 0"; container.style.height = 0; firstSelected = firstSelected || content; lastSelected = lastSelected || firstSelected; var contentTop = content.offsetTop; var contentHeight = content.offsetHeight; var top = firstSelected.offsetTop; var bottom = lastSelected.offsetTop + lastSelected.offsetHeight; var selectedHeight = bottom - top; var middle = Math.floor((top + bottom) / 2); var containerHeight = container.offsetHeight; var scale = 1; if (selectedHeight > containerHeight) { scale = containerHeight / selectedHeight; var minScale = 0.2; scale = scale < minScale ? minScale : scale; } // center in the middle of the selected element var center = middle; var scaledContentHeight = contentHeight * scale; var scaledSelectedHeight = selectedHeight * scale; var contentMargin = (contentHeight - scaledContentHeight) / 2; var halfContent = contentHeight / 2; if (scale !== 1) { center = (middle - halfContent) * scale + halfContent; } // console.log("top", top); // console.log("bottom", bottom); // console.log("containerHeight", containerHeight); // console.log("selectedHeight", selectedHeight); // console.log("scaledSelectedHeight", scaledSelectedHeight); // console.log("contentHeight", contentHeight); // console.log("scaledContentHeight", scaledContentHeight); if (containerHeight >= scaledContentHeight) { // center in the middle of the content center = contentHeight / 2; } else if (containerHeight >= scaledSelectedHeight) { var minScroll = contentMargin + containerHeight / 2; var maxScroll = contentHeight - contentMargin - containerHeight / 2; center = center < minScroll ? minScroll : center; center = center > maxScroll ? maxScroll : center; } else { // console.log("Bigger selected than container"); center = (top - halfContent) * scale + halfContent + containerHeight / 2; } // console.log(center); return { center: center, scale: scale }; }; var scrollTo = function scrollTo(container, content, center, scale, duration) { var startY = container.scrollTop; var containerScale = container.getBoundingClientRect().height / container.offsetHeight; // TODO browser support? var startScale = content.getBoundingClientRect().height / content.offsetHeight / containerScale; var endY = center; var step = { top: startY, scale: startScale }; var shouldAnimate = true; var tween = new _tween2.default.Tween(step).to({ top: endY, scale: scale }, duration).easing(_tween2.default.Easing.Circular.Out).onUpdate(function () { container.scrollTop = step.top | 0; content.style.transform = "scale(" + step.scale + ")"; }).onComplete(function () { shouldAnimate = false; }).start(); function animate(time) { if (!shouldAnimate) return; requestAnimationFrame(animate); _tween2.default.update(time); } requestAnimationFrame(animate); }; var contentClassName = "scroll-content"; var selectedClassName = "scroll-selected"; var Container = exports.Container = function (_React$Component) { _inherits(Container, _React$Component); function Container(props) { _classCallCheck(this, Container); var _this = _possibleConstructorReturn(this, (Container.__proto__ || Object.getPrototypeOf(Container)).call(this, props)); _this.containerRef = _react2.default.createRef(); return _this; } _createClass(Container, [{ key: "animate", value: function animate(duration) { var container = this.containerRef.current; var content = container.querySelector("." + contentClassName); var allSelected = container.querySelectorAll("." + selectedClassName); var firstSelected = allSelected.length ? allSelected[0] : content; var lastSelected = allSelected.length ? allSelected[allSelected.length - 1] : content; var _getNewCenter = getNewCenter(container, content, firstSelected, lastSelected), center = _getNewCenter.center, scale = _getNewCenter.scale; scrollTo(container, content, center, scale, duration); } }, { key: "componentDidMount", value: function componentDidMount() { this.animate(1); } }, { key: "componentDidUpdate", value: function componentDidUpdate() { this.animate(400); } }, { key: "render", value: function render() { var _props = this.props, type = _props.type, height = _props.height, children = _props.children, style = _props.style, rest = _objectWithoutProperties(_props, ["type", "height", "children", "style"]); return _react2.default.createElement(type || "div", _extends({ ref: this.containerRef, style: Object.assign({}, { // height: 0, margin: 0, overflow: "hidden", textAlign: "center", position: "relative" }, style) }, rest), _react2.default.createElement( "div", { style: { position: "relative" } }, children )); } }]); return Container; }(_react2.default.Component); var Content = exports.Content = function (_React$Component2) { _inherits(Content, _React$Component2); function Content() { _classCallCheck(this, Content); return _possibleConstructorReturn(this, (Content.__proto__ || Object.getPrototypeOf(Content)).apply(this, arguments)); } _createClass(Content, [{ key: "render", value: function render() { var _props2 = this.props, type = _props2.type, children = _props2.children, style = _props2.style, className = _props2.className, rest = _objectWithoutProperties(_props2, ["type", "children", "style", "className"]); return _react2.default.createElement(type || "div", _extends({ className: contentClassName + " " + className, style: Object.assign({}, { display: "inline-block", textAlign: "left", width: "100%" }, style) }, rest), children); } }]); return Content; }(_react2.default.Component); var Element = exports.Element = function (_React$Component3) { _inherits(Element, _React$Component3); function Element() { _classCallCheck(this, Element); return _possibleConstructorReturn(this, (Element.__proto__ || Object.getPrototypeOf(Element)).apply(this, arguments)); } _createClass(Element, [{ key: "render", value: function render() { var _props3 = this.props, type = _props3.type, selected = _props3.selected, children = _props3.children, className = _props3.className, rest = _objectWithoutProperties(_props3, ["type", "selected", "children", "className"]); return _react2.default.createElement(type || "div", _extends({ className: selected ? selectedClassName + " " + className : className }, rest), children); } }]); return Element; }(_react2.default.Component);