1
1
import { h , createContext } from 'preact' ;
2
2
import { useContext } from 'preact/hooks' ;
3
3
import { signal , useSignal , useSignalEffect } from '@preact/signals' ;
4
+ import { useQueryContext } from './QueryProvider.js' ;
5
+ import { eventToIntention } from '../../../../shared/handlers.js' ;
6
+ import { usePlatformName } from '../types.js' ;
7
+ import { useGlobalState } from './GlobalStateProvider.js' ;
4
8
5
- const SelectionContext = createContext ( {
6
- selected : signal ( /** @type {string[] } */ ( [ ] ) ) ,
7
- } ) ;
9
+ /**
10
+ * @typedef SelectionState
11
+ * @property {import("@preact/signals").Signal<number[]> } selected
12
+ */
13
+
14
+ /**
15
+ * @typedef {(s: (d: number[]) => number[]) => void } UpdateSelected
16
+ */
17
+
18
+ const SelectionContext = createContext (
19
+ /** @type {SelectionState } */ ( {
20
+ selected : signal ( /** @type {number[] } */ ( [ ] ) ) ,
21
+ } ) ,
22
+ ) ;
8
23
9
24
/**
10
25
* Provides a context for the selections
@@ -13,24 +28,96 @@ const SelectionContext = createContext({
13
28
* @param {import("preact").ComponentChild } props.children - The child components that will consume the history service context.
14
29
*/
15
30
export function SelectionProvider ( { children } ) {
16
- const selected = useSignal ( /** @type {string[] } */ ( [ ] ) ) ;
31
+ const selected = useSignal ( /** @type {number[] } */ ( [ ] ) ) ;
32
+ /** @type {UpdateSelected } */
33
+ const update = ( fn ) => {
34
+ selected . value = fn ( selected . value ) ;
35
+ console . log ( selected . value ) ;
36
+ } ;
37
+
38
+ useResetOnQueryChange ( update ) ;
39
+ useRowClick ( update , selected ) ;
40
+
41
+ return < SelectionContext . Provider value = { { selected } } > { children } </ SelectionContext . Provider > ;
42
+ }
43
+
44
+ /**
45
+ * @param {UpdateSelected } update
46
+ */
47
+ function useResetOnQueryChange ( update ) {
48
+ const query = useQueryContext ( ) ;
49
+ useSignalEffect ( ( ) => {
50
+ const unsubs = [
51
+ // when anything about the query changes, reset selections
52
+ query . subscribe ( ( ) => {
53
+ update ( ( prev ) => [ ] ) ;
54
+ } ) ,
55
+ ] ;
56
+
57
+ return ( ) => {
58
+ for ( const unsub of unsubs ) {
59
+ unsub ( ) ;
60
+ }
61
+ } ;
62
+ } ) ;
63
+ }
64
+
65
+ /**
66
+ * @param {UpdateSelected } update
67
+ * @param {import("@preact/signals").Signal<number[]> } selected
68
+ */
69
+ function useRowClick ( update , selected ) {
70
+ const platformName = usePlatformName ( ) ;
71
+ const { results } = useGlobalState ( ) ;
72
+ const lastSelected = useSignal ( /** @type {{index: number; id: string}|null } */ ( null ) ) ;
17
73
useSignalEffect ( ( ) => {
18
74
function handler ( /** @type {MouseEvent } */ event ) {
19
75
if ( ! ( event . target instanceof Element ) ) return ;
20
- if ( event . target . matches ( 'a' ) ) return ;
21
76
const itemRow = /** @type {HTMLElement|null } */ ( event . target . closest ( '[data-history-entry][data-index]' ) ) ;
22
77
const selection = toRowSelection ( itemRow ) ;
23
- if ( selection ) {
24
- event . preventDefault ( ) ;
25
- event . stopImmediatePropagation ( ) ;
78
+ if ( ! itemRow || ! selection ) return ;
26
79
27
- // MVP for getting the tests to pass. Next PRs will expand functionality
28
- selected . value = [ selection . id ] ;
80
+ event . preventDefault ( ) ;
81
+ event . stopImmediatePropagation ( ) ;
82
+
83
+ const intention = eventToIntention ( event , platformName ) ;
84
+ const currentSelected = itemRow . getAttribute ( 'aria-selected' ) === 'true' ;
85
+
86
+ switch ( intention ) {
87
+ case 'click' : {
88
+ // MVP for getting the tests to pass. Next PRs will expand functionality
89
+ update ( ( prev ) => [ selection . index ] ) ;
90
+ lastSelected . value = selection ;
91
+ break ;
92
+ }
93
+ case 'ctrl+click' : {
94
+ update ( ( prev ) => {
95
+ const index = prev . indexOf ( selection . index ) ;
96
+ if ( index > - 1 ) {
97
+ const next = prev . slice ( ) ;
98
+ next . splice ( index , 1 ) ;
99
+ return next ;
100
+ }
101
+ return prev . concat ( selection . index ) ;
102
+ } ) ;
103
+ if ( ! currentSelected ) {
104
+ lastSelected . value = selection ;
105
+ } else {
106
+ lastSelected . value = null ;
107
+ }
108
+ break ;
109
+ }
110
+ case 'shift+click' : {
111
+ // todo
112
+ break ;
113
+ }
29
114
}
30
115
}
31
116
document . addEventListener ( 'click' , handler ) ;
117
+ return ( ) => {
118
+ document . removeEventListener ( 'click' , handler ) ;
119
+ } ;
32
120
} ) ;
33
- return < SelectionContext . Provider value = { { selected } } > { children } </ SelectionContext . Provider > ;
34
121
}
35
122
36
123
// Hook for consuming the context
0 commit comments