Skip to content

feat: Allow config parameter dialog to be resized in width #2838

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: alpha
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 79 additions & 3 deletions src/components/Modal/Modal.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Field from 'components/Field/Field.react';
import Icon from 'components/Icon/Icon.react';
import Popover from 'components/Popover/Popover.react';
import Position from 'lib/Position';
import React from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import PropTypes from 'lib/PropTypes';
import styles from 'components/Modal/Modal.scss';

Expand Down Expand Up @@ -39,11 +39,68 @@ const Modal = ({
customFooter,
textModal = false,
width,
initialWidth = width ?? 500,
continueText,
onContinue,
showContinue,
buttonsInCenter = React.Children.count(children) === 0,
}) => {
const modalRef = useRef(null);
const [currentWidth, setCurrentWidth] = useState(initialWidth);
const resizing = useRef({ active: false, side: null, startX: 0, startWidth: 0 });

// Helper to get min width
const minWidth = initialWidth;

const handleMouseDown = (side) => (e) => {
e.preventDefault();
if (!modalRef.current) {
return;
}
resizing.current = {
active: true,
side,
startX: e.clientX,
startWidth: currentWidth,
};
modalRef.current.classList.add(styles.noTransition);
};

const handleMouseMove = useCallback(
(e) => {
if (!resizing.current.active || !modalRef.current) {
return;
}
const { side, startX, startWidth } = resizing.current;
if (side === 'right') {
let newWidth = startWidth + 2 * (e.clientX - startX);
newWidth = Math.max(minWidth, newWidth);
setCurrentWidth(newWidth);
} else if (side === 'left') {
let newWidth = startWidth - 2 * (e.clientX - startX);
newWidth = Math.max(minWidth, newWidth);
setCurrentWidth(newWidth);
}
},
[minWidth]
);

const handleMouseUp = useCallback(() => {
resizing.current = { active: false, side: null, startX: 0, startWidth: 0 };
if (modalRef.current) {
modalRef.current.classList.remove(styles.noTransition);
}
}, []);

useEffect(() => {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}, [handleMouseMove, handleMouseUp]);

if (children) {
children = React.Children.map(children, c => {
if (c && c.type === Field && c.props.label) {
Expand Down Expand Up @@ -81,7 +138,25 @@ const Modal = ({

return (
<Popover fadeIn={true} fixed={true} position={origin} modal={true} color="rgba(17,13,17,0.8)">
<div className={[styles.modal, styles[type]].join(' ')} style={{ width }}>
<div
ref={modalRef}
className={[styles.modal, styles[type]].join(' ')}
style={{
width: currentWidth,
}}
>
{/* Left resize handle */}
<div
className={styles.resizeHandleLeft}
style={{ position: 'absolute', left: 0, top: 0, width: 8, height: '100%', cursor: 'ew-resize', zIndex: 10 }}
onMouseDown={handleMouseDown('left')}
/>
{/* Right resize handle */}
<div
className={styles.resizeHandleRight}
style={{ position: 'absolute', right: 0, top: 0, width: 8, height: '100%', cursor: 'ew-resize', zIndex: 10 }}
onMouseDown={handleMouseDown('right')}
/>
<div className={styles.header}>
<div
style={{
Expand Down Expand Up @@ -133,7 +208,8 @@ Modal.propTypes = {
progress: PropTypes.bool.describe('Passed to the confirm button.'),
customFooter: PropTypes.node.describe('used to fill any custom footer use case.'),
textModal: PropTypes.bool.describe('Used for modals that contain only text to pad the text.'),
width: PropTypes.number.describe('custom width of modal.'),
width: PropTypes.number.describe('custom width of modal (legacy prop, prefer initialWidth).'),
initialWidth: PropTypes.number.describe('custom width of modal.'),
buttonsInCenter: PropTypes.bool.describe(
'If true, the buttons will appear in the center of the modal, instead of to the right. By default, the buttons appear on the right unless the modal contains no children, in which case they appear in the center.'
),
Expand Down
15 changes: 15 additions & 0 deletions src/components/Modal/Modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,18 @@
}

@include modalKeyframes();

.resizeHandle {
width: 16px;
height: 16px;
position: absolute;
bottom: 0;
right: 0;
cursor: se-resize;
background: rgba(255, 255, 255, 0.5);
z-index: 10;
}

.noTransition {
transition: none !important;
}
Loading