/* * 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 { forwardRef } from "react"; import { Alignment, Classes } from "../../common"; import { DISPLAYNAME_PREFIX, type HTMLInputProps } from "../../common/props"; import { Card, type CardProps } from "../card/card"; import type { CheckedControlProps, ControlProps } from "../forms/controlProps"; import { Checkbox, Radio, Switch } from "../forms/controls"; import { useCheckedControl } from "./useCheckedControl"; export type ControlKind = "switch" | "checkbox" | "radio"; /** * Subset of {@link CardProps} which can be used to adjust its behavior. */ type SupportedCardProps = Omit; /** * Subset of {@link ControlProps} which can be used to adjust its behavior. */ type SupportedControlProps = Pick< ControlProps, keyof CheckedControlProps | "alignIndicator" | "disabled" | "inputRef" | "label" | "value" >; /** * Shared props interface for all control card components, including `CheckboxCard`, `RadioCard`, and `SwitchCard`. * The label content may be specified as either `label` or `children`, but not both. */ export interface ControlCardProps extends SupportedCardProps, SupportedControlProps { /** * Which kind of form control to render inside the card. */ controlKind: ControlKind; // N.B. this is split out of the root properties in the interface because it would conflict with CardProps' HTMLDivProps /** * HTML input attributes to forward to the control `` element. */ inputProps?: Omit; /** * Whether the component should use "selected" Card styling when checked. * * @default true */ showAsSelectedWhenChecked?: boolean; } /** * ControlCard component, used to render a {@link Card} with a form control. * * @internal */ export const ControlCard: React.FC = forwardRef((props, ref) => { const { alignIndicator = Alignment.END, checked: _checked, children, className, controlKind, defaultChecked: _defaultChecked, disabled, inputProps, inputRef, label, onChange: _onChange, showAsSelectedWhenChecked = true, value, ...cardProps } = props; const { checked, onChange } = useCheckedControl(props); // use a container element to achieve a good flex layout const labelElement =
{children ?? label}
; const controlProps: ControlProps = { alignIndicator, checked, disabled, inline: true, inputRef, labelElement, onChange, value, ...inputProps, }; const classes = classNames(Classes.CONTROL_CARD, className, { [Classes.SELECTED]: showAsSelectedWhenChecked && checked, }); return ( {controlKind === "switch" ? ( ) : controlKind === "checkbox" ? ( ) : controlKind === "radio" ? ( ) : ( labelElement )} ); }); ControlCard.displayName = `${DISPLAYNAME_PREFIX}.ControlCard`;