@@ -7,6 +7,33 @@ type AvailableStatus =
7
7
| 'default'
8
8
| ( string & { } ) ; // Allows extra status
9
9
10
+ type UiState <
11
+ Status extends AvailableStatus ,
12
+ Data extends Record < string , unknown > ,
13
+ > = {
14
+ is : < S extends Status > ( status : S ) => boolean ;
15
+ state : {
16
+ __status : Status ;
17
+ } & Data ;
18
+ match : < S extends Status > (
19
+ status : S | Array < S > ,
20
+ handler : (
21
+ data : Omit <
22
+ Extract < UiState < Status , Data > [ 'state' ] , { __status : S } > ,
23
+ '__status'
24
+ >
25
+ ) => React . ReactNode ,
26
+ __matched ?: boolean ,
27
+ run ?: ( ) => React . ReactNode
28
+ ) => {
29
+ nonExhaustive : ( ) => React . ReactNode ;
30
+ } & ( Exclude < Status , S > extends never
31
+ ? {
32
+ exhaustive : ( ) => React . ReactNode ;
33
+ }
34
+ : Pick < UiState < Exclude < Status , S > , Data > , 'match' > ) ;
35
+ } ;
36
+
10
37
export const getUiState = <
11
38
Status extends AvailableStatus ,
12
39
Data extends Record < string , unknown > ,
@@ -17,30 +44,54 @@ export const getUiState = <
17
44
data ?: D
18
45
) => { __status : S } & D
19
46
) => { __status : Status } & Data
20
- ) : {
21
- with : < S extends Status > (
22
- status : S
23
- ) => {
24
- __status : S ;
25
- } ;
26
- is : < S extends Status > ( status : S ) => boolean ;
27
- state : {
28
- __status : Status ;
29
- } & Data ;
30
- } => {
47
+ ) : UiState < Status , Data > => {
31
48
const state = getState ( ( status , data = { } as ExplicitAny ) => {
32
49
return {
33
50
__status : status ,
34
51
...data ,
35
52
} ;
36
53
} ) ;
37
- return {
38
- with : ( status ) => ( {
39
- __status : status ,
40
- } ) ,
54
+
55
+ const isMatching = < S extends Status > ( status : Status ) : status is S =>
56
+ status === state . __status ;
57
+
58
+ const isMatchingArray = < S extends Status > (
59
+ status : Array < Status >
60
+ ) : status is Array < S > => status . includes ( state . __status ) ;
61
+
62
+ const uiState : UiState < Status , Data > = {
63
+ state,
41
64
is : ( status ) => {
42
65
return state . __status === status ;
43
66
} ,
44
- state,
67
+ match : ( status , handler , __matched = false , render = ( ) => null ) => {
68
+ if (
69
+ ! __matched && typeof status === 'string'
70
+ ? isMatching ( status )
71
+ : isMatchingArray ( status as Array < Status > )
72
+ ) {
73
+ return {
74
+ ...( uiState as ExplicitAny ) ,
75
+ __matched : true ,
76
+ exhaustive : ( ) => handler ( state as ExplicitAny ) ,
77
+ nonExhaustive : ( ) => handler ( state as ExplicitAny ) ,
78
+ match : ( status , _handler ) =>
79
+ uiState . match ( status , _handler , true , ( ) =>
80
+ handler ( uiState . state as ExplicitAny )
81
+ ) ,
82
+ } ;
83
+ }
84
+
85
+ return {
86
+ ...uiState ,
87
+ __matched,
88
+ exhaustive : render ,
89
+ nonExhaustive : render ,
90
+ match : ( status , handler ) =>
91
+ uiState . match ( status , handler , __matched , render ) ,
92
+ } ;
93
+ } ,
45
94
} ;
95
+
96
+ return uiState ;
46
97
} ;
0 commit comments