Skip to content

Commit 1acd3fe

Browse files
committed
feat: add badge count on filter button (in searchbar)
1 parent 7624970 commit 1acd3fe

File tree

1 file changed

+132
-108
lines changed

1 file changed

+132
-108
lines changed

src/components/SearchBar.tsx

+132-108
Original file line numberDiff line numberDiff line change
@@ -1,136 +1,160 @@
1+
import { AppIcon } from "./AppIcon";
12
import {
2-
Divider,
3-
IconButton,
4-
InputBase,
5-
InputBaseProps,
6-
Popover,
3+
Badge,
4+
Divider,
5+
IconButton,
6+
InputBase,
7+
InputBaseProps,
8+
Popover,
79
} from "@material-ui/core";
810
import React, { FC, useEffect, useRef, useState } from "react";
911
import styled from "styled-components";
10-
import { AppIcon } from "./AppIcon";
1112

1213
const SearchInput = styled(InputBase)`
13-
width: 100%;
14-
transition: width 75ms ease-in-out;
14+
width: 100%;
15+
transition: width 75ms ease-in-out;
1516
16-
input {
17-
color: black;
18-
}
17+
input {
18+
color: black;
19+
}
1920
20-
input::placeholder {
21-
color: rgba(0, 0, 0, 124);
22-
}
21+
input::placeholder {
22+
color: rgba(0, 0, 0, 124);
23+
}
2324
`;
2425

2526
const SearchInputWrapper = styled.div`
26-
display: flex;
27-
align-items: center;
28-
background: rgba(225, 225, 225, 0.2);
29-
color: white;
30-
width: max-content;
31-
border-radius: 2px;
32-
padding-left: 8px;
33-
height: 32px;
34-
transition: height 75ms ease-in-out;
35-
width:100%;
27+
display: flex;
28+
align-items: center;
29+
background: rgba(225, 225, 225, 0.2);
30+
color: white;
31+
width: max-content;
32+
border-radius: 2px;
33+
padding-left: 8px;
34+
height: 32px;
35+
transition: height 75ms ease-in-out;
36+
width: 100%;
3637
37-
&:focus-within {
38-
background: rgba(225, 225, 225, 0.5);
39-
height: 40px;
40-
}
38+
&:focus-within {
39+
background: rgba(225, 225, 225, 0.5);
40+
height: 40px;
41+
}
4142
`;
4243

4344
const FilterDropDown: FC<{
44-
FilterForm?: React.ReactNode;
45-
handleClick?: Function;
45+
FilterForm?: React.ReactNode;
46+
handleClick?: Function;
4647
}> = ({ FilterForm, handleClick, ...props }) => {
47-
const [open, setOpen] = useState(false);
48-
const btnRef = useRef<HTMLButtonElement>(null);
48+
const [open, setOpen] = useState(false);
49+
const btnRef = useRef<HTMLButtonElement>(null);
4950

50-
return (
51-
<>
52-
<IconButton
53-
ref={btnRef}
54-
size="small"
55-
style={{ color: "inherit" }}
56-
onClick={() => {
57-
(handleClick && handleClick());
58-
setOpen(true)
59-
}}
60-
{...props}
61-
>
62-
<AppIcon name="filter-variant" />
63-
</IconButton>
64-
<Popover
65-
open={open}
66-
anchorEl={btnRef.current}
67-
onClose={() => setOpen(false)}
68-
>
69-
{FilterForm}
70-
</Popover>
71-
</>
72-
);
51+
return (
52+
<>
53+
<IconButton
54+
ref={btnRef}
55+
size="small"
56+
style={{ color: "inherit" }}
57+
onClick={() => {
58+
handleClick && handleClick();
59+
setOpen(true);
60+
}}
61+
{...props}
62+
>
63+
<AppIcon name="filter-variant" />
64+
</IconButton>
65+
<Popover
66+
open={open}
67+
anchorEl={btnRef.current}
68+
onClose={() => setOpen(false)}
69+
>
70+
{FilterForm}
71+
</Popover>
72+
</>
73+
);
7374
};
7475

