-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[core] feat: new SegmentedControl component (#6454)
Co-authored-by: Charles <cperinet@palantir.com> Co-authored-by: Adi Dahiya <adahiya@palantir.com>
- Loading branch information
1 parent
b023644
commit 507f7c5
Showing
14 changed files
with
444 additions
and
22 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ | |
@page checkbox | ||
@page radio | ||
@page html-select | ||
@page segmented-control | ||
@page sliders | ||
@page switch | ||
|
||
|
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
54 changes: 54 additions & 0 deletions
54
packages/core/src/components/segmented-control/_segmented-control.scss
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,54 @@ | ||
@use "sass:math"; | ||
@import "../../common/variables"; | ||
|
||
.#{$ns}-segmented-control { | ||
background-color: $light-gray5; | ||
border-radius: $pt-border-radius; | ||
display: flex; | ||
gap: 3px; | ||
padding: 3px; | ||
|
||
&.#{$ns}-inline { | ||
display: inline-flex; | ||
} | ||
|
||
&.#{$ns}-fill { | ||
width: 100%; | ||
|
||
> .#{$ns}-button { | ||
flex-grow: 1; | ||
} | ||
} | ||
|
||
> .#{$ns}-button:not(.#{$ns}-minimal) { | ||
box-shadow: 0 0 0 1px $pt-divider-black-muted; | ||
|
||
&:not(.#{$ns}-intent-primary) { | ||
background-color: $white; | ||
|
||
.#{$ns}-dark & { | ||
background-color: $dark-gray5; | ||
} | ||
} | ||
} | ||
|
||
> .#{$ns}-button.#{$ns}-minimal { | ||
color: $pt-text-color-muted; | ||
|
||
.#{$ns}-dark & { | ||
color: $pt-dark-text-color-muted; | ||
} | ||
} | ||
|
||
> .#{$ns}-button.#{$ns}-minimal:disabled { | ||
color: $pt-text-color-disabled; | ||
|
||
.#{$ns}-dark & { | ||
color: $pt-dark-text-color-disabled; | ||
} | ||
} | ||
|
||
.#{$ns}-dark & { | ||
background-color: $dark-gray3; | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
packages/core/src/components/segmented-control/segmented-control.md
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,45 @@ | ||
--- | ||
tag: new | ||
--- | ||
|
||
@# Segmented control | ||
|
||
A **SegmentedControl** is a linear collection of buttons which allows a user to choose an option from multiple choices, | ||
similar to a [**Radio**](#core/components/radio) group. | ||
|
||
Compared to the [**ButtonGroup**](#core/components/button-group) component, **SegmentedControl** has affordances | ||
to signify a selection UI and a reduced visual weight which is appropriate for forms. | ||
|
||
@reactExample SegmentedControlExample | ||
|
||
@## Usage | ||
|
||
**SegmentedControl** can be used as either a controlled or uncontrolled component with the `value`, `defaultValue`, | ||
and `onChange` props. | ||
|
||
Options are specified as `OptionProps` objects, just like [RadioGroup](#core/components/radio.radiogroup) and | ||
[HTMLSelect](#core/components/html-select). | ||
|
||
```tsx | ||
<SegmentedControl | ||
options={[ | ||
{ | ||
label: "List", | ||
value: "list", | ||
}, | ||
{ | ||
label: "Grid", | ||
value: "grid", | ||
}, | ||
{ | ||
label: "Gallery", | ||
value: "gallery", | ||
}, | ||
]} | ||
defaultValue="list" | ||
/> | ||
``` | ||
|
||
@## Props interface | ||
|
||
@interface SegmentedControlProps |
149 changes: 149 additions & 0 deletions
149
packages/core/src/components/segmented-control/segmentedControl.tsx
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,149 @@ | ||
/* | ||
* Copyright 2023 Palantir Technologies, Inc. All rights reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import classNames from "classnames"; | ||
import * as React from "react"; | ||
|
||
import { Classes, Intent } from "../../common"; | ||
import { | ||
type ControlledValueProps, | ||
DISPLAYNAME_PREFIX, | ||
type OptionProps, | ||
type Props, | ||
removeNonHTMLProps, | ||
} from "../../common/props"; | ||
import { Button } from "../button/buttons"; | ||
|
||
export type SegmentedControlIntent = typeof Intent.NONE | typeof Intent.PRIMARY; | ||
|
||
/** | ||
* SegmentedControl component props. | ||
*/ | ||
export interface SegmentedControlProps | ||
extends Props, | ||
ControlledValueProps<string>, | ||
React.RefAttributes<HTMLDivElement> { | ||
/** | ||
* Whether the control should take up the full width of its container. | ||
* | ||
* @default false | ||
*/ | ||
fill?: boolean; | ||
|
||
/** | ||
* Whether the control should appear as an inline element. | ||
*/ | ||
inline?: boolean; | ||
|
||
/** | ||
* Whether this control should use large buttons. | ||
* | ||
* @default false | ||
*/ | ||
large?: boolean; | ||
|
||
/** | ||
* Visual intent to apply to the selected value. | ||
*/ | ||
intent?: SegmentedControlIntent; | ||
|
||
/** | ||
* List of available options. | ||
*/ | ||
options: Array<OptionProps<string>>; | ||
|
||
/** | ||
* Whether this control should use small buttons. | ||
* | ||
* @default false | ||
*/ | ||
small?: boolean; | ||
} | ||
|
||
/** | ||
* Segmented control component. | ||
* | ||
* @see https://blueprintjs.com/docs/#core/components/segmented-control | ||
*/ | ||
export const SegmentedControl: React.FC<SegmentedControlProps> = React.forwardRef((props, ref) => { | ||
const { | ||
className, | ||
defaultValue, | ||
fill, | ||
inline, | ||
intent, | ||
large, | ||
onValueChange, | ||
options, | ||
small, | ||
value: controlledValue, | ||
...htmlProps | ||
} = props; | ||
|
||
const [localValue, setLocalValue] = React.useState<string | undefined>(defaultValue); | ||
const selectedValue = controlledValue ?? localValue; | ||
|
||
const handleOptionClick = React.useCallback( | ||
(newSelectedValue: string, targetElement: HTMLElement) => { | ||
setLocalValue(newSelectedValue); | ||
onValueChange?.(newSelectedValue, targetElement); | ||
}, | ||
[onValueChange], | ||
); | ||
|
||
const classes = classNames(Classes.SEGMENTED_CONTROL, className, { | ||
[Classes.FILL]: fill, | ||
[Classes.INLINE]: inline, | ||
}); | ||
|
||
return ( | ||
<div className={classes} ref={ref} {...removeNonHTMLProps(htmlProps)}> | ||
{options.map(option => ( | ||
<SegmentedControlOption | ||
{...option} | ||
intent={intent} | ||
isSelected={selectedValue === option.value} | ||
key={option.value} | ||
large={large} | ||
onClick={handleOptionClick} | ||
small={small} | ||
/> | ||
))} | ||
</div> | ||
); | ||
}); | ||
SegmentedControl.defaultProps = { | ||
defaultValue: undefined, | ||
intent: Intent.NONE, | ||
}; | ||
SegmentedControl.displayName = `${DISPLAYNAME_PREFIX}.SegmentedControl`; | ||
|
||
interface SegmentedControlOptionProps | ||
extends OptionProps<string>, | ||
Pick<SegmentedControlProps, "intent" | "small" | "large"> { | ||
isSelected: boolean; | ||
onClick: (value: string, targetElement: HTMLElement) => void; | ||
} | ||
|
||
function SegmentedControlOption({ isSelected, label, onClick, value, ...buttonProps }: SegmentedControlOptionProps) { | ||
const handleClick = React.useCallback( | ||
(event: React.MouseEvent<HTMLElement>) => onClick?.(value, event.currentTarget), | ||
[onClick, value], | ||
); | ||
|
||
return <Button onClick={handleClick} minimal={!isSelected} text={label} {...buttonProps} />; | ||
} | ||
SegmentedControlOption.displayName = `${DISPLAYNAME_PREFIX}.SegmentedControlOption`; |
Oops, something went wrong.
507f7c5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[core] feat: new SegmentedControl component (#6454)
Build artifact links for this commit: documentation | landing | table | demoThis is an automated comment from the deploy-preview CircleCI job.