UNPKG

react-scrubber

Version:

React scrubber component with touch controls, styling, and lots event handlers

318 lines 15.4 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Scrubber = void 0; var react_1 = __importStar(require("react")); var object_fromentries_1 = __importDefault(require("object.fromentries")); var clamp = function (min, max, val) { return Math.min(Math.max(min, val), max); }; var round = function (val, dp) { return parseFloat(val.toFixed(dp)); }; // Use Object.fromEntries when available var filter = function (object, fn) { return (0, object_fromentries_1.default)(Object.entries(object).filter(function (_a) { var key = _a[0], val = _a[1]; return fn(key, val); })); }; var Scrubber = /** @class */ (function (_super) { __extends(Scrubber, _super); function Scrubber() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.barRef = (0, react_1.createRef)(); _this.state = { seeking: false, mouseX: null, mouseY: null, touchId: null, touchX: null, touchY: null, hover: false, }; _this.getPositionFromMouseX = function () { var barDomNode = _this.barRef.current; if (!barDomNode) { return 0; } var _a = _this.props, min = _a.min, max = _a.max; var _b = _this.state, mouseX = _b.mouseX, touchX = _b.touchX; var _c = barDomNode.getBoundingClientRect(), elementLeft = _c.left, width = _c.width; var left = elementLeft + window.scrollX; var cursor = typeof touchX === 'number' ? touchX : mouseX || 0; var clamped = clamp(left, left + width, cursor); var decimal = round((clamped - left) / width, 7); return round((max - min) * decimal, 7) + min; }; _this.getPositionFromMouseY = function () { var barDomNode = _this.barRef.current; if (!barDomNode) { return 0; } var _a = _this.props, min = _a.min, max = _a.max; var _b = _this.state, mouseY = _b.mouseY, touchY = _b.touchY; var _c = barDomNode.getBoundingClientRect(), elementBottom = _c.bottom, height = _c.height; var cursor = typeof touchY === 'number' ? touchY : mouseY || 0; var bottom = elementBottom + window.scrollY; var clamped = clamp(bottom - height, bottom, cursor); var decimal = round((bottom - clamped) / height, 7); return round((max - min) * decimal, 7) + min; }; _this.getPositionFromCursor = function () { var vertical = _this.props.vertical; return vertical ? _this.getPositionFromMouseY() : _this.getPositionFromMouseX(); }; _this.handleMouseOver = function (e) { _this.setState({ mouseX: e.pageX, mouseY: e.pageY, hover: true }, function () { if (_this.props.onMouseOver) { _this.props.onMouseOver(_this.getPositionFromCursor()); } }); }; _this.handleMouseLeave = function (e) { _this.setState({ mouseX: e.pageX, mouseY: e.pageY, hover: false }, function () { if (_this.props.onMouseLeave) { _this.props.onMouseLeave(_this.getPositionFromCursor()); } }); }; _this.handleMouseMove = function (e) { _this.setState({ mouseX: e.pageX, mouseY: e.pageY }, function () { var position = undefined; if (_this.state.hover && _this.props.onMouseMove) { position = _this.getPositionFromCursor(); _this.props.onMouseMove(position); } if (_this.state.seeking && _this.props.onScrubChange) { if (position === undefined) { position = _this.getPositionFromCursor(); } _this.props.onScrubChange(position); } }); }; _this.handleTouchMove = function (e) { if (_this.state.seeking) { e.preventDefault(); } var touch = Array.from(e.changedTouches).find(function (t) { return t.identifier === _this.state.touchId; }); if (touch) { _this.setState({ touchX: touch.pageX, touchY: touch.pageY }, function () { if (_this.state.seeking && _this.props.onScrubChange) { _this.props.onScrubChange(_this.getPositionFromCursor()); } }); } }; _this.handleSeekStart = function (e) { _this.setState({ seeking: true, mouseX: e.pageX, mouseY: e.pageY }, function () { if (_this.props.onScrubStart) { _this.props.onScrubStart(_this.getPositionFromCursor()); } }); }; _this.handleTouchStart = function (e) { var touch = e.changedTouches[0]; _this.setState({ hover: true, seeking: true, touchId: touch.identifier, touchX: touch.pageX, touchY: touch.pageY }, function () { if (_this.props.onScrubStart) { _this.props.onScrubStart(_this.getPositionFromCursor()); } }); }; _this.handleSeekEnd = function () { if (_this.state.seeking) { if (_this.props.onScrubEnd) { _this.props.onScrubEnd(_this.getPositionFromCursor()); } _this.setState({ seeking: false }); } }; _this.handleTouchEnd = function (e) { var touch = Array.from(e.changedTouches).find(function (t) { return t.identifier === _this.state.touchId; }); if (touch && _this.state.seeking) { if (_this.props.onScrubEnd) { _this.props.onScrubEnd(_this.getPositionFromCursor()); } _this.setState({ hover: false, seeking: false, touchX: null, touchY: null, touchId: null }); } }; _this.renderTooltip = function () { var _a; var _b = _this.props, tooltip = _b.tooltip, vertical = _b.vertical; if (!tooltip) { return; } var isInHover = !_this.state.seeking && _this.state.hover; var isInSeeking = _this.state.seeking; var shouldRender = isInHover && (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabledOnHover) || isInSeeking && (tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabledOnScrub); if (!shouldRender) { return; } var className = 'bar__tooltip'; if (tooltip.className) { className = "".concat(className, " ").concat(tooltip.className); } var value = _this.getPositionFromCursor(); var valuePercent = _this.getValuePercent(value); var text = tooltip.formatString ? tooltip.formatString(value) : value.toFixed().toString(); return (react_1.default.createElement("div", { className: className, style: (_a = {}, _a[vertical ? 'bottom' : 'left'] = "".concat(valuePercent, "%"), _a) }, react_1.default.createElement("div", { className: "bar__tooltip-text" }, text), react_1.default.createElement("div", { className: "bar__tooltip-arrow" }))); }; _this.renderMarkers = function () { var _a = _this.props, vertical = _a.vertical, markers = _a.markers; if (markers) { return markers.map(function (value, index) { var className = "bar__marker"; var markerStyle = {}; if (typeof value === 'number') { var valuePercent = _this.getValuePercent(value); if (vertical) { markerStyle.bottom = "".concat(valuePercent, "%"); } else { markerStyle.left = "".concat(valuePercent, "%"); } } else { var startPercent = _this.getValuePercent(value.start); var endPercent = value.end && _this.getValuePercent(value.end); if (vertical) { markerStyle.bottom = "".concat(startPercent, "%"); if (endPercent) { markerStyle.top = "".concat(100 - parseFloat(endPercent), "%"); markerStyle.height = 'unset'; } } else { markerStyle.left = "".concat(startPercent, "%"); if (endPercent) { markerStyle.right = "".concat(100 - parseFloat(endPercent), "%"); markerStyle.width = 'unset'; } } if (value.className) { className = "".concat(className, " ").concat(value.className); } } return react_1.default.createElement("div", { key: index, className: className, style: markerStyle }); }); } return null; }; _this.getValuePercent = function (value) { var _a = _this.props, min = _a.min, max = _a.max; return (((clamp(min, max, value) - min) / (max - min)) * 100).toFixed(5); }; return _this; } Scrubber.prototype.componentDidMount = function () { window.addEventListener('mousemove', this.handleMouseMove); window.addEventListener('mouseup', this.handleSeekEnd); window.addEventListener('touchmove', this.handleTouchMove); window.addEventListener('touchend', this.handleTouchEnd); }; Scrubber.prototype.componentWillUnmount = function () { window.removeEventListener('mousemove', this.handleMouseMove); window.removeEventListener('mouseup', this.handleSeekEnd); window.removeEventListener('touchmove', this.handleTouchMove); window.removeEventListener('touchend', this.handleTouchEnd); }; Scrubber.prototype.render = function () { var _a, _b, _c; var _d = this.props, className = _d.className, value = _d.value, _e = _d.bufferPosition, bufferPosition = _e === void 0 ? 0 : _e, vertical = _d.vertical; var valuePercent = this.getValuePercent(value); var bufferPercent = this.getValuePercent(bufferPosition); var classes = ['scrubber', vertical ? 'vertical' : 'horizontal']; if (this.state.hover) classes.push('hover'); if (this.state.seeking) classes.push('seeking'); if (className) classes.push(className); var propsKeys = [ 'className', 'value', 'min', 'max', 'bufferPosition', 'vertical', 'onScrubStart', 'onScrubEnd', 'onScrubChange', 'onMouseMove', 'onMouseOver', 'onMouseLeave', 'tooltip', 'markers', ]; var customProps = filter(this.props, function (key) { return !propsKeys.includes(key); }); return (react_1.default.createElement("div", __assign({ onMouseDown: this.handleSeekStart, onTouchStart: this.handleTouchStart, onTouchEnd: function (e) { return e.preventDefault(); }, onMouseOver: this.handleMouseOver, onMouseLeave: this.handleMouseLeave }, customProps, { className: classes.join(' ') }), react_1.default.createElement("div", { className: "bar", ref: this.barRef }, react_1.default.createElement("div", { className: "bar__buffer", style: (_a = {}, _a[vertical ? 'height' : 'width'] = "".concat(bufferPercent, "%"), _a) }), this.renderMarkers(), react_1.default.createElement("div", { className: "bar__progress", style: (_b = {}, _b[vertical ? 'height' : 'width'] = "".concat(valuePercent, "%"), _b) }), react_1.default.createElement("div", { className: "bar__thumb", style: (_c = {}, _c[vertical ? 'bottom' : 'left'] = "".concat(valuePercent, "%"), _c) }), this.renderTooltip()))); }; return Scrubber; }(react_1.Component)); exports.Scrubber = Scrubber; ; //# sourceMappingURL=index.js.map