ctrl/command+click on toggle-all will toggle each item
Massimo Melina committed
Apr 20, 2023 at 23:07 UTC
70946b98182e7c0fd257f4c75d19f93d66b4537b
3 files changed
+41
-18
README.md
+1
@@ -207,6 +207,7 @@ This is enough, but you may want to configure generated links accordingly:
207
- Appending `#LOGIN` to address will bring up the login dialog
208
- Appending ?lang=CODE to address will force a specific language
209
- env `SESSION_DURATION` can be set to any number of seconds. Default is 1 day.
210
+- right/ctrl/command click on toggle-all checkbox will invert each checkbox state
211
212
## Contribute
213
frontend/src/FilterBar.ts
+22
-13
@@ -4,6 +4,7 @@ import { useDebounce } from 'usehooks-ts'
4
import { Checkbox } from './components'
5
import { useI18N } from './i18n'
6
import { usePath } from './useFetchList'
7
+import { with_ } from './misc'
8
9
export function FilterBar() {
10
const { list, filteredList, selected, patternFilter, showFilter } = useSnapState()
@@ -19,19 +20,13 @@ export function FilterBar() {
20
return h('div', { id: 'filter-bar', className: showFilter ? 'show-sliding' : 'before-sliding' },
21
h(Checkbox, {
22
value: all,
22
- onChange(){
23
- const will = !all
24
- const sel = state.selected
25
- for (const { uri } of state.filteredList || state.list) {
26
- const was = sel[uri]
27
- if (was === will) continue
28
- if (was)
29
- delete sel[uri]
30
- else
31
- sel[uri] = true
32
- }
33
-
34
- setAll(will)
23
+ onContextMenu(ev) {
24
+ ev.preventDefault()
25
+ select(undefined)
26
+ },
27
+ onChange(v, ev){
28
+ const toggle = with_(ev.nativeEvent as any, v => v.ctrlKey || v.metaKey)
29
+ select(toggle ? undefined : v)
30
},
31
}),
32
h('input', {
@@ -49,4 +44,18 @@ export function FilterBar() {
44
fil !== undefined && fil < list.length && t('filter_count', {n:fil}, "{n} filtered"),
45
].filter(Boolean).join(', ') ),
46
)
47
+
48
+ function select(will: boolean | undefined) {
49
+ const sel = state.selected
50
+ for (const { uri } of state.filteredList || state.list) {
51
+ const was = sel[uri] || false
52
+ if (was === will) continue
53
+ if (was)
54
+ delete sel[uri]
55
+ else
56
+ sel[uri] = true
57
+ }
58
+ if (will !== undefined)
59
+ setAll(will)
60
+ }
61
}
\ No newline at end of file
frontend/src/components.ts
+18
-5
@@ -1,7 +1,16 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import { getHFS, hfsEvent, hIcon } from './misc'
4
-import { createElement as h, Fragment, HTMLAttributes, isValidElement, ReactNode, useMemo } from 'react'
4
+import {
5
+ ChangeEvent,
6
+ createElement as h,
7
+ Fragment,
8
+ HTMLAttributes,
9
+ InputHTMLAttributes,
10
+ isValidElement,
11
+ ReactNode,
12
+ useMemo
13
+} from 'react'
14
15
export function Spinner(props: any) {
16
return hIcon('spinner', { className:'spinner', ...props })
@@ -23,11 +32,15 @@ export function FlexV(props:any) {
32
return h(Flex, { vert:true, ...props })
33
}
34
26
-interface CheckboxProps { children?:ReactNode, value:any, onChange?:(v:boolean)=>void }
27
-export function Checkbox({ onChange, value, children, ...props }:CheckboxProps) {
28
- const ret = h('input',{
35
+interface CheckboxProps extends Omit<Partial<InputHTMLAttributes<any>>, 'onChange'> {
36
+ children?: ReactNode,
37
+ value: any,
38
+ onChange?: (v: boolean, ev: ChangeEvent) => void
39
+}
40
+export function Checkbox({ onChange, value, children, ...props }: CheckboxProps) {
41
+ const ret = h('input', {
42
type: 'checkbox',
30
- onChange: ev => onChange?.(Boolean(ev.target.checked)),
43
+ onChange: ev => onChange?.(Boolean(ev.target.checked), ev),
44
checked: Boolean(value),
45
value: 1,
46
...props