Custom RadioButton

This commit is contained in:
Aarni Halinen
2020-07-23 20:58:12 +03:00
parent bc9db92e1f
commit f87f22df72
4 changed files with 153 additions and 9 deletions
+8 -8
View File
@@ -10,13 +10,13 @@ const Container = styled.label`
user-select: none;
/* On mouse-over, add a grey background color */
&:hover input ~ .custombox {
&:hover input ~ .custom-cbox {
background-color: #d4d0c7; /* grey1 */
}
`;
/* Hide the browser's default checkbox */
const DefaultBox = styled.input`
const HiddenDefaultElement = styled.input`
position: absolute;
opacity: 0;
height: 0;
@@ -24,14 +24,14 @@ const DefaultBox = styled.input`
`;
/* Create a custom checkbox */
const CustomBox = styled.span<{checked?: boolean}>`
const CustomCBoxElement = styled.span<{checked?: boolean}>`
border-radius: 4px;
position: absolute;
top: 0;
left: 0;
height: 1em;
width: 1em;
background-color: ${(props) => props.checked ? "#002d3a" : "#efece4"}; /* dark-blue or grey2 */
background-color: ${(props) => props.checked ? "#57b2df" : "#efece4"}; /* blue1 or grey2 */
&:focus &:before {
transition: box-shadow 150ms ease;
@@ -47,7 +47,7 @@ const CustomBox = styled.span<{checked?: boolean}>`
}
`;
const Checkmark = styled.span`
const Checkmark = styled.div`
position: absolute;
left: 0.375em;
top: calc(0.25em - 1px);
@@ -66,15 +66,15 @@ type CheckboxProps = Omit<
const Checkbox: React.FC<CheckboxProps> = ({children, checked, ...props}) => (
<Container>
{children}
<DefaultBox
<HiddenDefaultElement
type="checkbox"
checked={checked}
{...props}
tabIndex={-1}
/>
<CustomBox tabIndex={0} checked={checked} className="custombox">
<CustomCBoxElement tabIndex={0} checked={checked} className="custom-cbox">
{checked && (<Checkmark />)}
</CustomBox>
</CustomCBoxElement>
</Container>
)
@@ -0,0 +1,69 @@
import React from "react";
import styled from "styled-components";
const Container = styled.label`
display: block;
position: relative;
padding-left: 2rem;
cursor: pointer;
font-size: 1.5rem; /* 24px */
user-select: none;
/* On mouse-over, add a grey background color */
&:hover input ~ .custom-radio {
background-color: #d4d0c7; /* grey1 */
}
`;
const HiddenDefaultElement = styled.input`
position: absolute;
opacity: 0;
height: 0;
width: 0;
`;
const CustomRadioElement = styled.span<{checked?: boolean}>`
position: absolute;
top: 0;
left: 0;
height: 1em;
width: 1em;
background-color: ${(props) => props.checked ? "#57b2df" : "#efece4"}; /* blue1 or grey2 */
border-radius: 50%;
`;
const Indicator = styled.div`
position: absolute;
top: 0.25em;
left: 0.25em;
width: 0.5em;
height: 0.5em;
border-radius: 50%;
background: white;
`;
type RadioButtonProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"type"
>;
const RadioButton: React.FC<RadioButtonProps> = ({
checked,
children,
...props
}) => (
<Container>
{children}
<HiddenDefaultElement
type="radio"
checked={checked}
{...props}
tabIndex={-1}
/>
<CustomRadioElement tabIndex={0} checked={checked} className="custom-radio">
{checked && (<Indicator />)}
</CustomRadioElement>
</Container>
);
export default RadioButton;
@@ -0,0 +1,74 @@
import React from "react";
import styled from "styled-components";
import { WidgetProps } from "react-jsonschema-form";
import RadioButton from "./RadioButton";
type RadioButtonWidgetProps = Omit<WidgetProps, "options"> & {
options: any;
};
const RadioButtonContainer = styled.div`
margin-bottom: 0.5rem;
`;
const RadioButtonWidget: React.FC<RadioButtonWidgetProps> = (props) => {
const {
options,
value,
required,
disabled,
readonly,
autofocus,
onBlur,
onFocus,
onChange,
id,
} = props;
// Generating a unique field name to identify this set of radio buttons
const name = Math.random().toString();
const { enumOptions, enumDisabled, inline } = options;
// checked={checked} has been moved above name={name}, As mentioned in #349;
// this is a temporary fix for radio button rendering bug in React, facebook/react#7630.
return (
<div className="field-radio-group" id={id}>
{enumOptions.map((option, i) => {
const checked = option.value === value;
const itemDisabled =
enumDisabled && enumDisabled.indexOf(option.value) != -1;
const disabledCls =
disabled || itemDisabled || readonly ? "disabled" : "";
const radio = (
<RadioButton
checked={checked}
name={name}
required={required}
value={option.value}
disabled={disabled || itemDisabled || readonly}
autoFocus={autofocus && i === 0}
onChange={() => onChange(option.value)}
onBlur={onBlur && (event => onBlur(id, event.target.value))}
onFocus={onFocus && (event => onFocus(id, event.target.value))}
>
{option.label}
</RadioButton>
);
return inline ? (
<label key={i} className={`radio-inline ${disabledCls}`}>
{radio}
</label>
) : (
<RadioButtonContainer key={i} className={disabledCls}>
{radio}
</RadioButtonContainer>
);
})}
</div>
);
}
RadioButtonWidget.defaultProps = {
autofocus: false,
};
export default RadioButtonWidget;
+2 -1
View File
@@ -7,9 +7,10 @@ import { buildSchema, buildUISchema } from "./FormUtils";
import AsideSection from "@components/AsideSection";
import MainSection from "@components/MainSection";
import Checkboxes from "@components/Checkbox/Checkboxes";
import RadioButtonWidget from "@components/RadioButton/RadioButtonWidget";
const customWidgets = {
// "radio": Checkbox,
"radio": RadioButtonWidget,
"checkboxes": Checkboxes
};