75-
export interface SearchBarProps extends InputBaseProps {
76-
/**
77-
* Called when the user clicks on the clear button.
78-
*/
79-
onClear?: Function;
76+
export interface SearchBarProps<
77+
T extends { dirty: boolean; dirtyByNumber: number }
78+
> extends InputBaseProps {
79+
/**
80+
* Called when the user clicks on the clear button.
81+
*/
82+
onClear?: Function;
8083

81-
/**
82-
* The form to use in the filter dropdown
83-
*/
84-
FilterForm?: React.ReactNode;
85-
onClickFilterButton?: Function;
84+
/**
85+
* The form to use in the filter dropdown
86+
*/
87+
FilterForm?: React.ReactNode;
88+
onClickFilterButton?: Function;
89+
store: T;
8690
}
8791

88-
export const SearchBar = React.forwardRef<HTMLInputElement, SearchBarProps>(
89-
({ onClear, FilterForm, defaultValue, onClickFilterButton, ...props }, ref) => {
90-
const [searchEmpty, setSearchEmpty] = useState(
91-
() => !defaultValue || defaultValue === ""
92-
);
93-
const inputRef = useRef<HTMLInputElement>(null);
92+
export const SearchBar = React.forwardRef<
93+
HTMLInputElement,
94+
SearchBarProps<any>
95+
>(
96+
(
97+
{ onClear, FilterForm, defaultValue, onClickFilterButton, store, ...props },
98+
ref
99+
) => {
100+
const [searchEmpty, setSearchEmpty] = useState(
101+
() => !defaultValue || defaultValue === ""
102+
);
103+
const inputRef = useRef<HTMLInputElement>(null);
104+
105+
// TODO: Props clear event instead
106+
const handleClick = () => {
107+
if (
108+
inputRef?.current?.value &&
109+
inputRef?.current.value !== "" &&
110+
onClear
111+
) {
112+
// Clear
113+
onClear();
114+
} else {
115+
inputRef?.current?.focus();
116+
}
117+
};
94118

95-
// TODO: Props clear event instead
96-
const handleClick = () => {
97-
if (
98-
inputRef?.current?.value &&
99-
inputRef?.current.value !== "" &&
100-
onClear
101-
) {
102-
// Clear
103-
onClear();
104-
} else {
105-
inputRef?.current?.focus();
106-
}
107-
};
119+
useEffect(() => {
120+
ref = inputRef;
121+
}, [ref]);
108122

109-
useEffect(() => {
110-
ref = inputRef;
111-
}, [ref]);
123+
return (
124+
<Badge
125+
color="secondary"
126+
invisible={!store.dirty}
127+
badgeContent={store.dirtyByNumber}
128+
>
129+
<SearchInputWrapper>
130+
<SearchInput
131+
{...props}
132+
onInput={(e) => {
133+
setSearchEmpty(
134+
inputRef.current === null || inputRef.current?.value === ""
135+
);
136+
props.onInput && props.onInput(e);
137+
}}
138+
inputRef={inputRef}
139+
inputProps={{ "aria-label": "search" }}
140+
/>
141+
<IconButton onClick={handleClick} color="inherit" size="small">
142+
<AppIcon name={searchEmpty ? "magnify" : "close"} />
143+
</IconButton>
112144

113-
return (
114-
<SearchInputWrapper>
115-
<SearchInput
116-
{...props}
117-
onInput={(e) => {
118-
setSearchEmpty(
119-
inputRef.current === null || inputRef.current?.value === ""
120-
);
121-
props.onInput && props.onInput(e);
122-
}}
123-
inputRef={inputRef}
124-
inputProps={{ "aria-label": "search" }}
125-
/>
126-
<IconButton onClick={handleClick} color="inherit" size="small">
127-
<AppIcon name={searchEmpty ? "magnify" : "close"} />
128-
</IconButton>
129-
<Divider orientation="vertical" flexItem />
130-
{FilterForm && <FilterDropDown FilterForm={FilterForm} handleClick={onClickFilterButton} />}
131-
</SearchInputWrapper>
132-
);
133-
}
145+
{FilterForm && (
146+
<>
147+
<Divider orientation="vertical" flexItem />
148+
<FilterDropDown
149+
FilterForm={FilterForm}
150+
handleClick={onClickFilterButton}
151+
/>
152+
</>
153+
)}
154+
</SearchInputWrapper>
155+
</Badge>
156+
);
157+
}
134158
);
135159

136160
export default SearchBar;

0 commit comments

Comments
 (0)