SPDX-License-Identifier: MIT
Copyright (c) 2019 Red Hat, Inc.
*/
https://github.com/patternfly/patternfly-react/blob/v5/packages/react-templates/src/components/Select/CheckboxSelect.tsx
We don't use it directly from the @patternfly/react-templates node
module since we want to add features to it, and also to isolate us
from gratuitous upstream changes.
Our changes:
- The selection is controlled from the outside and not maintained
as internal state. This is how things should work with React.
- The selected value can be any JavaScript value, not just a string
or number. Normally, the (stringified) value is used as a React
key. If your values are not suitable for this because they are
neither strings nor numbers, you need to provide an explicit key.
{ key: 12, value: { param: "filestate", value: "enabled" }, content: _("Enabled") }
- The badge is always maintained by this component, in a way that
avoids layout flicker.
*/
import React from 'react';
import { Badge } from "@patternfly/react-core/dist/esm/components/Badge/index.js";
import { Select, SelectList, SelectOption, SelectOptionProps, SelectProps } from "@patternfly/react-core/dist/esm/components/Select/index.js";
import { MenuToggle, MenuToggleElement, MenuToggleProps } from "@patternfly/react-core/dist/esm/components/MenuToggle/index.js";
export interface CheckboxSelectOption<T> extends Omit<SelectOptionProps, 'content'> {
content: React.ReactNode;
value: T;
}
export interface CheckboxSelectProps<T> extends Omit<SelectProps, 'toggle' | 'onSelect'> {
options?: CheckboxSelectOption<T>[];
selected: T[];
onSelect: (value: T, checked: boolean) => void;
onToggle?: (nextIsOpen: boolean) => void;
isDisabled?: boolean;
toggleContent: React.ReactNode;
toggleWidth?: string;
toggleProps?: MenuToggleProps;
noBadge?: boolean,
}
export function CheckboxSelect<T>({
options,
selected,
isDisabled = false,
onSelect,
onToggle,
toggleContent,
toggleWidth,
toggleProps,
noBadge = false,
...props
}: CheckboxSelectProps<T>) {
const [isOpen, setIsOpen] = React.useState(false);
const checkboxSelectOptions = options?.map((option) => {
const { content, value, key, ...props } = option;
const isSelected = selected.includes(value);
return (
<SelectOption value={value} key={key !== undefined ? key : `${value}`}
hasCheckbox isSelected={isSelected} {...props}>
{content}
</SelectOption>
);
});
const onToggleClick = () => {
onToggle && onToggle(!isOpen);
setIsOpen(!isOpen);
};
const _onSelect = (event: React.MouseEvent<Element, MouseEvent> | undefined, value: T | undefined) => {
if (value && event) {
onSelect(value, (event.target as HTMLInputElement).checked);
}
};
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
onClick={onToggleClick}
isExpanded={isOpen}
isDisabled={isDisabled}
style={
{
width: toggleWidth
} as React.CSSProperties
}
{...toggleProps}
>
{toggleContent}
{!noBadge &&
<Badge style={{ visibility: selected.length > 0 ? "visible" : "hidden" }} isRead>
{selected.length}
</Badge>}
</MenuToggle>
);
return (
<Select
isOpen={isOpen}
selected={selected}
onSelect={_onSelect}
onOpenChange={(isOpen) => {
onToggle && onToggle(isOpen);
setIsOpen(isOpen);
}}
toggle={toggle}
role="menu"
{...props}
>
<SelectList>{checkboxSelectOptions}</SelectList>
</Select>
);
};