-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #71 from perimetre/5.2.0
5.2.0
- Loading branch information
Showing
7 changed files
with
309 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
src/components/HorizontalResizeablePanel/HorizontalResizeablePanel.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// also exported from '@storybook/react' if you can deal with breaking changes in 6.1 | ||
import { Meta, Story } from '@storybook/react/types-6-0'; | ||
import classnames from 'classnames'; | ||
import React from 'react'; | ||
import { HorizontalResizeablePanel } from '.'; | ||
import { colorOptions, widthHeightOptions } from '../../prebuiltTailwindTheme'; | ||
import { backgroundColorClassnameMap, heightClassnameMap, widthClassnameMap } from '../../storybookMappers'; | ||
|
||
export default { | ||
title: 'Components/HorizontalResizeablePanel', | ||
component: HorizontalResizeablePanel, | ||
argTypes: { | ||
resizeRight: { | ||
defaultValue: true | ||
}, | ||
width: { | ||
defaultValue: '1/4', | ||
control: { | ||
type: 'select', | ||
options: widthHeightOptions | ||
} | ||
}, | ||
height: { | ||
defaultValue: 'screen', | ||
control: { | ||
type: 'select', | ||
options: widthHeightOptions | ||
} | ||
}, | ||
backgroundColor: { | ||
defaultValue: 'pui-primary', | ||
control: { | ||
type: 'select', | ||
options: colorOptions | ||
} | ||
}, | ||
className: { | ||
control: { | ||
type: 'text' | ||
} | ||
} | ||
} | ||
} as Meta; | ||
|
||
/** | ||
* A story that displays a horizontal resizeable example | ||
* | ||
* @param props The story props | ||
* @param props.width The example width size | ||
* @param props.height The example height size | ||
* @param props.backgroundColor the example background color | ||
* @param props.className the classname to pass down if any | ||
*/ | ||
const Template: Story = ({ width, height, backgroundColor, className, ...props }) => { | ||
return ( | ||
<div | ||
className={classnames({ | ||
[heightClassnameMap[height || 'auto']]: height && height.length > 0, | ||
[widthClassnameMap[width || 'auto']]: width && width.length > 0 | ||
})} | ||
> | ||
<HorizontalResizeablePanel | ||
{...props} | ||
className={classnames(backgroundColorClassnameMap[backgroundColor || 'transparent'], className)} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
export const Default = Template.bind({}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import classNames from 'classnames'; | ||
import React, { useCallback, useEffect, useRef } from 'react'; | ||
|
||
// eslint-disable-next-line @typescript-eslint/ban-types | ||
export type HorizontalResizeablePanelProps = React.HTMLAttributes<HTMLDivElement> & { | ||
/** | ||
* Turn on or off the resizing behavior on the left border | ||
*/ | ||
resizeLeft?: boolean; | ||
/** | ||
* Turn on or off the resizing behavior on the right border | ||
*/ | ||
resizeRight?: boolean; | ||
/** | ||
* The minimum width allowed when resizing from the left border | ||
*/ | ||
minLeftSize?: number; | ||
/** | ||
* The maximum width allowed when resizing from the left border | ||
*/ | ||
maxLeftSize?: number; | ||
/** | ||
* The minimum width allowed when resizing from the right border | ||
*/ | ||
minRightSize?: number; | ||
/** | ||
* The maximum width allowed when resizing from the right border | ||
*/ | ||
maxRightSize?: number; | ||
}; | ||
|
||
/** | ||
* A panel with resize abilities on its left and right borders | ||
* | ||
* @param props The component props | ||
* @param props.resizeLeft Turn on or off the resizing behavior on the left border | ||
* @param props.resizeRight Turn on or off the resizing behavior on the right border | ||
* @param props.minLeftSize The minimum width allowed when resizing from the left border | ||
* @param props.maxLeftSize The maximum width allowed when resizing from the left border | ||
* @param props.minRightSize The minimum width allowed when resizing from the right border | ||
* @param props.maxRightSize The maximum width allowed when resizing from the right border | ||
* @param props.children The element children components | ||
*/ | ||
export const HorizontalResizeablePanel: React.FC<HorizontalResizeablePanelProps> = ({ | ||
resizeLeft, | ||
resizeRight, | ||
minLeftSize, | ||
maxLeftSize, | ||
minRightSize, | ||
maxRightSize, | ||
children, | ||
...props | ||
}) => { | ||
const dragRef = useRef<{ isResizing: boolean; lastDownX: number; resizeSide: 'left' | 'right' } | null>(null); | ||
const ref = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
/** | ||
* Handler for when the user is moving the mouse | ||
* | ||
* @param e The mouse event | ||
*/ | ||
const onMouseMove = (e: MouseEvent) => { | ||
// we don't want to do anything if we aren't resizing. | ||
if (!ref.current || !dragRef.current?.isResizing) return; | ||
|
||
if (dragRef.current.resizeSide === 'left') { | ||
let width = ref.current.clientWidth + (e.clientX - ref.current.offsetLeft) * -1; | ||
|
||
if (minLeftSize && width < minLeftSize) { | ||
width = minLeftSize; | ||
} | ||
|
||
if (maxLeftSize && width > maxLeftSize) { | ||
width = maxLeftSize; | ||
} | ||
|
||
ref.current.style.width = `${width}px`; | ||
} else if (dragRef.current.resizeSide === 'right') { | ||
let width = e.clientX - ref.current.offsetLeft; | ||
|
||
if (minRightSize && width < minRightSize) { | ||
width = minRightSize; | ||
} | ||
|
||
if (maxRightSize && width > maxRightSize) { | ||
width = maxRightSize; | ||
} | ||
|
||
ref.current.style.width = `${width}px`; | ||
} | ||
}; | ||
|
||
/** | ||
* Handler for when the user stops dragging the mouse | ||
*/ | ||
const onMouseUp = () => { | ||
// Resets the values | ||
dragRef.current = { isResizing: false, lastDownX: 0, resizeSide: 'left' }; | ||
}; | ||
|
||
window.addEventListener('mousemove', onMouseMove, false); | ||
window.addEventListener('mouseup', onMouseUp, false); | ||
return () => { | ||
window.removeEventListener('mousemove', onMouseMove); | ||
window.removeEventListener('mouseup', onMouseUp); | ||
}; | ||
}, [maxLeftSize, maxRightSize, minLeftSize, minRightSize]); | ||
|
||
const onMove = useCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>, resizeSide: 'left' | 'right') => { | ||
dragRef.current = { isResizing: true, lastDownX: e.clientX, resizeSide }; | ||
}, []); | ||
|
||
return ( | ||
<div {...props} className={classNames('relative h-full w-full', props.className)} ref={ref}> | ||
{resizeLeft && ( | ||
<button | ||
className="absolute inset-y-0 w-2 cursor-col-resize" | ||
style={{ left: '-4px' }} | ||
onMouseDown={(e) => onMove(e, 'left')} | ||
/> | ||
)} | ||
{resizeRight && ( | ||
<button | ||
className="absolute inset-y-0 w-2 cursor-col-resize" | ||
style={{ right: '-4px' }} | ||
onMouseDown={(e) => onMove(e, 'right')} | ||
/> | ||
)} | ||
{children} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters