| 1 | import { state, useSnapState } from './state' |
| 2 | import { createElement as h, useEffect, useState } from 'react' |
| 3 | import { useDebounce } from 'usehooks-ts' |
| 4 | import { Checkbox, CustomCode } from './components' |
| 5 | import { usePath } from './useFetchList' |
| 6 | import { onHfsEvent, with_ } from './misc' |
| 7 | import i18n from './i18n' |
| 8 | const { useI18N } = i18n |
| 9 | |
| 10 | export function FilterBar() { |
| 11 | const { list, filteredList, selected, patternFilter, showFilter } = useSnapState() |
| 12 | const [all, setAll] = useState(false) |
| 13 | const [filter, setFilter] = useState(patternFilter) |
| 14 | useEffect(() => setAll(false), [patternFilter, usePath()]) // reset on change |
| 15 | const {t} = useI18N() |
| 16 | |
| 17 | state.patternFilter = useDebounce(showFilter ? filter : '', 300) |
| 18 | useEffect(() => onHfsEvent('', 'entryToggleSelection', () => setAll(false)), []) |
| 19 | |
| 20 | const tabIndex = showFilter ? undefined : -1 |
| 21 | return h('div', { id: 'filter-bar', style: { display: showFilter ? undefined : 'none' } }, |
| 22 | h(Checkbox, { |
| 23 | value: all, |
| 24 | tabIndex, |
| 25 | 'aria-hidden': !showFilter, |
| 26 | 'aria-label': t`Select all`, |
| 27 | onContextMenu(ev) { |
| 28 | ev.preventDefault() |
| 29 | select(undefined) |
| 30 | }, |
| 31 | onChange(v, ev){ |
| 32 | const toggle = with_(ev.nativeEvent as any, v => v.ctrlKey || v.metaKey) |
| 33 | select(toggle ? undefined : v) |
| 34 | }, |
| 35 | }), |
| 36 | h('input', { |
| 37 | id: 'filter', |
| 38 | placeholder: t('filter_placeholder', "Type here to filter the list below"), |
| 39 | autoComplete: 'off', |
| 40 | value: filter, |
| 41 | tabIndex, |
| 42 | autoFocus: true, |
| 43 | onChange(ev) { |
| 44 | setFilter(ev.target.value) |
| 45 | } |
| 46 | }), |
| 47 | h(CustomCode, { name: 'afterFilter' }), |
| 48 | h('span', {}, [ |
| 49 | with_(Object.keys(selected).length, n => n && t('select_count', { n }, "{n} selected")), |
| 50 | with_(filteredList?.length, n => n !== undefined && n < list.length && t('filter_count', {n}, "{n} filtered")), |
| 51 | ].filter(Boolean).join(', ') ), |
| 52 | ) |
| 53 | |
| 54 | function select(will: boolean | undefined) { // undefined will cause toggle of each element |
| 55 | const sel = state.selected |
| 56 | for (const e of state.filteredList || state.list) { |
| 57 | const { uri } = e |
| 58 | const was = sel[uri] || false |
| 59 | if (was === will) continue |
| 60 | if (was) |
| 61 | delete sel[uri] |
| 62 | else if (e.canSelect()) |
| 63 | sel[uri] = true |
| 64 | } |
| 65 | if (will !== undefined) |
| 66 | setAll(will) |
| 67 | } |
| 68 | } |