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 SimpleSelect.tsx from
8 :
9 : https://github.com/patternfly/patternfly-react/blob/v5/packages/react-templates/src/components/Select/SimpleSelect.tsx
10 :
11 : We don't use it directly from the @patternfly/react-templates node
12 : module since we might want to add features to it, and also to
13 : isolate us 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: "boot", value: 12 }, content: _("Boot number 12") }
26 :
27 : - Support for dividers.
28 :
29 : { decorator: "divider", key: "..." }
30 :
31 : - Support for headers.
32 :
33 : { decorator: "header", content: _("Also available"), key: "..." }
34 :
35 : */
36 :
37 : /* eslint-disable */
38 :
39 138 : import React from 'react';
40 : import {
41 : Select,
42 : SelectList,
43 : SelectOption,
44 : SelectOptionProps,
45 : SelectProps,
46 : } from '@patternfly/react-core/dist/esm/components/Select';
47 : import { Divider } from '@patternfly/react-core/dist/esm/components/Divider';
48 : import { MenuToggle, MenuToggleElement, MenuToggleProps } from '@patternfly/react-core/dist/esm/components/MenuToggle';
49 :
50 : import "cockpit-components-select.scss";
51 :
52 : export interface SimpleSelectDividerOption {
53 : decorator: "divider";
54 :
55 : key: string | number;
56 : };
57 :
58 : export interface SimpleSelectHeaderOption {
59 : decorator: "header";
60 :
61 : key: string | number;
62 : content: React.ReactNode;
63 : };
64 :
65 : export interface SimpleSelectMenuOption<T> extends Omit<SelectOptionProps, 'content'> {
66 : decorator?: undefined;
67 :
68 : /** Content of the select option. */
69 : content: React.ReactNode;
70 : /** Value of the select option. */
71 : value: T;
72 : }
73 :
74 : export type SimpleSelectOption<T> = SimpleSelectMenuOption<T> |
75 : SimpleSelectDividerOption |
76 : SimpleSelectHeaderOption;
77 :
78 : export interface SimpleSelectProps<T> extends Omit<SelectProps, 'toggle' | 'onSelect'> {
79 : /** Initial options of the select. */
80 : options: SimpleSelectOption<T>[];
81 : /** Selected option */
82 : selected: T;
83 : /** Callback triggered on selection. */
84 : onSelect: (selection: T) => void;
85 : /** Callback triggered when the select opens or closes. */
86 : onToggle?: (nextIsOpen: boolean) => void;
87 : /** Flag indicating the select should be disabled. */
88 : isDisabled?: boolean;
89 : /** Content of the toggle. Defaults to the selected option. */
90 : toggleContent?: React.ReactNode;
91 : /** Placeholder text for the select input. */
92 : placeholder?: string;
93 : /** Width of the toggle. */
94 : toggleWidth?: string;
95 : /** Additional props passed to the toggle. */
96 : toggleProps?: MenuToggleProps;
97 : }
98 :
99 29 : export function SimpleSelect<T>({
100 29 : options,
101 29 : selected,
102 29 : isDisabled = false,
103 29 : onSelect,
104 29 : onToggle,
105 29 : toggleContent,
106 29 : toggleWidth,
107 29 : toggleProps,
108 29 : placeholder = '',
109 29 : ...props
110 29 : }: SimpleSelectProps<T>) {
111 29 : const [isOpen, setIsOpen] = React.useState(false);
112 :
113 29 : const simpleSelectOptions = options.map(option => {
114 29 : if (option.decorator == "divider")
115 5 : return <Divider key={option.key} component="li" />;
116 :
117 29 : if (option.decorator == "header")
118 29 : return (
119 3 : <SelectOption key={option.key}
120 3 : isDisabled
121 3 : className="ct-select-header">
122 3 : {option.content}
123 3 : </SelectOption>
124 : );
125 :
126 29 : const { content, value, key, ...props } = option;
127 29 : return (
128 13 : <SelectOption value={value} key={key !== undefined ? key : `${value}`} {...props}>
129 29 : {content}
130 29 : </SelectOption>
131 : );
132 29 : });
133 :
134 11 : const onToggleClick = () => {
135 0 : onToggle && onToggle(!isOpen);
136 11 : setIsOpen(!isOpen);
137 11 : };
138 :
139 10 : const _onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: T | undefined) => {
140 10 : if (value) {
141 10 : onSelect(value);
142 10 : }
143 0 : onToggle && onToggle(true);
144 10 : setIsOpen(false);
145 10 : };
146 :
147 29 : let content: React.ReactNode = placeholder;
148 29 : if (toggleContent)
149 3 : content = toggleContent;
150 29 : else if (selected)
151 29 : content = options.find((o): o is SimpleSelectMenuOption<T> => !o.decorator && o.value == selected)?.content;
152 :
153 29 : const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
154 29 : <MenuToggle
155 29 : ref={toggleRef}
156 29 : onClick={onToggleClick}
157 29 : isExpanded={isOpen}
158 29 : isDisabled={isDisabled}
159 29 : style={
160 29 : {
161 29 : width: toggleWidth
162 29 : } as React.CSSProperties
163 : }
164 29 : {...toggleProps}
165 : >
166 29 : {content}
167 29 : </MenuToggle>
168 : );
169 :
170 29 : return (
171 29 : <Select
172 29 : isOpen={isOpen}
173 29 : selected={selected}
174 29 : onSelect={_onSelect}
175 0 : onOpenChange={(isOpen) => {
176 0 : onToggle && onToggle(isOpen);
177 0 : setIsOpen(isOpen);
178 0 : }}
179 29 : toggle={toggle}
180 29 : shouldFocusToggleOnSelect
181 29 : {...props}
182 : >
183 29 : <SelectList>{simpleSelectOptions}</SelectList>
184 29 : </Select>
185 : );
186 29 : };
187 :
188 138 : SimpleSelect.displayName = 'SimpleSelect';
|