@blueprintjs/core
Version:
Core styles & components
106 lines • 6.04 kB
JavaScript
/*
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SegmentedControl = void 0;
const tslib_1 = require("tslib");
const classnames_1 = tslib_1.__importDefault(require("classnames"));
const React = tslib_1.__importStar(require("react"));
const common_1 = require("../../common");
const props_1 = require("../../common/props");
const buttons_1 = require("../button/buttons");
/**
* Segmented control component.
*
* @see https://blueprintjs.com/docs/#core/components/segmented-control
*/
exports.SegmentedControl = React.forwardRef((props, ref) => {
const { className, defaultValue, fill, inline, intent = common_1.Intent.NONE,
// eslint-disable-next-line @typescript-eslint/no-deprecated
large, onValueChange, options, role = "radiogroup", size = "medium",
// eslint-disable-next-line @typescript-eslint/no-deprecated
small, value: controlledValue, ...htmlProps } = props;
const [localValue, setLocalValue] = React.useState(defaultValue);
const selectedValue = controlledValue !== null && controlledValue !== void 0 ? controlledValue : localValue;
const outerRef = React.useRef(null);
const handleOptionClick = React.useCallback((newSelectedValue, targetElement) => {
setLocalValue(newSelectedValue);
onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(newSelectedValue, targetElement);
}, [onValueChange]);
const handleKeyDown = React.useCallback((e) => {
var _a;
if (role === "radiogroup" || role === "menu") {
// in a `radiogroup`, arrow keys select next item, not tab key.
const direction = common_1.Utils.getArrowKeyDirection(e, ["ArrowLeft", "ArrowUp"], ["ArrowRight", "ArrowDown"]);
const outerElement = outerRef.current;
if (direction === undefined || !outerElement)
return;
const focusedElement = (_a = common_1.Utils.getActiveElement(outerElement)) === null || _a === void 0 ? void 0 : _a.closest("button");
if (!focusedElement)
return;
// must rely on DOM state because we have no way of mapping `focusedElement` to a React.JSX.Element
const enabledOptionElements = Array.from(outerElement.querySelectorAll("button:not(:disabled)"));
const focusedIndex = enabledOptionElements.indexOf(focusedElement);
if (focusedIndex < 0)
return;
e.preventDefault();
// auto-wrapping at 0 and `length`
const newIndex = (focusedIndex + direction + enabledOptionElements.length) % enabledOptionElements.length;
const newOption = enabledOptionElements[newIndex];
newOption.click();
newOption.focus();
}
}, [outerRef, role]);
const classes = (0, classnames_1.default)(common_1.Classes.SEGMENTED_CONTROL, className, {
[common_1.Classes.FILL]: fill,
[common_1.Classes.INLINE]: inline,
});
const isAnySelected = options.some(option => selectedValue === option.value);
const buttonRole = ({
/* eslint-disable sort-keys */
radiogroup: "radio",
menu: "menuitemradio",
group: undefined,
toolbar: undefined,
/* eslint-enable sort-keys */
})[role];
return (React.createElement("div", { ...(0, props_1.removeNonHTMLProps)(htmlProps), role: role, onKeyDown: handleKeyDown, className: classes, ref: (0, common_1.mergeRefs)(ref, outerRef) }, options.map((option, index) => {
const isSelected = selectedValue === option.value;
return (React.createElement(SegmentedControlOption, { ...option, intent: intent, isSelected: isSelected, key: option.value,
// eslint-disable-next-line @typescript-eslint/no-deprecated
large: large, onClick: handleOptionClick, size: size,
// eslint-disable-next-line @typescript-eslint/no-deprecated
small: small, role: buttonRole, ...(role === "radiogroup" || role === "menu"
? {
"aria-checked": isSelected,
// "roving tabIndex" on a radiogroup: https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
// `!isAnySelected` accounts for case where no value is currently selected
// (passed value/defaultValue is not one of the values of the passed options.)
// In this case, set first item to be tabbable even though it's unselected.
tabIndex: isSelected || (index === 0 && !isAnySelected) ? 0 : -1,
}
: {
"aria-pressed": isSelected,
}) }));
})));
});
exports.SegmentedControl.displayName = `${props_1.DISPLAYNAME_PREFIX}.SegmentedControl`;
function SegmentedControlOption({ isSelected, label, onClick, value, ...buttonProps }) {
const handleClick = React.useCallback((event) => onClick === null || onClick === void 0 ? void 0 : onClick(value, event.currentTarget), [onClick, value]);
return (React.createElement(buttons_1.Button, { ...buttonProps, onClick: handleClick, text: label !== null && label !== void 0 ? label : value, variant: !isSelected ? "minimal" : undefined }));
}
SegmentedControlOption.displayName = `${props_1.DISPLAYNAME_PREFIX}.SegmentedControlOption`;
//# sourceMappingURL=segmentedControl.js.map
;