Line data Source code
1 : /*
2 : SPDX-License-Identifier: MIT
3 :
4 : Copyright (c) 2019 Red Hat, Inc.
5 : */
6 :
7 : /* This is a copy of MultiTypeaheadSelect.tsx from
8 :
9 : https://github.com/patternfly/patternfly-react/blob/v5/packages/react-templates/src/components/Select/CheckboxSelect.tsx
10 :
11 : We don't use it directly from the @patternfly/react-templates node
12 : module since we want to add features to it, and also to isolate us
13 : from gratuitous upstream changes.
14 :
15 : Our changes:
16 :
17 : - The selection is controlled from the outside and not maintained
18 : as internal state. This is how things should work with React.
19 :
20 : - The selected value can be any JavaScript value, not just a string
21 : or number. Normally, the (stringified) value is used as a React
22 : key. If your values are not suitable for this because they are
23 : neither strings nor numbers, you need to provide an explicit key.
24 :
25 : { key: 12, value: { param: "filestate", value: "enabled" }, content: _("Enabled") }
26 :
27 : - The badge is always maintained by this component, in a way that
28 : avoids layout flicker.
29 :
30 : */
31 :
32 : /* eslint-disable */
33 :
34 44 : import React from 'react';
35 : import { Badge } from "@patternfly/react-core/dist/esm/components/Badge/index.js";
36 : import { Select, SelectList, SelectOption, SelectOptionProps, SelectProps } from "@patternfly/react-core/dist/esm/components/Select/index.js";
37 : import { MenuToggle, MenuToggleElement, MenuToggleProps } from "@patternfly/react-core/dist/esm/components/MenuToggle/index.js";
38 :
39 : export interface CheckboxSelectOption<T> extends Omit<SelectOptionProps, 'content'> {
40 : /** Content of the select option. */
41 : content: React.ReactNode;
42 : /** Value of the select option. */
43 : value: T;
44 : }
45 :
46 : export interface CheckboxSelectProps<T> extends Omit<SelectProps, 'toggle' | 'onSelect'> {
47 : /** Options of the select. */
48 : options?: CheckboxSelectOption<T>[];
49 : /** Currently checked options */
50 : selected: T[];
51 : /** Callback triggered when checking or unchecking an option. */
52 : onSelect: (value: T, checked: boolean) => void;
53 : /** Callback triggered when the select opens or closes. */
54 : onToggle?: (nextIsOpen: boolean) => void;
55 : /** Flag indicating the select should be disabled. */
56 : isDisabled?: boolean;
57 : /** Content of the toggle. */
58 : toggleContent: React.ReactNode;
59 : /** Width of the toggle. */
60 : toggleWidth?: string;
61 : /** Additional props passed to the toggle. */
62 : toggleProps?: MenuToggleProps;
63 : /** Is there a badge with the selected options count? */
64 : noBadge?: boolean,
65 : }
66 :
67 37 : export function CheckboxSelect<T>({
68 37 : options,
69 37 : selected,
70 37 : isDisabled = false,
71 37 : onSelect,
72 37 : onToggle,
73 37 : toggleContent,
74 37 : toggleWidth,
75 37 : toggleProps,
76 37 : noBadge = false,
77 37 : ...props
78 37 : }: CheckboxSelectProps<T>) {
79 37 : const [isOpen, setIsOpen] = React.useState(false);
80 :
81 37 : const checkboxSelectOptions = options?.map((option) => {
82 37 : const { content, value, key, ...props } = option;
83 37 : const isSelected = selected.includes(value);
84 37 : return (
85 3 : <SelectOption value={value} key={key !== undefined ? key : `${value}`}
86 37 : hasCheckbox isSelected={isSelected} {...props}>
87 37 : {content}
88 37 : </SelectOption>
89 : );
90 37 : });
91 :
92 2 : const onToggleClick = () => {
93 0 : onToggle && onToggle(!isOpen);
94 2 : setIsOpen(!isOpen);
95 2 : };
96 :
97 2 : const _onSelect = (event: React.MouseEvent<Element, MouseEvent> | undefined, value: T | undefined) => {
98 2 : if (value && event) {
99 2 : onSelect(value, (event.target as HTMLInputElement).checked);
100 2 : }
101 2 : };
102 :
103 37 : const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
104 37 : <MenuToggle
105 37 : ref={toggleRef}
106 37 : onClick={onToggleClick}
107 37 : isExpanded={isOpen}
108 37 : isDisabled={isDisabled}
109 37 : style={
110 37 : {
111 37 : width: toggleWidth
112 37 : } as React.CSSProperties
113 : }
114 37 : {...toggleProps}
115 : >
116 37 : {toggleContent}
117 37 : {!noBadge &&
118 4 : <Badge style={{ visibility: selected.length > 0 ? "visible" : "hidden" }} isRead>
119 27 : {selected.length}
120 27 : </Badge>}
121 37 : </MenuToggle>
122 : );
123 :
124 37 : return (
125 37 : <Select
126 37 : isOpen={isOpen}
127 37 : selected={selected}
128 37 : onSelect={_onSelect}
129 1 : onOpenChange={(isOpen) => {
130 0 : onToggle && onToggle(isOpen);
131 1 : setIsOpen(isOpen);
132 1 : }}
133 37 : toggle={toggle}
134 37 : role="menu"
135 37 : {...props}
136 : >
137 37 : <SelectList>{checkboxSelectOptions}</SelectList>
138 37 : </Select>
139 : );
140 37 : };
|