/* * Copyright 2016 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 { createElement, forwardRef, useCallback, useEffect, useRef, useState } from "react"; import { Classes, mergeRefs } from "../../common"; import { DISPLAYNAME_PREFIX } from "../../common/props"; import type { ControlProps } from "./controlProps"; /** Internal props for Checkbox/Radio/Switch to render correctly. */ interface ControlInternalProps extends ControlProps { type: "checkbox" | "radio"; typeClassName: string; indicatorChildren?: React.ReactNode; } /** * Renders common control elements, with additional props to customize appearance. * This component is not exported and is only used within this module for `Checkbox`, `Radio`, and `Switch` below. */ const ControlInternal: React.FC = forwardRef( (props, ref) => { const { alignIndicator, children, className, indicatorChildren, inline, inputRef, label, labelElement, // eslint-disable-next-line @typescript-eslint/no-deprecated large, size = "medium", style, type, typeClassName, tagName = "label", ...htmlProps } = props; const classes = classNames( Classes.CONTROL, typeClassName, { [Classes.DISABLED]: htmlProps.disabled, [Classes.INLINE]: inline, }, Classes.alignmentClass(alignIndicator), Classes.sizeClass(size, { large }), className, ); return createElement( tagName, { className: classes, ref, style }, , {indicatorChildren}, label, labelElement, children, ); }, ); ControlInternal.displayName = `${DISPLAYNAME_PREFIX}.Control`; // // Switch // /** * Switch component props. */ export interface SwitchProps extends ControlProps { /** * Text to display inside the switch indicator when checked. * If `innerLabel` is provided and this prop is omitted, then `innerLabel` * will be used for both states. * * @default innerLabel */ innerLabelChecked?: string; /** * Text to display inside the switch indicator when unchecked. */ innerLabel?: string; } /** * Switch component. * * @see https://blueprintjs.com/docs/#core/components/switch */ export const Switch: React.FC = forwardRef((props, ref) => { const { innerLabelChecked, innerLabel, ...controlProps } = props; const switchLabels = innerLabel || innerLabelChecked ? [
{innerLabelChecked ? innerLabelChecked : innerLabel}
,
{innerLabel}
, ] : null; return ( ); }); Switch.displayName = `${DISPLAYNAME_PREFIX}.Switch`; // // Radio // /** * Radio component props. */ export type RadioProps = ControlProps; /** * Radio component. * * @see https://blueprintjs.com/docs/#core/components/radio */ export const Radio: React.FC = forwardRef((props, ref) => { return ; }); Radio.displayName = `${DISPLAYNAME_PREFIX}.Radio`; // // Checkbox // /** * Checkbox component props. */ export interface CheckboxProps extends ControlProps { /** Whether this checkbox is initially indeterminate (uncontrolled mode). */ defaultIndeterminate?: boolean; /** * Whether this checkbox is indeterminate, or "partially checked." * The checkbox will appear with a small dash instead of a tick to indicate that the value * is not exactly true or false. * * Note that this prop takes precendence over `checked`: if a checkbox is marked both * `checked` and `indeterminate` via props, it will appear as indeterminate in the DOM. */ indeterminate?: boolean; } /** * Checkbox component. * * @see https://blueprintjs.com/docs/#core/components/checkbox */ export const Checkbox: React.FC = forwardRef((props, ref) => { const { defaultIndeterminate, indeterminate, onChange, ...controlProps } = props; const [isIndeterminate, setIsIndeterminate] = useState(indeterminate || defaultIndeterminate || false); const localInputRef = useRef(null); const inputRef = mergeRefs(props.inputRef, localInputRef); const handleChange = useCallback( (evt: React.ChangeEvent) => { // update state immediately only if uncontrolled if (indeterminate === undefined) { setIsIndeterminate(evt.target.indeterminate); } // otherwise wait for props change. always invoke handler. onChange?.(evt); }, [indeterminate, onChange], ); useEffect(() => { if (indeterminate !== undefined) { setIsIndeterminate(indeterminate); } }, [indeterminate]); useEffect(() => { if (localInputRef.current != null) { localInputRef.current.indeterminate = isIndeterminate; } }, [localInputRef, isIndeterminate]); return ( ); }); Checkbox.displayName = `${DISPLAYNAME_PREFIX}.Checkbox`;