Skip to content
Snippets Groups Projects
Unverified Commit 3fc29afe authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Add SegmentedControl component (#16885)

* Add basic segmented control component

* Specify segmented control's selected option color

* Update SegmentedControl's padding

* Use `PropTypes.node` instead of a bunch of types

* Remove `aria-selected` from CSS
parent 37f9c8ed
No related branches found
No related tags found
No related merge requests found
......@@ -92,6 +92,7 @@ function Radio({
xspace={xspace}
yspace={yspace}
onClick={e => onChange(optionValueFn(option))}
aria-selected={selected}
>
{option.icon && <Icon name={option.icon} mr={1} />}
<input
......
......@@ -13,7 +13,6 @@ const BaseList = styled.ul`
const BaseItem = styled.li.attrs({
mr: props => (!props.vertical && !props.last ? props.xspace : null),
mb: props => (props.vertical && !props.last ? props.yspace : null),
"aria-selected": props => props.selected,
})`
${space}
display: flex;
......
import React, { useState } from "react";
import { SegmentedControl } from "metabase/components/SegmentedControl";
export const component = SegmentedControl;
export const category = "input";
export const description = `
Radio-like segmented control input
`;
const SIMPLE_OPTIONS = [
{ name: "Gadget", value: 0 },
{ name: "Gizmo", value: 1 },
];
const OPTIONS_WITH_ICONS = [
{ name: "Gadget", value: 0, icon: "lightbulb" },
{ name: "Gizmo", value: 1, icon: "folder" },
{ name: "Doohickey", value: 2, icon: "insight" },
];
const OPTIONS_WITH_COLORS = [
{
name: "Gadget",
value: 0,
icon: "lightbulb",
selectedColor: "accent1",
},
{ name: "Gizmo", value: 1, icon: "folder", selectedColor: "accent2" },
{ name: "Doohickey", value: 2, icon: "insight" },
];
function SegmentedControlDemo(props) {
const [value, setValue] = useState(0);
return (
<SegmentedControl
{...props}
value={value}
onChange={val => setValue(val)}
/>
);
}
export const examples = {
default: <SegmentedControlDemo options={SIMPLE_OPTIONS} />,
icons: <SegmentedControlDemo options={OPTIONS_WITH_ICONS} />,
colored: <SegmentedControlDemo options={OPTIONS_WITH_COLORS} />,
};
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import _ from "underscore";
import Icon from "metabase/components/Icon";
import { SegmentedList, SegmentedItem } from "./SegmentedControl.styled";
const optionShape = PropTypes.shape({
name: PropTypes.node.isRequired,
value: PropTypes.any.isRequired,
icon: PropTypes.string,
// Expects a color alias, not a color code
// Example: brand, accent1, success
// Won't work: red, #000, rgb(0, 0, 0)
selectedColor: PropTypes.string,
});
const propTypes = {
name: PropTypes.string,
value: PropTypes.any,
options: PropTypes.arrayOf(optionShape).isRequired,
onChange: PropTypes.func,
};
export function SegmentedControl({
name: nameFromProps,
value,
options,
onChange,
...props
}) {
const id = useMemo(() => _.uniqueId("radio-"), []);
const name = nameFromProps || id;
return (
<SegmentedList {...props}>
{options.map((option, index) => {
const isSelected = option.value === value;
const isFirst = index === 0;
const isLast = index === options.length - 1;
return (
<SegmentedItem
key={option.value}
isSelected={isSelected}
isFirst={isFirst}
isLast={isLast}
onClick={e => onChange(option.value)}
selectedColor={option.selectedColor || "brand"}
>
{option.icon && <Icon name={option.icon} mr={1} />}
<input
id={`${name}-${option.value}`}
className="Form-radio"
type="radio"
name={name}
value={option.value}
checked={isSelected}
/>
<span>{option.name}</span>
</SegmentedItem>
);
})}
</SegmentedList>
);
}
SegmentedControl.propTypes = propTypes;
import styled from "styled-components";
import { color } from "metabase/lib/colors";
const BORDER_RADIUS = "8px";
export const SegmentedList = styled.ul`
display: flex;
`;
export const SegmentedItem = styled.li`
display: flex;
align-items: center;
font-weight: bold;
cursor: pointer;
color: ${props => (props.isSelected ? color(props.selectedColor) : null)};
padding: 6px 12px;
border: 1px solid ${color("border")};
border-right-width: ${props => (props.isLast ? "1px" : 0)};
border-top-left-radius: ${props => (props.isFirst ? BORDER_RADIUS : 0)};
border-bottom-left-radius: ${props => (props.isFirst ? BORDER_RADIUS : 0)};
border-top-right-radius: ${props => (props.isLast ? BORDER_RADIUS : 0)};
border-bottom-right-radius: ${props => (props.isLast ? BORDER_RADIUS : 0)};
:hover {
color: ${props => (!props.isSelected ? color(props.selectedColor) : null)};
}
`;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment