@@ -11,60 +11,69 @@ import { Typography } from './typography'
1111
1212import { cn } from '../../utils/cn'
1313
14- export type FilterItem = {
15- column : string
16- value : string
17- isSelected ?: boolean
14+ export type FilterOption < T extends string = string > = {
15+ value : T
1816 label : string
17+ isSelected ?: boolean
1918 description ?: string
2019}
2120
22- interface FilterProps {
23- items : FilterItem [ ]
24- onChange : ( items : FilterItem [ ] ) => void
21+ export type FilterOptions = Record < string , FilterOption [ ] >
22+
23+ export function getSelectedValues < T extends string > ( options : FilterOption < T > [ ] ) {
24+ return options . filter ( ( option ) => option . isSelected ) . map ( ( option ) => option . value )
2525}
2626
27- export function getSelectedValues ( column : string , items : FilterItem [ ] ) {
28- return items . filter ( ( item ) => item . column === column && item . isSelected ) . map ( ( item ) => item . value )
27+ export interface FilterProps {
28+ options : FilterOptions
29+ onChange : ( options : FilterOptions ) => void
2930}
3031
31- export function Filter ( { items , onChange } : FilterProps ) {
32+ export function Filter ( { options , onChange } : FilterProps ) {
3233 const [ openModal , setOpenModal ] = React . useState ( false )
33- const [ internalItems , setInternalItems ] = React . useState ( items )
34+ const [ internalOptions , setInternalOptions ] = React . useState ( options )
3435 const [ selectedColumn , setSelectedColumn ] = React . useState < string | null > (
35- items . length > 0 ? items [ 0 ] . column : null
36+ Object . keys ( options ) [ 0 ] ?? null
3637 )
3738
38- function toggleItem ( column : string , value : string ) {
39- const newItems = internalItems . map ( ( item ) => ( {
40- ...item ,
41- isSelected :
42- item . column === column && item . value === value ? ! item . isSelected : item . isSelected ,
43- } ) )
44- setInternalItems ( newItems )
39+ function toggleItem ( column : string , label : string ) {
40+ setInternalOptions ( ( prev ) => {
41+ const prevOptions = prev [ column ]
42+ if ( ! prevOptions ) return prev
43+ const newOptions = prevOptions . map ( ( option ) => ( {
44+ ...option ,
45+ isSelected : option . label === label ? ! option . isSelected : option . isSelected ,
46+ } ) )
47+ return { ...prev , [ column ] : newOptions }
48+ } )
4549 }
4650
4751 function apply ( ) {
48- onChange ( internalItems )
52+ onChange ( internalOptions )
4953 setOpenModal ( false )
5054 }
5155
5256 function reset ( ) {
53- const newItems = internalItems . map ( ( item ) => ( { ...item , isSelected : false } ) )
54- setInternalItems ( newItems )
55- onChange ( newItems )
57+ const newOptions = Object . fromEntries (
58+ Object . entries ( internalOptions ) . map ( ( [ column , options ] ) => [
59+ column ,
60+ options . map ( ( option ) => ( { ...option , isSelected : false } ) ) ,
61+ ] )
62+ )
63+ setInternalOptions ( newOptions )
64+ onChange ( newOptions )
5665 setOpenModal ( false )
5766 }
5867
5968 function columnCount ( column : string ) {
60- return internalItems . reduce (
61- ( acc , item ) => acc + ( item . column === column && item . isSelected ? 1 : 0 ) ,
62- 0
63- )
69+ return internalOptions [ column ] ?. length || 0
6470 }
6571
66- const columns = Array . from ( new Set ( internalItems . map ( ( item ) => item . column ) ) )
67- const totalSelected = internalItems . reduce ( ( acc , item ) => acc + ( item . isSelected ? 1 : 0 ) , 0 )
72+ const columns = Object . keys ( internalOptions )
73+ const totalSelected = Object . entries ( options ) . reduce (
74+ ( acc , [ _ , options ] ) => acc + options . filter ( ( option ) => option . isSelected ) . length ,
75+ 0
76+ )
6877
6978 return (
7079 < Popover open = { openModal } onOpenChange = { setOpenModal } >
@@ -90,34 +99,33 @@ export function Filter({ items, onChange }: FilterProps) {
9099 onClick = { ( ) => setSelectedColumn ( column ) }
91100 >
92101 < Typography weight = "normal" type = "body" >
93- Columns { column }
102+ { column }
94103 </ Typography >
95104 < CountBadge count = { columnCount ( column ) } />
96105 </ li >
97106 ) ) }
98107 </ ul >
99108
100109 < ul className = "flex-1 h-full border border-border-primary p-3 rounded-lg overflow-auto" >
101- { internalItems
102- . filter ( ( item ) => item . column === selectedColumn )
103- . map ( ( item ) => (
110+ { selectedColumn &&
111+ internalOptions [ selectedColumn ] ?. map ( ( option ) => (
104112 < li
105- key = { `${ item . column } -${ item . value } ` }
113+ key = { `${ selectedColumn } -${ option . value } ` }
106114 className = "w-full flex gap-4 items-start p-4 cursor-pointer"
107- onClick = { ( ) => toggleItem ( item . column , item . value ) }
115+ onClick = { ( ) => toggleItem ( selectedColumn , option . label ) }
108116 >
109- < Checkbox checked = { item . isSelected } className = "mt-1" />
117+ < Checkbox checked = { option . isSelected } className = "mt-1" />
110118 < div >
111119 < Typography weight = "medium" type = "body" className = "block" >
112- { item . label }
120+ { option . label }
113121 </ Typography >
114- { item . description && (
122+ { option . description && (
115123 < Typography
116124 weight = "normal"
117125 type = "caption"
118126 className = "text-text-secondary block"
119127 >
120- { item . description }
128+ { option . description }
121129 </ Typography >
122130 ) }
123131 </ div >
0 commit comments