@sap-ux/ui-components
Version:
SAP UI Components Library
222 lines • 8.81 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UISplitter = exports.UISplitterLayoutType = exports.UISplitterType = void 0;
const react_1 = __importDefault(require("react"));
require("./UISplitter.scss");
const Icons_1 = require("../Icons");
const UIIcon_1 = require("../UIIcon");
var UISplitterType;
(function (UISplitterType) {
UISplitterType["Resize"] = "resize";
UISplitterType["Toggle"] = "toggle";
})(UISplitterType || (exports.UISplitterType = UISplitterType = {}));
var UISplitterLayoutType;
(function (UISplitterLayoutType) {
UISplitterLayoutType["Standard"] = "standard";
UISplitterLayoutType["Compact"] = "compact";
})(UISplitterLayoutType || (exports.UISplitterLayoutType = UISplitterLayoutType = {}));
/**
*
*/
class UISplitter extends react_1.default.Component {
/**
*
* @param props
*/
constructor(props) {
super(props);
this.mousedownPosition = {
x: 0,
y: 0
};
this.size = 14;
this.compactSize = 8;
this.rootRef = react_1.default.createRef();
this.stopMousemoveResize = this.stopMousemoveResize.bind(this);
this.doMousemoveResize = this.doMousemoveResize.bind(this);
// Splitter overlay DOM for block dom during resize
this.splitterOverlay = document.createElement('div');
this.splitterOverlay.setAttribute('class', `splitter__overlay ${props.vertical ? 'splitter__overlay--vertical' : 'splitter__overlay--horizontal'}`);
}
/**
* Method called on mousedown over splitter.
* Method would start resize session.
*
* @param {React.MouseEvent} event Mouse.
*/
startSplitterMove(event) {
this.mousedownPosition.x = event.clientX;
this.mousedownPosition.y = event.clientY;
// Update DOM
this.updateSplitterEvents(true);
// Stop event bubbling
event.stopPropagation();
}
/**
* Method called when resize session is in progress and user moves mouse.
*
* @param {React.MouseEvent} event Mouse event.
*/
doMousemoveResize(event) {
const propertyName = this.props.vertical ? 'clientY' : 'clientX';
const coordinateNameName = this.props.vertical ? 'y' : 'x';
const newPosition = event[propertyName] - this.mousedownPosition[coordinateNameName];
this.doResize(newPosition);
}
/**
* Method which receives new delta position of splitter and updates DOM with calling callback.
*
* @param {number} deltaPosition Delta position of splitter.
*/
doResize(deltaPosition) {
const cssPropertyName = this.props.vertical ? 'top' : 'left';
if (this.animationFrame) {
window.cancelAnimationFrame(this.animationFrame);
}
this.animationFrame = window.requestAnimationFrame(() => {
// Call callback
if (this.props.onResize(deltaPosition)) {
// Update resizer DOM
const dom = this.rootRef.current;
if (dom) {
dom.style[cssPropertyName] = `${deltaPosition - this.size / 2}px`;
}
}
});
}
/**
* Method called on mouseup or mouseleave events.
* Method would end resize session.
*/
stopMousemoveResize() {
this.updateSplitterEvents(false);
}
/**
* Method to update splitter DOM and events depending on if resize started or ended.
*
* @param {boolean} start Resize is started or ended.
*/
updateSplitterEvents(start) {
if (start) {
document.body.addEventListener('mouseup', this.stopMousemoveResize);
document.body.addEventListener('mouseleave', this.stopMousemoveResize);
document.body.addEventListener('mousemove', this.doMousemoveResize);
document.body.appendChild(this.splitterOverlay);
}
else {
document.body.removeEventListener('mouseup', this.stopMousemoveResize);
document.body.removeEventListener('mouseleave', this.stopMousemoveResize);
document.body.removeEventListener('mousemove', this.doMousemoveResize);
document.body.removeChild(this.splitterOverlay);
}
if (start && this.props.onResizeStart) {
this.props.onResizeStart();
}
else if (!start && this.props.onResizeEnd) {
this.props.onResizeEnd();
}
}
/**
* Method called when clicked over splitter and splitter type is 'Toggle'.
*/
toggleSplitter() {
if (this.props.onToggle) {
this.props.onToggle();
}
}
/**
* Gets icon.
*
* @param type
* @param splitterLayoutType
* @returns {string}
*/
getIcon(type, splitterLayoutType = UISplitterLayoutType.Standard) {
if (type === UISplitterType.Toggle) {
return Icons_1.UiIcons.ArrowLeft;
}
else if (splitterLayoutType === UISplitterLayoutType.Compact) {
return Icons_1.UiIcons.Grabber;
}
else {
return Icons_1.UiIcons.VerticalGrip;
}
}
/**
* Method called when keydown events fired while splitter is focused.
* Method enables support for resize using keyboard.
*
* @param {React.KeyboardEvent<HTMLDivElement>} event KeyDown event.
*/
onKeyDown(event) {
if (this.props.type === UISplitterType.Resize) {
const stepSize = 10;
let deltaPosition = 0;
if (['ArrowLeft', 'ArrowUp'].includes(event.key)) {
deltaPosition = -stepSize;
}
else if (['ArrowRight', 'ArrowDown'].includes(event.key)) {
deltaPosition = stepSize;
}
if (deltaPosition === 0) {
// There no resize with keyboard
return;
}
// Do full resize cycle - start, move and end
if (this.props.onResizeStart) {
this.props.onResizeStart();
}
this.doResize(deltaPosition);
if (this.props.onResizeEnd) {
this.props.onResizeEnd();
}
}
else if (this.props.type === UISplitterType.Toggle && event.key === 'Enter') {
this.toggleSplitter();
}
}
/**
* Method returns class names string depending on props and component state.
*
* @returns {number} Minimal size of section.
*/
getClassNames() {
const { type, vertical, hidden, splitterLayoutType } = this.props;
let classNames = `splitter splitter--${type}`;
// vertical or horizontal
classNames += ` ${vertical ? 'splitter--vertical' : 'splitter--horizontal'}`;
if (hidden && type === UISplitterType.Resize) {
classNames += ' splitter--hidden';
}
classNames += ` ${splitterLayoutType === UISplitterLayoutType.Standard ? 'splitter--standard' : 'splitter--compact'}`;
return classNames;
}
/**
* @returns {React.ReactElement}
*/
render() {
const { type, vertical, hidden, title, splitterLayoutType } = this.props;
const tabIndex = this.props.splitterTabIndex ?? 0;
const size = splitterLayoutType === UISplitterLayoutType.Standard ? this.size : this.compactSize;
const splitterOffset = type === UISplitterType.Toggle ? -size : -size / 2;
const role = type === UISplitterType.Toggle ? 'button' : 'separator';
const orientation = vertical ? 'horizontal' : 'vertical';
let ariaPressed;
if (type === UISplitterType.Toggle) {
ariaPressed = !hidden;
}
return (react_1.default.createElement("div", { ref: this.rootRef, role: role, "aria-orientation": orientation, "aria-pressed": ariaPressed, title: title, tabIndex: tabIndex, onKeyDown: this.onKeyDown.bind(this), style: {
...(vertical && { height: size, top: splitterOffset }),
...(!vertical && { width: size, left: splitterOffset })
}, onMouseDown: type === UISplitterType.Resize
? (event) => this.startSplitterMove(event)
: undefined, onClick: type === UISplitterType.Toggle ? () => this.toggleSplitter() : undefined, className: this.getClassNames() },
react_1.default.createElement("div", { className: "splitter__grip" },
react_1.default.createElement(UIIcon_1.UIIcon, { iconName: this.getIcon(type, splitterLayoutType) }))));
}
}
exports.UISplitter = UISplitter;
//# sourceMappingURL=UISplitter.js.map