@@ -23,7 +23,7 @@ const DEMO_NOTES = [
2323 title : "Welcome 👋" ,
2424 text : "This is the *web demo* of Keep Sticky Board.\n\nDrag me by my title bar." ,
2525 color : "yellow" ,
26- labels : [ "demo" , "welcome" ]
26+ labels : [ "demo" , "welcome" , "ss" , "s" , "e" , "t" ]
2727 } ,
2828 {
2929 id : "demo-2" ,
@@ -258,9 +258,11 @@ export default function App() {
258258 const [ notes , setNotes ] = useState ( [ ] ) ;
259259 const [ positions , setPositions ] = useState ( { } ) ;
260260 const [ query , setQuery ] = useState ( "" ) ;
261- const [ activeLabel , setActiveLabel ] = useState ( "ALL" ) ;
262261 const saveTimer = useRef ( null ) ;
263262 const [ zoom , setZoom ] = useState ( 1 ) ;
263+ const [ openFilterBox , setOpenFilterBox ] = useState ( false ) ;
264+ const [ selectedLabels , setSelectedLabels ] = useState ( [ ] ) ;
265+ const [ filterLogic , setFilterLogic ] = useState ( "OR" ) ;
264266
265267 // Initial load:
266268 // - Electron: load from keepAPI
@@ -367,26 +369,42 @@ export default function App() {
367369 setActiveLabel ( "ALL" ) ;
368370 }
369371
372+ const toggleLabel = ( label ) => {
373+ if ( label === "ALL" ) {
374+ setSelectedLabels ( [ ] ) ;
375+ return ;
376+ }
377+ setSelectedLabels ( prev =>
378+ prev . includes ( label )
379+ ? prev . filter ( l => l !== label )
380+ : [ ...prev , label ]
381+ ) ;
382+ } ;
383+
370384 const allLabels = useMemo ( ( ) => {
371385 const s = new Set ( ) ;
372386 for ( const n of notes ) for ( const l of n . labels || [ ] ) s . add ( l ) ;
373387 return [ "ALL" , ...Array . from ( s ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ] ;
374388 } , [ notes ] ) ;
375389
376390 const filtered = useMemo ( ( ) => {
377- const q = query . trim ( ) . toLowerCase ( ) ;
378- return notes . filter ( ( n ) => {
379- if ( activeLabel !== "ALL" && ! ( n . labels || [ ] ) . includes ( activeLabel ) ) return false ;
380- if ( ! q ) return true ;
381-
382- const imageTerms = getNoteImages ( n )
383- . map ( ( img ) => `${ img . alt || "" } ${ img . src || "" } ` )
384- . join ( "\n" ) ;
391+ const q = query . trim ( ) . toLowerCase ( ) ;
392+ return notes . filter ( ( n ) => {
393+ // Label Filter Logic
394+ if ( selectedLabels . length > 0 ) {
395+ const noteLabels = n . labels || [ ] ;
396+ if ( filterLogic === "OR" ) {
397+ if ( ! selectedLabels . some ( l => noteLabels . includes ( l ) ) ) return false ;
398+ } else {
399+ if ( ! selectedLabels . every ( l => noteLabels . includes ( l ) ) ) return false ;
400+ }
401+ }
385402
386- const hay = `${ n . title || "" } \n${ n . text || "" } \n${ imageTerms } ` . toLowerCase ( ) ;
387- return hay . includes ( q ) ;
388- } ) ;
389- } , [ notes , query , activeLabel ] ) ;
403+ if ( ! q ) return true ;
404+ const hay = `${ n . title || "" } \n${ n . text || "" } ` . toLowerCase ( ) ;
405+ return hay . includes ( q ) ;
406+ } ) ;
407+ } , [ notes , query , selectedLabels , filterLogic ] ) ;
390408
391409 function noteColor ( n ) {
392410 const c = String ( n . color || "" ) . toLowerCase ( ) ;
@@ -436,11 +454,33 @@ export default function App() {
436454 value = { query }
437455 onChange = { ( e ) => setQuery ( e . target . value ) }
438456 />
439- < select className = "select" value = { activeLabel } onChange = { ( e ) => setActiveLabel ( e . target . value ) } >
440- { allLabels . map ( ( l ) => (
441- < option key = { l } value = { l } > { l } </ option >
442- ) ) }
443- </ select >
457+ < button className = { `filter btn ${ openFilterBox ? "active" : "" } ` } onClick = { ( ) => setOpenFilterBox ( prev => ! prev ) } > Filter Notes</ button >
458+ < div className = { `filter-section ${ openFilterBox ? "active" : "" } ` } >
459+ { /* Logic Toggle: AND vs OR */ }
460+ < div className = "logic-toggle" >
461+ < button
462+ className = { `btn-toggle ${ filterLogic === 'OR' ? 'active' : '' } ` }
463+ onClick = { ( ) => setFilterLogic ( 'OR' ) }
464+ > OR </ button >
465+ < button
466+ className = { `btn-toggle ${ filterLogic === 'AND' ? 'active' : '' } ` }
467+ onClick = { ( ) => setFilterLogic ( 'AND' ) }
468+ > AND </ button >
469+ </ div >
470+
471+ { /* Multi-select Chips */ }
472+ < div className = "chips-container" >
473+ { allLabels . map ( ( l ) => (
474+ < button
475+ key = { l }
476+ className = { `chip-filter ${ selectedLabels . includes ( l ) || ( l === "ALL" && selectedLabels . length === 0 ) ? "selected" : "" } ` }
477+ onClick = { ( ) => toggleLabel ( l ) }
478+ >
479+ { l }
480+ </ button >
481+ ) ) }
482+ </ div >
483+ </ div >
444484 </ div >
445485 </ header >
446486 < div className = "board" style = { {
0 commit comments