UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

224 lines (191 loc) 6.91 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 5.6.0 */ import React from "react"; import PropTypes from "prop-types"; import { withResizableSidebar } from "../../contexts/ResizeSidebar/ResizeSidebarContext"; const SIDEBARS = { LEFT: "left", RIGHT: "right", }; class ResizableSidebar extends React.Component { constructor(props) { super(props); this.sidebarRef = React.createRef(); this.bindCallbacks(); this.state = this.setDefaultState(); } /** * Set the default state * @returns state */ setDefaultState() { return { resizing: false, startX: 0, startWidth: 0, }; } /** * Bind callbacks */ bindCallbacks() { this.handleMouseDown = this.handleMouseDown.bind(this); this.handleMouseMove = this.handleMouseMove.bind(this); this.handleMouseUp = this.handleMouseUp.bind(this); this.handleDoubleClick = this.handleDoubleClick.bind(this); } /** * Set the width of the sidebars on load */ componentDidMount() { const { minWidth, gutterLeft, sidebarContext } = this.props; const side = gutterLeft ? SIDEBARS.RIGHT : SIDEBARS.LEFT; setTimeout(() => { // Run this right after DOM is painted to access the correct offsetWidth of container and set initial width properly const containerWidth = sidebarContext.containerRef?.current?.offsetWidth || 1; const initialWidth = this.percentToPx(minWidth, containerWidth); sidebarContext.setSidebarWidth(side, initialWidth); }, 0); } /** * Remove event listeners when component is un-mounted */ componentWillUnmount() { this.removeEventListeners(); } /** * Convert pixel value to percentage * @param {number} px * @param {number} containerWidth * @returns */ pxToPercent(px, containerWidth) { return `${(px / containerWidth) * 100}%`; } /** * Convert percentage value to pixel * @param {string} percentStr * @param {number} containerWidth * @returns */ percentToPx(percentStr, containerWidth) { const percent = parseFloat(percentStr); if (isNaN(percent)) { return 0; } return (percent / 100) * containerWidth; } /** * Handle mouse down event * @param {*} e */ handleMouseDown(e) { e.preventDefault(); const { gutterLeft, sidebarContext } = this.props; const startWidth = gutterLeft ? sidebarContext.right.width || 0 : sidebarContext.left.width || 0; this.setState({ resizing: true, startX: e.clientX, startWidth, }); document.addEventListener("mousemove", this.handleMouseMove); document.addEventListener("mouseup", this.handleMouseUp); //to stop resizing when mouse leaves document or window loses focus document.addEventListener("mouseleave", this.handleMouseUp); window.addEventListener("blur", this.handleMouseUp); } /** * Handle the mouse move event - calculate the size change * @param {Event} e */ handleMouseMove(e) { if (!this.state.resizing) { return; } const { startX, startWidth } = this.state; const { gutterLeft, minWidth, maxWidth, sidebarContext } = this.props; const containerWidth = sidebarContext?.containerRef?.current?.offsetWidth || 1; const dx = e.clientX - startX; const delta = gutterLeft ? -dx : dx; const side = gutterLeft ? SIDEBARS.RIGHT : SIDEBARS.LEFT; const minPx = this.percentToPx(minWidth, containerWidth); const maxPx = this.percentToPx(maxWidth, containerWidth); const newWidth = Math.max(minPx, Math.min(maxPx, startWidth + delta)); // Clamp to max width if resizing exceeds max width sidebarContext.setSidebarWidth(side, newWidth); } /** * Handle mouse up event - remove event listeners */ handleMouseUp() { this.setState({ resizing: false }); this.removeEventListeners(); } /** * Handle double click event - default the sidebars to original widths */ handleDoubleClick() { const { gutterLeft, minWidth, sidebarContext } = this.props; const containerWidth = sidebarContext?.containerRef?.current?.offsetWidth || 1; const minPx = this.percentToPx(minWidth, containerWidth); const side = gutterLeft ? SIDEBARS.RIGHT : SIDEBARS.LEFT; sidebarContext.setSidebarWidth(side, minPx); } /** * Remove event listeners */ removeEventListeners() { document.removeEventListener("mousemove", this.handleMouseMove); document.removeEventListener("mouseup", this.handleMouseUp); document.removeEventListener("mouseleave", this.handleMouseUp); window.removeEventListener("blur", this.handleMouseUp); } render() { const { gutterLeft, resizable, classNames, children, sidebarContext } = this.props; const { containerRef, left, right } = sidebarContext; const widthPx = gutterLeft ? right.width : left.width; const containerWidth = containerRef?.current?.offsetWidth || 1; const widthPercent = this.pxToPercent(widthPx, containerWidth); /** use 'resource-workspace left-side-bar' and 'resource-workspace right-side-bar' for Resource Workspace sidebars */ /** use 'users-workspace left-side-bar' and 'users-workspace right-side-bar' for Users Workspace sidebars */ const classes = `resizable-sidebar${classNames ? ` ${classNames}` : ""}`; return ( <div className={classes} ref={this.sidebarRef} style={{ width: widthPercent }}> {gutterLeft && resizable && ( <div className="gutter" onMouseDown={this.handleMouseDown} onDoubleClick={this.handleDoubleClick} /> )} {children} {!gutterLeft && resizable && ( <div className="gutter" onMouseDown={this.handleMouseDown} onDoubleClick={this.handleDoubleClick} /> )} </div> ); } } ResizableSidebar.propTypes = { resizable: PropTypes.bool, // if the sidebar is resizable or not gutterLeft: PropTypes.bool, // if resizable from left or right classNames: PropTypes.string, // classes to add children: PropTypes.any, // children to display sidebarContext: PropTypes.object.isRequired, minWidth: PropTypes.string, // min width of the sidebar maxWidth: PropTypes.string, // max width of the sidebar }; ResizableSidebar.defaultProps = { resizable: true, gutterLeft: false, classNames: "", minWidth: "20%", maxWidth: "35%", }; export default withResizableSidebar(ResizableSidebar);