Skip to content

Commit

Permalink
Merge pull request #71 from perimetre/5.2.0
Browse files Browse the repository at this point in the history
5.2.0
  • Loading branch information
AssisrMatheus authored Jun 29, 2022
2 parents b1a3a94 + c4fe99f commit b680d22
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

## [5.2.0] 2022-06-29

### Added

- Added `HorizontalResizeablePanel` component

## [5.1.0] 2022-06-28

### Added
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@perimetre/ui",
"description": "A component library made by @perimetre",
"version": "5.1.0",
"version": "5.2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/perimetre/ui.git"
Expand Down
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({});
133 changes: 133 additions & 0 deletions src/components/HorizontalResizeablePanel/index.tsx
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>
);
};
1 change: 1 addition & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from './ResourcesCard';
export * from './ExpertCard';
export * from './ProgramCard';
export * from './BaseCard';
export * from './HorizontalResizeablePanel';
97 changes: 97 additions & 0 deletions src/storybookMappers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,103 @@ export const puiColorClassnameMap = {
'pui-success': 'pui-color-pui-success'
};

export const backgroundColorClassnameMap = {
transparent: 'bg-transparent',
current: 'bg-current',
black: 'bg-black',
white: 'bg-white',
'gray-50': 'bg-gray-50',
'gray-100': 'bg-gray-100',
'gray-200': 'bg-gray-200',
'gray-300': 'bg-gray-300',
'gray-400': 'bg-gray-400',
'gray-500': 'bg-gray-500',
'gray-600': 'bg-gray-600',
'gray-700': 'bg-gray-700',
'gray-800': 'bg-gray-800',
'gray-900': 'bg-gray-900',
'red-50': 'bg-red-50',
'red-100': 'bg-red-100',
'red-200': 'bg-red-200',
'red-300': 'bg-red-300',
'red-400': 'bg-red-400',
'red-500': 'bg-red-500',
'red-600': 'bg-red-600',
'red-700': 'bg-red-700',
'red-800': 'bg-red-800',
'red-900': 'bg-red-900',
'yellow-50': 'bg-yellow-50',
'yellow-100': 'bg-yellow-100',
'yellow-200': 'bg-yellow-200',
'yellow-300': 'bg-yellow-300',
'yellow-400': 'bg-yellow-400',
'yellow-500': 'bg-yellow-500',
'yellow-600': 'bg-yellow-600',
'yellow-700': 'bg-yellow-700',
'yellow-800': 'bg-yellow-800',
'yellow-900': 'bg-yellow-900',
'green-50': 'bg-green-50',
'green-100': 'bg-green-100',
'green-200': 'bg-green-200',
'green-300': 'bg-green-300',
'green-400': 'bg-green-400',
'green-500': 'bg-green-500',
'green-600': 'bg-green-600',
'green-700': 'bg-green-700',
'green-800': 'bg-green-800',
'green-900': 'bg-green-900',
'blue-50': 'bg-blue-50',
'blue-100': 'bg-blue-100',
'blue-200': 'bg-blue-200',
'blue-300': 'bg-blue-300',
'blue-400': 'bg-blue-400',
'blue-500': 'bg-blue-500',
'blue-600': 'bg-blue-600',
'blue-700': 'bg-blue-700',
'blue-800': 'bg-blue-800',
'blue-900': 'bg-blue-900',
'indigo-50': 'bg-indigo-50',
'indigo-100': 'bg-indigo-100',
'indigo-200': 'bg-indigo-200',
'indigo-300': 'bg-indigo-300',
'indigo-400': 'bg-indigo-400',
'indigo-500': 'bg-indigo-500',
'indigo-600': 'bg-indigo-600',
'indigo-700': 'bg-indigo-700',
'indigo-800': 'bg-indigo-800',
'indigo-900': 'bg-indigo-900',
'purple-50': 'bg-purple-50',
'purple-100': 'bg-purple-100',
'purple-200': 'bg-purple-200',
'purple-300': 'bg-purple-300',
'purple-400': 'bg-purple-400',
'purple-500': 'bg-purple-500',
'purple-600': 'bg-purple-600',
'purple-700': 'bg-purple-700',
'purple-800': 'bg-purple-800',
'purple-900': 'bg-purple-900',
'pink-50': 'bg-pink-50',
'pink-100': 'bg-pink-100',
'pink-200': 'bg-pink-200',
'pink-300': 'bg-pink-300',
'pink-400': 'bg-pink-400',
'pink-500': 'bg-pink-500',
'pink-600': 'bg-pink-600',
'pink-700': 'bg-pink-700',
'pink-800': 'bg-pink-800',
'pink-900': 'bg-pink-900',
'pui-primary': 'bg-pui-primary',
'pui-secondary': 'bg-pui-secondary',
'pui-paragraph-0': 'bg-pui-paragraph-0',
'pui-paragraph-300': 'bg-pui-paragraph-300',
'pui-paragraph-500': 'bg-pui-paragraph-500',
'pui-paragraph-900': 'bg-pui-paragraph-900',
'pui-initial': 'bg-pui-initial',
'pui-placeholder-color': 'bg-pui-placeholder-color',
'pui-error': 'bg-pui-error',
'pui-success': 'bg-pui-success'
};

export type ColorMapper = typeof puiColorClassnameMap;

export const gradientFromClassNameMap: ColorMapper = {
Expand Down

0 comments on commit b680d22

Please sign in to comment.