Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new props to control clear search icon, support for icons as js… #9

Merged
merged 6 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,13 @@ Provides you with an object to replace the default icons used.
</td>
<td><code>undefined</code></td>
</tr>
<tr>
<td><code><b>clearSearchClick?:</b> function</code></td>
<td>
The callback function which will be triggered on clicking close icon inside search box
</td>
<td><code>undefined</code></td>
</tr>
</tbody>
</table>

Expand All @@ -257,6 +264,7 @@ the below code shows all the overridable styles:
SearchIcon?: {...styles},
ArrowIcon?: {...styles},
HiddenChipsIndicator?: {...styles},
ClearSearchIcon?: {...styles},
SelectedMenuItem?: (id) => ({...styles}),
UnSelectedMenuItem?: (id) => ({...styles}),
ChipComponent?: (id) => ({...styles}),
Expand All @@ -270,9 +278,10 @@ To customize the style of various components, you can use the following prop nam
- `Container`: Overrides the style of the multi-selection UI container.
- `CheckedIcon`: Overrides the style of the checked icon.
- `ChipCloseIcon`: Overrides the style of the close icon within the chip.
- `ClearSearchIcon`: Overrides the style of the close icon within the search box.
- `HelperText`: Overrides the style of the helper text.
- `HiddenChipsIndicator`: Overrides the style of the bubble indicating the number of hidden chips if the thresholdForBubble prop has a value.
- `InputBox`: Overrides the style of the box containing the chips and search bar.
- `InputBox`: Overrides the style of the box containing the chips and search bar. Can be used to style the placeholder if the search is hidden.
- `SearchIcon`: Overrides the style of the search icon.
- `SearchComponent`: Overrides the styles of the search component.
- `UnCheckedIcon`: Overrides the style of the unchecked box.
Expand All @@ -292,15 +301,17 @@ The following code displays the icons that can be customized
<MultiSelection
options={optionsArray}
icons={{
Search?: url,
ChipClose?: url,
Checked?: url,
Arrow?: url
Arrow?: url || JSX.Element,
ChipClose?: url || JSX.Element,
Checked?: url || JSX.Element,
ClearSearch?: url || JSX.Element,
Search?: url || JSX.Element
}}
/>
```

- `Arrow` - Overrides the down arrow(right)
- `ChipClose` - Overrides the chip close icon
- `Checked` - Overrides the checkbox checked icon
- `ClearSearch` - Overrides the close icon inside search box
- `Search` - Overrides the search icon
16 changes: 9 additions & 7 deletions src/lib/multi-select/chips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { MouseEvent, useMemo } from "react";
import closeIcon from "../../assets/x.svg";
import { ChipListPropType, OptionType } from "./types";
import { Elements, ElementsWithCallableStyle } from "./constants";
import { getStyles } from "./utils/utils";
import { getStyles, renderAsImage } from "./utils/utils";
import classes from "./styles.module.scss";

const Chips = (props: ChipListPropType): JSX.Element => {
Expand Down Expand Up @@ -50,12 +50,14 @@ const Chips = (props: ChipListPropType): JSX.Element => {
onClick(e, item.id)
}
>
<img
src={icon ?? closeIcon}
alt=""
className={classes.chipClose}
style={styles[Elements.ChipCloseIcon]}
/>
{renderAsImage(icon) ?
<img
src={icon as string ?? closeIcon}
alt=""
className={classes.chipClose}
style={styles[Elements.ChipCloseIcon]}
/>:
icon}
</button>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions src/lib/multi-select/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum Elements {
SearchIcon = "SearchIcon",
ArrowIcon = "ArrowIcon",
HiddenChipsIndicator = "HiddenChipsIndicator",
ClearSearchIcon = "ClearSearchIcon"
}

export enum ElementsWithCallableStyle {
Expand Down
30 changes: 18 additions & 12 deletions src/lib/multi-select/menuItems.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { getStyles } from "./utils/utils";
import React, { useMemo } from "react";
import CheckMark from "../../assets/CheckBox.svg";
import { getStyles, renderAsImage } from "./utils/utils";
import { ModalProps, OptionType } from "./types";
import {
DEFAULT_EMPTY_LIST_MESSAGE,
Expand All @@ -22,6 +23,9 @@ const OptionListingModal = (props: ModalProps): JSX.Element => {
onOptionClick,
styles = {}
} = props;

const showDefault = useMemo(()=> renderAsImage(icon), [icon]);

return (
<>
{list?.length && (list.length !== selectedIds.length || !hideSelected)
Expand All @@ -31,7 +35,7 @@ const OptionListingModal = (props: ModalProps): JSX.Element => {
!hideSelected) && (
<button
key={item.id}
className={classes.eachItem}
className={`${classes.eachItem} ${selectedIds.includes(item.id) && classes.selectedItem}`}
onClick={(): void => onOptionClick(item.id)}
style={getStyles(
!selectedIds.includes(item.id)
Expand All @@ -45,14 +49,16 @@ const OptionListingModal = (props: ModalProps): JSX.Element => {
>
{showCheckbox &&
(selectedIds.includes(item.id) ? (
<div
className={`${classes.checkbox} ${classes.icon}`}
style={{
backgroundImage: `url(${icon})`,
...styles[Elements.CheckedIcon]
}}
id="checked-checkbox"
/>
showDefault ?
<div
className={`${classes.checkbox} ${classes.icon}`}
style={{
backgroundImage: `url(${icon ?? CheckMark})`,
...styles[Elements.CheckedIcon]
}}
id="checked-checkbox"
/>
: icon
) : (
<div
className={`${classes.unchecked} ${classes.icon}`}
Expand All @@ -62,7 +68,7 @@ const OptionListingModal = (props: ModalProps): JSX.Element => {
))}
<div id="label">{item.name}</div>
</button>
)) || <></>
)) || <React.Fragment key={item.id}/>
)
: !isLoading &&
(renderEmptyItem || (
Expand Down
43 changes: 25 additions & 18 deletions src/lib/multi-select/multiSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { MouseEvent, useEffect, useMemo, useRef, useState } from "react";
import DownArrow from "../../assets/DropdownArrow.svg";
import CheckMark from "../../assets/CheckBox.svg";
import SearchComponent from "./searchComponent";
import { MultiSelectPropType, OptionType } from "./types";
import { DEFAULT_PLACEHOLDER, Elements } from "./constants";
import { renderAsImage } from "./utils/utils";
import classes from "./styles.module.scss";
import Chips from "./chips";
import MenuListing from "./menuItems";
Expand All @@ -28,11 +28,12 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => {
icons = {},
onSearch = undefined,
onItemClick = undefined,
setSelectedValues = undefined
setSelectedValues = undefined,
clearSearchClick = undefined
} = props;

const { Checked = CheckMark, Search, ChipClose, Arrow } = icons;

const { Checked, Search, ChipClose, Arrow, ClearSearch } = icons;
const arrowIcon = Arrow;
// to show/hide div containing the checkboxes
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
const [list, setList] = useState<OptionType[]>([]);
Expand All @@ -55,7 +56,7 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => {
}, [options]);

useEffect(() => {
if (typeof document!== undefined) {
if (typeof document!== 'undefined') {
document.addEventListener("mouseup", onMouseUp);
return () => document.removeEventListener("mouseup", onMouseUp);
}
Expand Down Expand Up @@ -131,15 +132,18 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => {
[options, selectedIds]
);

const onClick = (): void =>{
setShowAllChips(true);
setIsModalVisible(true);
}

return (
<div className={classes.container} style={styles[Elements.Container]}>
<div
className={`${classes.box} ${hasError && classes.errorBorder}`}
ref={intractableAreaRef}
style={styles[Elements.InputBox]}
onClick={(): void => {
setShowAllChips(true);
}}
onClick={onClick}
role="presentation"
>
<div className={classes.headSection}>
Expand All @@ -161,11 +165,12 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => {
onFocus={triggerModalOpen}
ref={inputRef}
icon={Search}
onCloseClick={clearSearchClick}
closeIcon={ClearSearch}
/>
)}
{hideSearch && !selectedIds.length && (
// same style for the search box is used
<div style={styles[Elements.SearchComponent]}>{placeholder}</div>
<div className={`${classes.searchInput} ${classes.label}`}>{placeholder}</div>
)}
</div>
<button
Expand All @@ -174,14 +179,16 @@ const MultiSelect = (props: MultiSelectPropType): JSX.Element => {
onClick={(e: MouseEvent<HTMLButtonElement>): void => onArrowClick(e)}
id="down-arrow"
>
<img
src={Arrow ?? DownArrow}
className={classes.rotation}
style={{
transform: `rotate(${isModalVisible ? "180deg" : "0deg"})`,
...styles[Elements.ArrowIcon]
}}
/>
{renderAsImage(arrowIcon)?
<img
src={arrowIcon as string ?? DownArrow}
className={classes.rotation}
style={{
transform: `rotate(${isModalVisible ? "180deg" : "0deg"})`,
...styles[Elements.ArrowIcon]
}}
/> :
arrowIcon}
</button>
</div>
{!isModalVisible && helperText && (
Expand Down
37 changes: 26 additions & 11 deletions src/lib/multi-select/searchComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { ForwardedRef, forwardRef, useEffect, useState } from "react";
import searchIcon from "../../assets/Search.svg";
import closeIcon from "../../assets/x-circle.svg";
import defaultCloseIcon from "../../assets/x-circle.svg";
import { renderAsImage } from './utils/utils'
import { SearchComponentPropType } from "./types";
import { Elements } from "./constants";
import classes from "./styles.module.scss";
Expand All @@ -9,24 +10,30 @@ const SearchComponent = (
props: SearchComponentPropType,
ref: ForwardedRef<HTMLInputElement>
): JSX.Element => {
const { onSearch, searchPlaceholder, styles = {}, onFocus, icon } = props;
const { onSearch, searchPlaceholder, styles = {}, onFocus, icon, onCloseClick, closeIcon } = props;
const [searchTerm, setSearchTerm] = useState<string>("");

useEffect(() => {
onSearch(searchTerm);
}, [searchTerm]);

const onCloseButtonClick = (): void => {
setSearchTerm("");
if(onCloseClick) onCloseClick();
}

return (
<div
className={classes.searchContainer}
style={styles[Elements.SearchComponent]}
>
<img
src={icon ?? searchIcon}
alt=""
className={classes.chipClose}
style={styles[Elements.SearchIcon]}
/>
{renderAsImage(icon)?
<img
src={icon as string ?? searchIcon}
alt=""
className={classes.chipClose}
style={styles[Elements.SearchIcon]}
/>: icon}
<input
type="text"
onChange={(e): void => setSearchTerm(e.target.value)}
Expand All @@ -41,9 +48,17 @@ const SearchComponent = (
<button
id="clear-search-button"
className={`${classes.buttonIcon} ${classes.icon}`}
style={{ backgroundImage: `url(${closeIcon})` }}
onClick={(): void => setSearchTerm("")}
/>
onClick={onCloseButtonClick}
>
{renderAsImage(closeIcon)?
<img
src={closeIcon as string ?? defaultCloseIcon}
alt=""
className={classes.chipClose}
style={styles[Elements.ClearSearchIcon]}
/> :
closeIcon}
</button>
)}
</div>
);
Expand Down
10 changes: 10 additions & 0 deletions src/lib/multi-select/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
padding: 0px 12px;
align-items: center;
position: relative;
flex: 1
}
.searchInput {
width: 100%;
Expand All @@ -15,6 +16,8 @@
font-size: inherit;
font-family: "Poppins";
background-color: inherit;
text-overflow: ellipsis;
min-width: 25px;
}
.icon {
width: 16px;
Expand Down Expand Up @@ -87,6 +90,9 @@
opacity: 0.7;
}
}
.selectedItem{
background-color: #D9E2F0;
}
.checkbox {
width: 20px;
height: 20px;
Expand Down Expand Up @@ -118,6 +124,7 @@
display: flex;
flex-wrap: wrap;
gap: 10px;
flex: 1
}
.error {
color: #ff0000;
Expand All @@ -137,3 +144,6 @@
.elevatedContent {
z-index: 1;
}
.label {
padding: 10px
}
Loading
Loading