Skip to content

Commit

Permalink
feat: adds toggle button component
Browse files Browse the repository at this point in the history
  • Loading branch information
AssisrMatheus committed Apr 12, 2023
1 parent 6c2af9a commit 36ab417
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 132 deletions.
140 changes: 22 additions & 118 deletions 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
Expand Up @@ -88,7 +88,7 @@
"draft-js": "^0.11.7",
"draftjs-to-html": "^0.9.1",
"draftjs-utils": "^0.10.2",
"framer-motion": "^3.3.0",
"framer-motion": "^10.11.6",
"hammerjs": "^2.0.8",
"html-react-parser": "^1.2.4",
"html-to-draftjs": "^1.5.0",
Expand Down
68 changes: 68 additions & 0 deletions src/components/ToggleButton/ToggleButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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 React, { useState } from 'react';
import { ToggleButton, ToggleButtonProps } from '.';
import { colorOptions } from '../../prebuiltTailwindTheme';
import classnames from 'classnames';
import { puiColorClassnameMap } from '../../storybookMappers';

export default {
title: 'Components/Inputs/ToggleButton',
component: ToggleButton,
argTypes: {
offLabel: { defaultValue: 'Off' },
onLabel: { defaultValue: 'On' },
color: {
defaultValue: 'pui-primary',
control: {
type: 'select',
options: colorOptions
}
},
defaultChecked: {
control: {
type: 'boolean'
}
},
disabled: {
control: {
type: 'boolean'
}
},
className: {
control: {
type: 'text'
}
},
onChange: { action: 'onChange' },
onBlur: { action: 'onBlur' },
onFocus: { action: 'onFocus' }
}
} as Meta;

/**
* A story that displays a ToggleButton example
*
* @param props the story props
* @param props.color User selected color
*/
const Template: Story<ToggleButtonProps & { color?: string }> = ({ color, ...props }) => {
const [checked, setChecked] = useState(false);

return (
<ToggleButton
{...props}
className={classnames(
{ [puiColorClassnameMap[color || 'transparent']]: color !== 'pui-primary' },
props.className
)}
checked={checked}
onChange={(e) => {
setChecked(e.target.checked);
if (props.onChange) props.onChange(e);
}}
/>
);
};

export const Default = Template.bind({});
71 changes: 71 additions & 0 deletions src/components/ToggleButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import classnames from 'classnames';
import { motion } from 'framer-motion';
import React from 'react';

const spring = {
type: 'spring',
stiffness: 700,
damping: 36
};

export type ToggleButtonProps = Omit<
React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
'children' | 'type'
> & {
/**
* Label text for the button's off state
*/
offLabel: string;
/**
* Label text for the button's on state
*/
onLabel: string;
};

/**
* A toggle input component
*
* @param props the component props
* @param props.offLabel Label text for the button's off state
* @param props.onLabel Label text for the button's on state
*/
export const ToggleButton: React.FC<ToggleButtonProps> = ({ offLabel, onLabel, ...props }) => (
<motion.label
layout
layoutRoot
className={classnames(
'flex w-fit relative border rounded-full items-center justify-around overflow-hidden',
{ 'cursor-pointer': !props.disabled && !props.readOnly, 'select-none': !props.readOnly },
!props.disabled ? 'border-black' : 'border-gray-300',
props.className
)}
>
{/* Hidden input so it fakes as a real html input and can be used with forms, it also gives us the full range of event inputs to listen to */}
<input type="checkbox" {...props} className="hidden" />

{[offLabel, onLabel].map((label, index) => {
const selected = (!props.checked && index === 0) || (props.checked && index === 1);
return (
// This defines the sizing that the background will copy from
<div key={index} className="relative w-fit flex items-center justify-center px-8 py-1">
{/* Label text */}
<p className={classnames(selected ? (!props.disabled ? 'text-white' : 'text-gray-500') : 'text-gray-400')}>
{label}
</p>

{/* This is the background that will get animated on toggle */}
{selected ? (
<motion.div
className={classnames(
'absolute -z-10 inset-0 rounded-full',
!props.disabled ? 'bg-pui-placeholder-color' : 'bg-gray-100'
)}
layoutId="underline"
transition={spring}
/>
) : null}
</div>
);
})}
</motion.label>
);
Loading

0 comments on commit 36ab417

Please sign in to comment.