Selector.tsx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. /*
  2. * Copyright 2021-2025 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, {
  14. useState,
  15. useCallback,
  16. useEffect,
  17. useMemo,
  18. useRef,
  19. CSSProperties,
  20. MouseEvent,
  21. ChangeEvent,
  22. SyntheticEvent,
  23. HTMLAttributes,
  24. } from "react";
  25. import Autocomplete from "@mui/material/Autocomplete";
  26. import Avatar from "@mui/material/Avatar";
  27. import Box from "@mui/material/Box";
  28. import Checkbox from "@mui/material/Checkbox";
  29. import Chip from "@mui/material/Chip";
  30. import FormControl from "@mui/material/FormControl";
  31. import FormControlLabel from "@mui/material/FormControlLabel";
  32. import FormGroup from "@mui/material/FormGroup";
  33. import FormLabel from "@mui/material/FormLabel";
  34. import InputLabel from "@mui/material/InputLabel";
  35. import List from "@mui/material/List";
  36. import ListItemButton from "@mui/material/ListItemButton";
  37. import ListItemIcon from "@mui/material/ListItemIcon";
  38. import ListItemText from "@mui/material/ListItemText";
  39. import ListItemAvatar from "@mui/material/ListItemAvatar";
  40. import MenuItem from "@mui/material/MenuItem";
  41. import OutlinedInput from "@mui/material/OutlinedInput";
  42. import Paper from "@mui/material/Paper";
  43. import Tooltip from "@mui/material/Tooltip";
  44. import Radio from "@mui/material/Radio";
  45. import RadioGroup from "@mui/material/RadioGroup";
  46. import Select, { SelectChangeEvent } from "@mui/material/Select";
  47. import TextField from "@mui/material/TextField";
  48. import { Theme, useTheme } from "@mui/material";
  49. import { useDrag, useDrop } from "react-dnd";
  50. import { doNotPropagateEvent, getSuffixedClassNames, getUpdateVar } from "./utils";
  51. import { createSendActionNameAction, createSendUpdateAction } from "../../context/taipyReducers";
  52. import {
  53. DragItem,
  54. dragSx,
  55. ItemProps,
  56. LovImage,
  57. paperBaseSx,
  58. SelTreeProps,
  59. showItem,
  60. SingleItem,
  61. useLovListMemo,
  62. } from "./lovUtils";
  63. import {
  64. useClassNames,
  65. useDispatch,
  66. useDispatchRequestUpdateOnFirstRender,
  67. useDynamicProperty,
  68. useModule,
  69. } from "../../utils/hooks";
  70. import { Icon } from "../../utils/icon";
  71. import { LovItem } from "../../utils/lov";
  72. import { getComponentClassName } from "./TaipyStyle";
  73. const MultipleItem = ({
  74. value,
  75. clickHandler,
  76. selectedValue,
  77. item,
  78. disabled,
  79. dragType = "",
  80. dropTypes,
  81. handleDrop,
  82. index = -1,
  83. lovVarName,
  84. targetId,
  85. }: ItemProps) => {
  86. const itemRef = useRef<HTMLDivElement>(null);
  87. const getDragItem = useCallback(
  88. () => (dragType && !disabled ? { id: value, index: -1 } : null),
  89. [dragType, disabled, value]
  90. );
  91. const [{ isDragging }, drag] = useDrag(
  92. () => ({
  93. type: dragType,
  94. item: getDragItem,
  95. collect: (monitor) => ({
  96. isDragging: monitor.isDragging(),
  97. }),
  98. end: (item: DragItem, monitor) => {
  99. const dropResult = monitor.getDropResult();
  100. if (dropResult) {
  101. handleDrop?.(item.id, item.index, lovVarName || "", item.targetId);
  102. }
  103. },
  104. }),
  105. [dragType, getDragItem, lovVarName, targetId]
  106. );
  107. const [, drop] = useDrop<DragItem, void, { handlerId: string }>(
  108. () => ({
  109. accept: dropTypes || "",
  110. hover: (item: DragItem) => {
  111. item.index = index;
  112. item.targetId = targetId;
  113. },
  114. }),
  115. [dropTypes, index, targetId]
  116. );
  117. drag(drop(itemRef));
  118. return (
  119. <ListItemButton
  120. onClick={clickHandler}
  121. data-id={value}
  122. dense
  123. disabled={disabled}
  124. ref={itemRef}
  125. sx={isDragging ? dragSx : undefined}
  126. >
  127. <ListItemIcon>
  128. <Checkbox
  129. disabled={disabled}
  130. edge="start"
  131. checked={selectedValue.includes(value)}
  132. tabIndex={-1}
  133. disableRipple
  134. />
  135. </ListItemIcon>
  136. {typeof item === "string" ? (
  137. <ListItemText primary={item} />
  138. ) : (
  139. <ListItemAvatar>
  140. <LovImage item={item} />
  141. </ListItemAvatar>
  142. )}
  143. </ListItemButton>
  144. );
  145. };
  146. const ITEM_HEIGHT = 48;
  147. const ITEM_PADDING_TOP = 8;
  148. const getMenuProps = (height?: string | number) => ({
  149. PaperProps: {
  150. style: {
  151. maxHeight: height || ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
  152. },
  153. },
  154. });
  155. const getStyles = (id: string, ids: readonly string[], theme: Theme) => ({
  156. fontWeight: ids.indexOf(id) === -1 ? theme.typography.fontWeightRegular : theme.typography.fontWeightMedium,
  157. });
  158. const getOptionLabel = (option: LovItem) => (typeof option.item === "string" ? option.item : option.item?.text) || "";
  159. const getOptionKey = (option: LovItem) => "" + option.id;
  160. const isOptionEqualToValue = (option: LovItem, value: LovItem) => option.id == value.id;
  161. const renderOption = (props: HTMLAttributes<HTMLLIElement>, option: LovItem) => (
  162. <li {...props} key={option.id}>
  163. {typeof option.item === "string" ? option.item : <LovImage item={option.item} />}
  164. </li>
  165. );
  166. const getLovItemsFromStr = (value: string | string[] | undefined, lovList: LovItem[], multiple: boolean) => {
  167. const val = multiple
  168. ? Array.isArray(value)
  169. ? value
  170. : [value]
  171. : Array.isArray(value) && value.length
  172. ? value[0]
  173. : value;
  174. return Array.isArray(val)
  175. ? (val.map((v) => lovList.find((item) => item.id == "" + v)).filter((i) => i) as LovItem[])
  176. : (val && lovList.find((item) => item.id == "" + val)) || null;
  177. };
  178. const renderBoxSx = {
  179. display: "flex",
  180. flexWrap: "wrap",
  181. gap: 0.5,
  182. width: "100%",
  183. } as CSSProperties;
  184. interface SelectorProps extends SelTreeProps {
  185. dropdown?: boolean;
  186. mode?: string;
  187. defaultSelectionMessage?: string;
  188. selectionMessage?: string;
  189. showSelectAll?: boolean;
  190. }
  191. const Selector = (props: SelectorProps) => {
  192. const {
  193. id,
  194. defaultValue = "",
  195. value,
  196. updateVarName = "",
  197. defaultLov = "",
  198. filter = false,
  199. propagate = true,
  200. lov,
  201. updateVars = "",
  202. width = "100%",
  203. height,
  204. valueById,
  205. mode = "",
  206. showSelectAll = false,
  207. } = props;
  208. const [searchValue, setSearchValue] = useState("");
  209. const [selectedValue, setSelectedValue] = useState<string[]>([]);
  210. const dispatch = useDispatch();
  211. const module = useModule();
  212. const theme = useTheme();
  213. const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
  214. const active = useDynamicProperty(props.active, props.defaultActive, true);
  215. const hover = useDynamicProperty(props.hoverText, props.defaultHoverText, undefined);
  216. const selectionMessage = useDynamicProperty(props.selectionMessage, props.defaultSelectionMessage, undefined);
  217. useDispatchRequestUpdateOnFirstRender(dispatch, id, module, updateVars, updateVarName);
  218. const isRadio = mode && mode.toLocaleLowerCase() == "radio";
  219. const isCheck = mode && mode.toLocaleLowerCase() == "check";
  220. const dropdown = isRadio || isCheck || props.dropdown === undefined ? false : props.dropdown;
  221. const multiple = isCheck ? true : isRadio || props.multiple === undefined ? false : props.multiple;
  222. const lovVarName = useMemo(() => getUpdateVar(updateVars, "lov"), [updateVars]);
  223. // Droppable area for drag and drop
  224. const dropTypes = useMemo(() => {
  225. if (props.dropTypes) {
  226. try {
  227. return JSON.parse(props.dropTypes);
  228. } catch (e) {
  229. console.error("Invalid dropTypes JSON string", e);
  230. }
  231. }
  232. return [];
  233. }, [props.dropTypes]);
  234. const [, dropRef] = useDrop(
  235. () => ({
  236. accept: dropTypes,
  237. hover: (item: DragItem) => {
  238. item.index = -1;
  239. item.targetId = id;
  240. },
  241. }),
  242. [dropTypes, id]
  243. );
  244. const handleDrop = useCallback(
  245. (itemId: string, dropIndex: number, targetVarName: string, targetId?: string) => {
  246. dispatch(
  247. createSendActionNameAction(props.onAction, module, {
  248. reason: "drop",
  249. source_var: lovVarName,
  250. source_id: id,
  251. item_id: itemId,
  252. drop_index: dropIndex,
  253. target_var: targetVarName,
  254. target_id: targetId,
  255. })
  256. );
  257. },
  258. [lovVarName, dispatch, module, props.onAction, id]
  259. );
  260. const lovList = useLovListMemo(lov, defaultLov);
  261. const listSx = useMemo(
  262. () => ({
  263. bgcolor: "transparent",
  264. overflowY: "auto",
  265. width: "100%",
  266. maxWidth: width,
  267. }),
  268. [width]
  269. );
  270. const heightSx = useMemo(() => {
  271. if (!height) {
  272. return undefined;
  273. }
  274. return {
  275. maxHeight: height,
  276. display: "flex",
  277. flexFlow: "column nowrap",
  278. overflowY: "auto",
  279. };
  280. }, [height]);
  281. const paperSx = useMemo(() => {
  282. let sx = paperBaseSx;
  283. if (height !== undefined) {
  284. sx = { ...sx, maxHeight: height };
  285. }
  286. return sx;
  287. }, [height]);
  288. const controlSx = useMemo(
  289. () => ({
  290. my: 1,
  291. mx: 0,
  292. width: width,
  293. "& .MuiFormControl-root": {
  294. maxWidth: "unset",
  295. my: 0,
  296. "& .MuiInputBase-root": { minHeight: 48, "& input": { minHeight: "unset" } },
  297. },
  298. }),
  299. [width]
  300. );
  301. useEffect(() => {
  302. if (value !== undefined && value !== null) {
  303. setSelectedValue(Array.isArray(value) ? value.map((v) => "" + v) : ["" + value]);
  304. setAutoValue(getLovItemsFromStr(value, lovList, multiple));
  305. } else if (defaultValue) {
  306. let parsedValue;
  307. try {
  308. parsedValue = JSON.parse(defaultValue);
  309. } catch {
  310. parsedValue = defaultValue;
  311. }
  312. setSelectedValue(Array.isArray(parsedValue) ? parsedValue : [parsedValue]);
  313. setAutoValue(getLovItemsFromStr(parsedValue, lovList, multiple));
  314. }
  315. }, [defaultValue, value, lovList, multiple]);
  316. const selectHandler = useCallback(
  317. (key: string) => {
  318. setSelectedValue((keys) => {
  319. if (multiple) {
  320. const newKeys = [...keys];
  321. const p = newKeys.indexOf(key);
  322. if (p === -1) {
  323. newKeys.push(key);
  324. } else {
  325. newKeys.splice(p, 1);
  326. }
  327. dispatch(
  328. createSendUpdateAction(
  329. updateVarName,
  330. newKeys,
  331. module,
  332. props.onChange,
  333. propagate,
  334. valueById ? undefined : lovVarName
  335. )
  336. );
  337. return newKeys;
  338. } else {
  339. dispatch(
  340. createSendUpdateAction(
  341. updateVarName,
  342. key,
  343. module,
  344. props.onChange,
  345. propagate,
  346. valueById ? undefined : lovVarName
  347. )
  348. );
  349. return [key];
  350. }
  351. });
  352. },
  353. [updateVarName, dispatch, multiple, propagate, lovVarName, valueById, props.onChange, module]
  354. );
  355. const clickHandler = useCallback(
  356. (evt: MouseEvent<HTMLElement>) => {
  357. if (active) {
  358. const { id: key = "" } = evt.currentTarget.dataset;
  359. selectHandler(key);
  360. }
  361. },
  362. [active, selectHandler]
  363. );
  364. const changeHandler = useCallback(
  365. (evt: ChangeEvent<HTMLInputElement>) => {
  366. if (active) {
  367. const { id: key = "" } = (evt.currentTarget as HTMLElement).parentElement?.dataset || {};
  368. selectHandler(key);
  369. }
  370. },
  371. [active, selectHandler]
  372. );
  373. const handleChange = useCallback(
  374. (event: SelectChangeEvent<typeof selectedValue>) => {
  375. const {
  376. target: { value },
  377. } = event;
  378. setSelectedValue(Array.isArray(value) ? value : [value]);
  379. dispatch(
  380. createSendUpdateAction(
  381. updateVarName,
  382. value,
  383. module,
  384. props.onChange,
  385. propagate,
  386. valueById ? undefined : lovVarName
  387. )
  388. );
  389. },
  390. [dispatch, updateVarName, propagate, lovVarName, valueById, props.onChange, module]
  391. );
  392. const handleCheckAllChange = useCallback(
  393. (event: SelectChangeEvent<HTMLInputElement>, checked: boolean) => {
  394. const sel = checked ? lovList.map((elt) => elt.id) : [];
  395. setSelectedValue(sel);
  396. dispatch(
  397. createSendUpdateAction(
  398. updateVarName,
  399. sel,
  400. module,
  401. props.onChange,
  402. propagate,
  403. valueById ? undefined : lovVarName
  404. )
  405. );
  406. },
  407. [lovList, dispatch, updateVarName, propagate, lovVarName, valueById, props.onChange, module]
  408. );
  409. const [autoValue, setAutoValue] = useState<LovItem | LovItem[] | null>(() => (multiple ? [] : null));
  410. const handleAutoChange = useCallback(
  411. (e: SyntheticEvent, sel: LovItem | LovItem[] | null) => {
  412. setAutoValue(sel);
  413. setSelectedValue(Array.isArray(sel) ? sel.map((item) => item.id) : sel ? [sel.id] : []);
  414. dispatch(
  415. createSendUpdateAction(
  416. updateVarName,
  417. Array.isArray(sel) ? sel.map((item) => item.id) : sel?.id,
  418. module,
  419. props.onChange,
  420. propagate,
  421. valueById ? undefined : lovVarName
  422. )
  423. );
  424. },
  425. [dispatch, updateVarName, propagate, lovVarName, valueById, props.onChange, module]
  426. );
  427. const handleDelete = useCallback(
  428. (e: React.SyntheticEvent) => {
  429. const id = e.currentTarget?.parentElement?.getAttribute("data-id");
  430. id &&
  431. setSelectedValue((oldKeys) => {
  432. const keys = oldKeys.filter((valId) => valId !== id);
  433. dispatch(
  434. createSendUpdateAction(
  435. updateVarName,
  436. keys,
  437. module,
  438. props.onChange,
  439. propagate,
  440. valueById ? undefined : lovVarName
  441. )
  442. );
  443. return keys;
  444. });
  445. },
  446. [updateVarName, propagate, dispatch, lovVarName, valueById, props.onChange, module]
  447. );
  448. const handleInput = useCallback((e: React.ChangeEvent<HTMLInputElement>) => setSearchValue(e.target.value), []);
  449. const dropdownValue = ((dropdown || isRadio) &&
  450. (multiple ? selectedValue : selectedValue.length ? selectedValue[0] : "")) as string[];
  451. return (
  452. <>
  453. {isRadio || isCheck ? (
  454. <FormControl sx={controlSx} className={`${className} ${getComponentClassName(props.children)}`}>
  455. {props.label ? <FormLabel>{props.label}</FormLabel> : null}
  456. <Tooltip title={hover || ""}>
  457. {isRadio ? (
  458. <RadioGroup
  459. value={dropdownValue}
  460. onChange={handleChange}
  461. className={getSuffixedClassNames(className, "-radio-group")}
  462. sx={heightSx}
  463. >
  464. {lovList.map((item) => (
  465. <FormControlLabel
  466. key={item.id}
  467. value={item.id}
  468. control={<Radio />}
  469. label={
  470. typeof item.item === "string" ? (
  471. item.item
  472. ) : (
  473. <LovImage item={item.item as Icon} />
  474. )
  475. }
  476. style={getStyles(item.id, selectedValue, theme)}
  477. disabled={!active}
  478. />
  479. ))}
  480. </RadioGroup>
  481. ) : (
  482. <FormGroup className={getSuffixedClassNames(className, "-check-group")} sx={heightSx}>
  483. {lovList.map((item) => (
  484. <FormControlLabel
  485. key={item.id}
  486. control={
  487. <Checkbox
  488. data-id={item.id}
  489. checked={selectedValue.includes(item.id)}
  490. onChange={changeHandler}
  491. />
  492. }
  493. label={
  494. typeof item.item === "string" ? (
  495. item.item
  496. ) : (
  497. <LovImage item={item.item as Icon} />
  498. )
  499. }
  500. style={getStyles(item.id, selectedValue, theme)}
  501. disabled={!active}
  502. ></FormControlLabel>
  503. ))}
  504. </FormGroup>
  505. )}
  506. </Tooltip>
  507. </FormControl>
  508. ) : dropdown ? (
  509. filter ? (
  510. <Tooltip title={hover || ""} placement="top">
  511. <Autocomplete
  512. id={id}
  513. disabled={!active}
  514. multiple={multiple}
  515. options={lovList}
  516. value={autoValue}
  517. onChange={handleAutoChange}
  518. getOptionLabel={getOptionLabel}
  519. getOptionKey={getOptionKey}
  520. isOptionEqualToValue={isOptionEqualToValue}
  521. sx={controlSx}
  522. className={`${className} ${getComponentClassName(props.children)}`}
  523. renderInput={(params) => <TextField {...params} label={props.label} margin="dense" />}
  524. renderOption={renderOption}
  525. />
  526. </Tooltip>
  527. ) : (
  528. <FormControl sx={controlSx} className={`${className} ${getComponentClassName(props.children)}`}>
  529. {props.label ? <InputLabel disableAnimation>{props.label}</InputLabel> : null}
  530. <Tooltip title={hover || ""} placement="top">
  531. <Select
  532. id={id}
  533. multiple={multiple}
  534. value={dropdownValue}
  535. onChange={handleChange}
  536. input={
  537. <OutlinedInput
  538. label={props.label}
  539. startAdornment={
  540. multiple && showSelectAll ? (
  541. <Tooltip
  542. title={
  543. selectedValue.length == lovList.length
  544. ? "Deselect All"
  545. : "Select All"
  546. }
  547. >
  548. <Checkbox
  549. disabled={!active}
  550. indeterminate={
  551. selectedValue.length > 0 &&
  552. selectedValue.length < lovList.length
  553. }
  554. checked={selectedValue.length == lovList.length}
  555. onChange={handleCheckAllChange}
  556. ></Checkbox>
  557. </Tooltip>
  558. ) : null
  559. }
  560. />
  561. }
  562. disabled={!active}
  563. renderValue={(selected) => (
  564. <Box sx={renderBoxSx}>
  565. {typeof selectionMessage === "string"
  566. ? selectionMessage
  567. : lovList
  568. .filter((it) =>
  569. Array.isArray(selected)
  570. ? selected.includes(it.id)
  571. : selected === it.id
  572. )
  573. .map((item, idx) => {
  574. if (multiple) {
  575. const chipProps = {} as Record<string, unknown>;
  576. if (typeof item.item === "string") {
  577. chipProps.label = item.item;
  578. } else {
  579. chipProps.label = item.item.text || "";
  580. chipProps.avatar = <Avatar src={item.item.path} />;
  581. }
  582. return (
  583. <Chip
  584. key={item.id}
  585. {...chipProps}
  586. onDelete={handleDelete}
  587. data-id={item.id}
  588. onMouseDown={doNotPropagateEvent}
  589. disabled={!active}
  590. />
  591. );
  592. } else if (idx === 0) {
  593. return typeof item.item === "string" ? (
  594. item.item
  595. ) : (
  596. <LovImage item={item.item} />
  597. );
  598. } else {
  599. return null;
  600. }
  601. })}
  602. </Box>
  603. )}
  604. MenuProps={getMenuProps(height)}
  605. >
  606. {lovList.map((item) => (
  607. <MenuItem
  608. key={item.id}
  609. value={item.id}
  610. style={getStyles(item.id, selectedValue, theme)}
  611. disabled={item.id === null}
  612. >
  613. {typeof item.item === "string" ? (
  614. item.item
  615. ) : (
  616. <LovImage item={item.item as Icon} />
  617. )}
  618. </MenuItem>
  619. ))}
  620. </Select>
  621. </Tooltip>
  622. </FormControl>
  623. )
  624. ) : (
  625. <FormControl sx={controlSx} className={`${className} ${getComponentClassName(props.children)}`}>
  626. {props.label ? (
  627. <InputLabel disableAnimation className="static-label">
  628. {props.label}
  629. </InputLabel>
  630. ) : null}
  631. <Tooltip title={hover || ""}>
  632. <Paper sx={paperSx}>
  633. {filter ? (
  634. <Box>
  635. <OutlinedInput
  636. margin="dense"
  637. placeholder="Search field"
  638. value={searchValue}
  639. onChange={handleInput}
  640. disabled={!active}
  641. startAdornment={
  642. multiple && showSelectAll ? (
  643. <Tooltip
  644. title={
  645. selectedValue.length == lovList.length
  646. ? "Deselect All"
  647. : "Select All"
  648. }
  649. >
  650. <Checkbox
  651. disabled={!active}
  652. indeterminate={
  653. selectedValue.length > 0 &&
  654. selectedValue.length < lovList.length
  655. }
  656. checked={selectedValue.length == lovList.length}
  657. onChange={handleCheckAllChange}
  658. ></Checkbox>
  659. </Tooltip>
  660. ) : null
  661. }
  662. />
  663. </Box>
  664. ) : multiple && showSelectAll ? (
  665. <Box paddingLeft={1}>
  666. <FormControlLabel
  667. control={
  668. <Checkbox
  669. disabled={!active}
  670. indeterminate={
  671. selectedValue.length > 0 && selectedValue.length < lovList.length
  672. }
  673. checked={selectedValue.length == lovList.length}
  674. onChange={handleCheckAllChange}
  675. ></Checkbox>
  676. }
  677. label={selectedValue.length == lovList.length ? "Deselect All" : "Select All"}
  678. />
  679. </Box>
  680. ) : null}
  681. <List sx={listSx} id={id} ref={dropRef}>
  682. {lovList.map((elt, idx) =>
  683. showItem(elt, searchValue) ? (
  684. multiple ? (
  685. <MultipleItem
  686. key={elt.id}
  687. value={elt.id}
  688. item={elt.item}
  689. selectedValue={selectedValue}
  690. clickHandler={clickHandler}
  691. disabled={!active}
  692. dragType={props.dragType}
  693. dropTypes={dropTypes}
  694. index={idx}
  695. handleDrop={handleDrop}
  696. lovVarName={lovVarName}
  697. targetId={id}
  698. />
  699. ) : (
  700. <SingleItem
  701. key={elt.id}
  702. value={elt.id}
  703. item={elt.item}
  704. selectedValue={selectedValue}
  705. clickHandler={clickHandler}
  706. disabled={!active}
  707. dragType={props.dragType}
  708. dropTypes={dropTypes}
  709. index={idx}
  710. handleDrop={handleDrop}
  711. lovVarName={lovVarName}
  712. targetId={id}
  713. />
  714. )
  715. ) : null
  716. )}
  717. </List>
  718. </Paper>
  719. </Tooltip>
  720. </FormControl>
  721. )}
  722. {props.children}
  723. </>
  724. );
  725. };
  726. export default Selector;