-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[refactor] rewrite Button & Form components (#16)
- Loading branch information
Showing
14 changed files
with
283 additions
and
150 deletions.
There are no files selected for viewing
Empty file.
Empty file.
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 |
---|---|---|
@@ -1,7 +1,9 @@ | ||
.parcelrc | ||
.parcel-cache/ | ||
.prettierrc.yml | ||
v1/ | ||
test/ | ||
jest.config.js | ||
jest.config.ts | ||
docs/ | ||
.husky/ | ||
.github/ | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import classNames from 'classnames'; | ||
import { JsxChildren, VNode } from 'dom-renderer'; | ||
import { FC, WebCellProps } from 'web-cell'; | ||
|
||
import { FormControlProps } from './Form'; | ||
import { Icon, IconProps } from './Icon'; | ||
import { Color } from './type'; | ||
|
||
export interface ButtonProps | ||
extends WebCellProps<HTMLButtonElement>, | ||
Omit<WebCellProps<HTMLAnchorElement>, 'type'>, | ||
Pick<FormControlProps<'input'>, 'size'> { | ||
variant?: Color | `outline-${Color}` | 'link'; | ||
active?: boolean; | ||
} | ||
|
||
export const Button: FC<ButtonProps> = ({ | ||
className, | ||
href, | ||
variant = 'primary', | ||
active, | ||
children, | ||
...props | ||
}) => { | ||
const { disabled, tabIndex } = props, | ||
Class = classNames('btn', variant && `btn-${variant}`, className); | ||
|
||
return href ? ( | ||
<a | ||
role="button" | ||
className={classNames(Class, { disabled, active })} | ||
tabIndex={disabled ? -1 : tabIndex} | ||
ariaDisabled={disabled + ''} | ||
ariaPressed={active + ''} | ||
{...props} | ||
> | ||
{children} | ||
</a> | ||
) : ( | ||
<button className={Class} {...props} ariaPressed={active + ''}> | ||
{children} | ||
</button> | ||
); | ||
}; | ||
|
||
export function isButton(node: JsxChildren): node is VNode { | ||
const { selector, props } = node as VNode; | ||
|
||
return /^(a|input|button)/.test(selector) && props?.className?.btn; | ||
} | ||
|
||
export type IconButtonProps = IconProps & ButtonProps; | ||
|
||
export const IconButton: FC<IconButtonProps> = ({ | ||
className, | ||
name, | ||
...button | ||
}) => ( | ||
<Button | ||
className={classNames('p-1', className)} | ||
style={{ lineHeight: '0.8' }} | ||
{...button} | ||
> | ||
<Icon name={name} /> | ||
</Button> | ||
); | ||
|
||
export const CloseButton: FC<WebCellProps<HTMLButtonElement>> = ({ | ||
className = '', | ||
...props | ||
}) => ( | ||
<button | ||
className={`btn-close ${className}`} | ||
type="button" | ||
ariaLabel="Close" | ||
{...props} | ||
/> | ||
); |
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,143 @@ | ||
import classNames from 'classnames'; | ||
import { VNode } from 'dom-renderer'; | ||
import { FC, WebCellProps } from 'web-cell'; | ||
import { uniqueID } from 'web-utility'; | ||
|
||
export type FormGroupProps = WebCellProps<HTMLDivElement>; | ||
|
||
export const FormGroup: FC<FormGroupProps> = ({ children, ...props }) => ( | ||
<div {...props}>{children}</div> | ||
); | ||
|
||
export type FormLabelProps = WebCellProps<HTMLLabelElement>; | ||
|
||
export const FormLabel: FC<FormLabelProps> = ({ | ||
className = '', | ||
children, | ||
...props | ||
}) => ( | ||
<label className={`form-label ${className}`} {...props}> | ||
{children} | ||
</label> | ||
); | ||
|
||
export interface FloatingLabelProps extends FormLabelProps { | ||
label: string; | ||
} | ||
|
||
export const FloatingLabel: FC<FloatingLabelProps> = ({ | ||
className = '', | ||
style, | ||
label, | ||
children, | ||
...props | ||
}) => ( | ||
<div className={`form-floating ${className}`} style={style}> | ||
{children} | ||
<label {...props}>{label}</label> | ||
</div> | ||
); | ||
|
||
export type FormControlTag = 'input' | 'textarea' | 'select'; | ||
|
||
export type FormControlProps<T extends FormControlTag> = WebCellProps & | ||
Omit<JSX.IntrinsicElements[T], 'size'> & { | ||
as?: T; | ||
htmlSize?: number; | ||
size?: 'sm' | 'lg'; | ||
plaintext?: boolean; | ||
}; | ||
|
||
export const FormControl = <T extends FormControlTag = 'input'>({ | ||
as: Tag, | ||
className = '', | ||
htmlSize, | ||
size, | ||
plaintext, | ||
...props | ||
}: FormControlProps<T>) => ( | ||
<Tag | ||
className={classNames( | ||
'form-control', | ||
size && `form-control-${size}`, | ||
(props as FormControlProps<'input'>).readOnly && | ||
plaintext && | ||
`form-control-plaintext`, | ||
(props as FormControlProps<'input'>).type === 'color' && | ||
`form-control-color`, | ||
className | ||
)} | ||
{...props} | ||
size={htmlSize} | ||
/> | ||
); | ||
|
||
export interface FormCheckProps extends WebCellProps<HTMLInputElement> { | ||
type: 'radio' | 'checkbox' | 'switch'; | ||
inline?: boolean; | ||
reverse?: boolean; | ||
label?: VNode; | ||
} | ||
|
||
export const FormCheck: FC<FormCheckProps> = ({ | ||
id = uniqueID(), | ||
className = '', | ||
style, | ||
title, | ||
type, | ||
inline, | ||
reverse, | ||
label, | ||
...props | ||
}) => ( | ||
<div | ||
className={classNames( | ||
label && 'form-check', | ||
inline && 'form-check-inline', | ||
reverse && 'form-check-reverse', | ||
type === 'switch' && 'form-switch', | ||
className | ||
)} | ||
style={style} | ||
> | ||
<input | ||
className="form-check-input" | ||
type={type === 'switch' ? 'checkbox' : type} | ||
role={type === 'switch' ? 'switch' : undefined} | ||
id={id} | ||
{...props} | ||
/> | ||
{label && ( | ||
<label className="form-check-label" htmlFor={id} title={title}> | ||
{label} | ||
</label> | ||
)} | ||
</div> | ||
); | ||
|
||
export type FormFieldProps<T extends FormControlTag> = FormGroupProps & | ||
FormLabelProps & | ||
FormControlProps<T> & { | ||
label?: string; | ||
labelFloat?: boolean; | ||
}; | ||
|
||
export const FormField = <T extends FormControlTag = 'input'>({ | ||
className, | ||
label, | ||
labelFloat, | ||
...props | ||
}: FormFieldProps<T>) => { | ||
label ||= props.title || (props as FormControlProps<'input'>).placeholder; | ||
|
||
const field = <FormControl {...(props as FormControlProps<T>)} />; | ||
|
||
return labelFloat ? ( | ||
<FloatingLabel {...{ className, label }}>{field}</FloatingLabel> | ||
) : ( | ||
<FormGroup className={className}> | ||
<FormLabel>{label}</FormLabel> | ||
{field} | ||
</FormGroup> | ||
); | ||
}; |
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
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
Oops, something went wrong.