react-scrubber
Version:
React scrubber component with touch controls, styling, and lots event handlers
318 lines • 15.4 kB
JavaScript
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
;