@@ -3,31 +3,71 @@ import { produce } from "immer";
33
44export const SEPARATOR = "." ;
55
6+ interface Status {
7+ isFocused : boolean ;
8+ isTouched : boolean ;
9+ }
10+
611interface Field {
712 id : string ;
813 name : string ;
914
10- defaultValue ? : string ;
11- initialValue ? : string ;
15+ defaultValue : string ;
16+ initialValue : string ;
1217 currentValue : string ;
1318
14- isFocused : boolean ;
15- isTouched : boolean ;
19+ status : Status ;
1620}
1721
1822type Fields = {
1923 [ key : string ] : Field | undefined ;
2024} ;
2125
22- export class GroupStoreMutable extends TinyEmitter < Callback > {
23- protected fields : Fields = { } ;
26+ interface GroupState {
27+ fields : Fields ;
28+ status : Status ;
29+ }
30+
31+ const defaultStatus = ( ) : Status => ( { isFocused : false , isTouched : false } ) ;
32+
33+ const calculateGroupStatus = ( state : GroupState ) : Status => {
34+ let isFocused = false ;
35+ let isTouched = false ;
2436
25- constructor ( ) {
26- super ( ) ;
37+ for ( const key of Object . keys ( state . fields ) ) {
38+ const field = state . fields [ key ] ;
39+ if ( field == null ) {
40+ continue ;
41+ }
42+
43+ if ( field . status . isFocused ) {
44+ isFocused = true ;
45+ }
46+ if ( field . status . isTouched ) {
47+ isTouched = true ;
48+ }
49+
50+ // If group is both focused and touched already, nothing is going to change anymore
51+ if ( isFocused && isTouched ) {
52+ // Thus, break.
53+ break ;
54+ }
2755 }
2856
57+ return {
58+ isFocused : isFocused ,
59+ isTouched : isTouched
60+ } ;
61+ } ;
62+
63+ export class GroupStoreMutable extends TinyEmitter < Callback > {
64+ protected state : GroupState = {
65+ fields : { } ,
66+ status : defaultStatus ( )
67+ } ;
68+
2969 public getField ( fieldId : string ) : Field | undefined {
30- return this . fields [ fieldId ] ;
70+ return this . state . fields [ fieldId ] ;
3171 }
3272
3373 public generateFieldId ( name : string , groupId : string ) : string {
@@ -38,56 +78,63 @@ export class GroupStoreMutable extends TinyEmitter<Callback> {
3878 name : string ,
3979 groupId : string ,
4080 defaultValue : string ,
41- initialValue : string
81+ initialValue ? : string
4282 ) : void {
4383 const id = this . generateFieldId ( name , groupId ) ;
4484
45- if ( this . fields [ id ] != null ) {
85+ if ( this . state . fields [ id ] != null ) {
4686 throw new Error ( `Field with an id '${ id } ' is already registered.` ) ;
4787 }
4888
89+ if ( initialValue == null ) {
90+ initialValue = defaultValue ;
91+ }
92+
4993 const newField : Field = {
5094 id : id ,
5195 name : name ,
5296 defaultValue : defaultValue ,
5397 initialValue : initialValue ,
54- currentValue : initialValue || defaultValue ,
55- isFocused : false ,
56- isTouched : false
98+ currentValue : initialValue ,
99+ status : {
100+ isFocused : false ,
101+ isTouched : false
102+ }
57103 } ;
58104
59- this . fields = produce ( this . fields , fields => {
60- fields [ id ] = newField ;
105+ this . state = produce ( this . state , state => {
106+ state . fields [ id ] = newField ;
61107 } ) ;
62108 this . emit ( ) ;
63109 }
64110
65111 public unregisterField ( id : string ) : void {
66- if ( this . fields [ id ] == null ) {
112+ if ( this . state . fields [ id ] == null ) {
67113 return ;
68114 }
69115
70- this . fields = produce ( this . fields , fields => {
71- fields [ id ] = undefined ;
116+ this . state = produce ( this . state , state => {
117+ state . fields [ id ] = undefined ;
72118 } ) ;
73119 this . emit ( ) ;
74120 }
75121
76122 public updateValue ( fieldId : string , value : string ) : void {
77- if ( this . fields [ fieldId ] == null ) {
123+ console . log ( `Value updated ${ fieldId } ` ) ;
124+ if ( this . state . fields [ fieldId ] == null ) {
78125 throw new Error (
79126 `Cannot update non-existent field value. (field id '${ fieldId } ').`
80127 ) ;
81128 }
82- this . fields = produce ( this . fields , fields => {
83- const field = fields [ fieldId ] ;
129+ this . state = produce ( this . state , state => {
130+ const field = state . fields [ fieldId ] ;
84131 if ( field == null ) {
85132 return ;
86133 }
87134
88135 // Value equality check should be abstracted for particular component and value type
89136 if ( field . currentValue !== value ) {
90- field . isTouched = true ;
137+ field . status . isTouched = true ;
91138 }
92139 field . currentValue = value ;
93140 } ) ;
@@ -96,41 +143,69 @@ export class GroupStoreMutable extends TinyEmitter<Callback> {
96143 }
97144
98145 public focus ( fieldId : string ) : void {
146+ console . log ( `Focus ${ fieldId } ` ) ;
99147 this . setFocused ( fieldId , true ) ;
100148 this . emit ( ) ;
101149 }
102150
103151 public blur ( fieldId : string ) : void {
152+ console . log ( `Blur ${ fieldId } ` ) ;
104153 this . setFocused ( fieldId , false ) ;
105154 this . emit ( ) ;
106155 }
107156
108157 private setFocused ( fieldId : string , isFocused : boolean ) : void {
109- console . log ( `Setting focus for field '${ fieldId } ' to ${ isFocused } ` ) ;
110- if ( this . fields [ fieldId ] == null ) {
158+ if ( this . state . fields [ fieldId ] == null ) {
111159 throw new Error (
112160 `Cannot update non-existent field value. (field id '${ fieldId } ').`
113161 ) ;
114162 }
115- this . fields = produce ( this . fields , fields => {
116- const field = fields [ fieldId ] ;
117- if ( field == null ) {
163+
164+ this . state = produce ( this . state , state => {
165+ const field = state . fields [ fieldId ] ;
166+ if ( field == null || field . status . isFocused === isFocused ) {
118167 return ;
119168 }
120- field . isFocused = isFocused ;
169+
170+ field . status . isFocused = isFocused ;
121171
122172 // If the field is not touched yet and got focused, make it touched.
123- if ( ! field . isTouched && isFocused ) {
124- field . isTouched = true ;
173+ if ( ! field . status . isTouched && isFocused ) {
174+ field . status . isTouched = true ;
175+ }
176+
177+ // Recalculate group status, because field status has changed
178+ state . status = calculateGroupStatus ( state ) ;
179+ } ) ;
180+ }
181+
182+ public reset ( ) : void {
183+ console . log ( `Reset state` ) ;
184+ this . state = produce ( this . state , state => {
185+ // Reset fields
186+ const fields = state . fields ;
187+ for ( const key of Object . keys ( fields ) ) {
188+ const field = fields [ key ] ;
189+ if ( field == null ) {
190+ continue ;
191+ }
192+
193+ field . currentValue = field . initialValue ;
194+ field . status = defaultStatus ( ) ;
125195 }
196+
197+ // Reset group status
198+ state . status = defaultStatus ( ) ;
126199 } ) ;
200+ this . emit ( ) ;
127201 }
128202
129203 public toObject ( ) : unknown {
130204 const result : { [ key : string ] : unknown } = { } ;
131- for ( const key in this . fields ) {
132- if ( this . fields . hasOwnProperty ( key ) ) {
133- const field = this . fields [ key ] ;
205+ const fields = this . state . fields ;
206+ for ( const key in fields ) {
207+ if ( fields . hasOwnProperty ( key ) ) {
208+ const field = fields [ key ] ;
134209 if ( field == null ) {
135210 continue ;
136211 }
0 commit comments