|
1 |
| -import * as React from "react"; |
2 |
| -import * as actionEmitter from "action-emitter"; |
3 |
| -import * as PropTypes from "prop-types"; |
4 |
| - |
5 | 1 | import {
|
6 |
| - FieldProps, |
7 | 2 | FieldValue,
|
8 |
| - FieldValidationType, |
9 |
| - FieldFormatValueCallback, |
10 |
| - FieldNormalizeValueCallback, |
11 |
| - FieldParseValueCallback, |
12 |
| - FieldStateRecord |
| 3 | + FieldProps |
13 | 4 | } from "../contracts/field";
|
14 |
| -import * as ValueHelpers from "../utils/value-helpers"; |
15 |
| -import { FormContextPropsObject } from "../contracts/form"; |
16 |
| -import { FormStore } from "../stores/form-store"; |
17 |
| -import { FormStoreStateRecord } from "../contracts/form-store"; |
18 |
| -import * as FormStoreActions from "../actions/form-store"; |
19 |
| -// import { FieldsGroupContextProps } from "../contracts/fields-group"; |
20 |
| -import { FSHContainer } from "../stores/form-stores-handler"; |
| 5 | +import { CoreField, CoreFieldState } from "./core-field"; |
21 | 6 |
|
22 |
| -export interface BaseFieldState { |
23 |
| - Field?: FieldStateRecord; |
24 |
| - Form?: FormStoreStateRecord; |
25 |
| - Value?: FieldValue; |
26 |
| -} |
| 7 | +export interface BaseFieldState extends CoreFieldState { |
27 | 8 |
|
28 |
| -export interface ParentContext { |
29 |
| - FormId: string; |
30 |
| - FormProps: FormContextPropsObject; |
31 |
| - FieldsGroupId: string; |
32 |
| - // FieldsGroupProps: FieldsGroupContextProps; |
33 | 9 | }
|
34 | 10 |
|
35 |
| - |
36 |
| -export abstract class BaseField<TProps extends FieldProps, TState extends BaseFieldState> |
37 |
| - extends React.Component<TProps, TState> { |
38 |
| - public context: ParentContext; |
39 |
| - |
40 |
| - static contextTypes: PropTypes.ValidationMap<ParentContext> = { |
41 |
| - FormId: PropTypes.string, |
42 |
| - FormProps: PropTypes.object, |
43 |
| - FieldsGroupId: PropTypes.string, |
44 |
| - // FieldsGroupProps: PropTypes.object |
45 |
| - }; |
46 |
| - |
47 |
| - static defaultProps: FieldProps = { |
48 |
| - // Empty string checked to have value in componentWillMount |
49 |
| - name: "", |
50 |
| - validationType: FieldValidationType.OnFieldRegistered | |
51 |
| - FieldValidationType.OnValueChange | |
52 |
| - FieldValidationType.OnPropsChange, |
53 |
| - // By default, fields data should be retained, even if the field is unmounted |
54 |
| - destroyOnUnmount: false |
55 |
| - }; |
56 |
| - |
57 |
| - protected get FormId(): string { |
58 |
| - return this.context.FormId; |
59 |
| - } |
60 |
| - |
61 |
| - protected get FormStore(): FormStore { |
62 |
| - return FSHContainer.FormStoresHandler.GetStore(this.FormId); |
63 |
| - } |
64 |
| - |
65 |
| - protected get FieldId(): string { |
66 |
| - return this.FormStore.GetFieldId(this.props.name, this.FieldsGroupId); |
67 |
| - } |
68 |
| - |
69 |
| - protected get FieldsGroupId(): string { |
70 |
| - return this.context.FieldsGroupId; |
71 |
| - } |
72 |
| - |
73 |
| - protected StoreEventSubscription: actionEmitter.EventSubscription; |
74 |
| - |
75 |
| - componentWillMount() { |
76 |
| - // props.name MUST have a proper value |
77 |
| - if (this.props.name == null || this.props.name === "") { |
78 |
| - throw new Error("simplr-forms-core: A proper field name must be given (undefined and empty string are not valid)."); |
79 |
| - } |
80 |
| - |
81 |
| - if (this.FormId == null) { |
82 |
| - throw new Error("simplr-forms-core: Field must be used inside a Form component."); |
83 |
| - } |
84 |
| - this.StoreEventSubscription = |
85 |
| - this.FormStore.addListener<FormStoreActions.StateUpdated>( |
86 |
| - FormStoreActions.StateUpdated, |
87 |
| - this.OnStoreUpdated.bind(this)); |
88 |
| - this.registerFieldInFormStore(); |
89 |
| - } |
90 |
| - |
91 |
| - componentWillReceiveProps(nextProps: FieldProps) { |
92 |
| - // Check if field name has not been changed |
93 |
| - if (this.props.name !== nextProps.name) { |
94 |
| - throw new Error(`simplr-forms-core: Field name must be constant`); |
95 |
| - } |
96 |
| - |
97 |
| - this.FormStore.UpdateProps(this.FieldId, nextProps); |
98 |
| - } |
99 |
| - |
100 |
| - componentWillUnmount() { |
101 |
| - if (this.StoreEventSubscription != null) { |
102 |
| - this.StoreEventSubscription.remove(); |
103 |
| - } |
104 |
| - if (this.FormStore != null && this.props.destroyOnUnmount) { |
105 |
| - this.FormStore.UnregisterField(this.FieldId); |
106 |
| - } |
107 |
| - } |
108 |
| - |
109 |
| - /** |
110 |
| - * ======================== |
111 |
| - * Protected methods |
112 |
| - * ======================== |
113 |
| - */ |
114 |
| - |
115 |
| - /** |
116 |
| - * Is field currently controlled. |
117 |
| - * |
118 |
| - * @readonly |
119 |
| - * @protected |
120 |
| - * |
121 |
| - * @memberOf BaseField |
122 |
| - */ |
123 |
| - protected get IsControlled() { |
124 |
| - return false; |
125 |
| - } |
126 |
| - |
127 |
| - /** |
128 |
| - * Current or default field value. |
129 |
| - * |
130 |
| - * @readonly |
131 |
| - * @protected |
132 |
| - * @type {FieldValue} |
133 |
| - * @memberOf BaseField |
134 |
| - */ |
135 |
| - protected get Value(): FieldValue { |
136 |
| - // If field is defined |
137 |
| - if (this.state != null && this.state.Value != null) { |
138 |
| - // Return its value |
139 |
| - return this.state.Value; |
140 |
| - } |
141 |
| - |
142 |
| - // Return default value |
143 |
| - return this.RawDefaultValue; |
144 |
| - } |
145 |
| - |
146 |
| - protected ProcessValueBeforeStore(value: FieldValue) { |
147 |
| - // Parse and normalize value |
148 |
| - return this.NormalizeValue(this.ParseValue(value)); |
149 |
| - } |
150 |
| - |
151 |
| - protected ProcessValueFromStore(value: FieldValue) { |
152 |
| - return this.FormatValue(value); |
153 |
| - } |
154 |
| - |
155 |
| - protected ParseValue(value: FieldValue): FieldValue { |
156 |
| - if (this.props.parseValue != null) { |
157 |
| - const parser = this.props.parseValue as FieldParseValueCallback; |
158 |
| - return parser(value); |
159 |
| - } |
160 |
| - return ValueHelpers.ParseValue( |
161 |
| - React.Children.toArray(this.props.children) as Array<JSX.Element>, |
162 |
| - value |
163 |
| - ); |
164 |
| - } |
165 |
| - |
166 |
| - protected FormatValue(value: FieldValue): FieldValue { |
167 |
| - if (this.props.formatValue != null) { |
168 |
| - const formatter = this.props.formatValue as FieldFormatValueCallback; |
169 |
| - return formatter(value); |
170 |
| - } |
171 |
| - |
172 |
| - return ValueHelpers.FormatValue( |
173 |
| - React.Children.toArray(this.props.children) as Array<JSX.Element>, |
174 |
| - value |
175 |
| - ); |
176 |
| - } |
177 |
| - |
178 |
| - protected NormalizeValue(value: FieldValue): FieldValue { |
179 |
| - if (this.props.normalizeValue != null) { |
180 |
| - const normalizer = this.props.normalizeValue as FieldNormalizeValueCallback; |
181 |
| - return normalizer(value); |
182 |
| - } |
183 |
| - |
184 |
| - return ValueHelpers.NormalizeValue( |
185 |
| - React.Children.toArray(this.props.children) as Array<JSX.Element>, |
186 |
| - value |
187 |
| - ); |
188 |
| - } |
189 |
| - |
190 |
| - protected ChildrenToRender() { |
191 |
| - throw new Error("simplr-forms-core: Not implemented. Needs to filter out Validators, Modifiers and Normalizers."); |
192 |
| - } |
193 |
| - |
194 |
| - protected OnStoreUpdated() { |
195 |
| - const newFormState = this.FormStore.GetState(); |
196 |
| - const newFieldState = this.FormStore.GetField(this.FieldId); |
197 |
| - |
198 |
| - const isStateDifferent = this.state == null || |
199 |
| - this.state.Field !== newFieldState || |
200 |
| - this.state.Form !== newFormState; |
201 |
| - |
202 |
| - if (isStateDifferent && newFieldState != null) { |
203 |
| - this.setState((state: TState) => { |
204 |
| - if (state == null) { |
205 |
| - state = {} as any; |
206 |
| - } |
207 |
| - state.Form = newFormState; |
208 |
| - state.Field = newFieldState; |
209 |
| - state.Value = this.ProcessValueFromStore(newFieldState.Value); |
210 |
| - return state; |
211 |
| - }); |
212 |
| - } |
213 |
| - } |
214 |
| - |
215 |
| - protected OnValueChange(newValue: FieldValue, processValue: boolean = true) { |
216 |
| - // Noop if the component is controlled from outside |
217 |
| - if (this.IsControlled) { |
218 |
| - return; |
219 |
| - } |
220 |
| - |
221 |
| - if (processValue) { |
222 |
| - newValue = this.ProcessValueBeforeStore(newValue); |
223 |
| - } |
224 |
| - |
225 |
| - this.FormStore.ValueChanged(this.FieldId, newValue); |
226 |
| - } |
227 |
| - |
228 |
| - /** |
229 |
| - * ======================== |
230 |
| - * Abstract methods |
231 |
| - * ======================== |
232 |
| - */ |
233 |
| - |
234 |
| - /** |
235 |
| - * React Component's render method |
236 |
| - */ |
237 |
| - abstract render(): JSX.Element | null; |
238 |
| - |
239 |
| - /** |
240 |
| - * Default field value. |
241 |
| - * |
242 |
| - * @readonly |
243 |
| - * @protected |
244 |
| - * |
245 |
| - * @memberOf BaseField |
246 |
| - */ |
| 11 | +export abstract class BaseField<TProps extends FieldProps, TState extends BaseFieldState> extends CoreField<TProps, TState> { |
247 | 12 | protected abstract get RawDefaultValue(): FieldValue;
|
248 | 13 |
|
249 |
| - /** |
250 |
| - * Initial value. |
251 |
| - */ |
252 |
| - protected abstract get RawInitialValue(): FieldValue; |
253 |
| - |
254 |
| - /** |
255 |
| - * Value before render. |
256 |
| - * Most common usage is for getting value from field props. |
257 |
| - * |
258 |
| - * @readonly |
259 |
| - * @protected |
260 |
| - * @type {(FieldContracts.ValueTypes | any)} |
261 |
| - * @memberOf BaseField |
262 |
| - */ |
263 |
| - protected abstract get RawValue(): FieldValue; |
264 |
| - |
265 |
| - /** |
266 |
| - * ======================== |
267 |
| - * Local helper methods |
268 |
| - * ======================== |
269 |
| - */ |
270 |
| - |
271 |
| - /** |
272 |
| - * Registers a field in FormStore or throws if the field was already registered |
273 |
| - * |
274 |
| - * @private |
275 |
| - * |
276 |
| - * @memberOf BaseField |
277 |
| - */ |
278 |
| - private registerFieldInFormStore() { |
279 |
| - if (this.FormStore.HasField(this.FieldId)) { |
280 |
| - throw new Error(`simplr-forms-core: Duplicate field id '${this.FieldId}'`); |
281 |
| - } |
| 14 | + protected get RawInitialValue(): FieldValue { |
| 15 | + return this.props.initialValue; |
| 16 | + } |
282 | 17 |
|
283 |
| - const defaultValue = this.ProcessValueBeforeStore(this.RawDefaultValue); |
284 |
| - const initialValue = this.ProcessValueBeforeStore(this.RawInitialValue); |
285 |
| - const value = this.ProcessValueBeforeStore(this.RawValue); |
286 |
| - this.FormStore.RegisterField( |
287 |
| - this.FieldId, |
288 |
| - defaultValue, |
289 |
| - initialValue, |
290 |
| - value, |
291 |
| - this.props, |
292 |
| - this.FieldsGroupId |
293 |
| - ); |
| 18 | + protected get RawValue(): FieldValue { |
| 19 | + return this.props.value; |
294 | 20 | }
|
295 | 21 | }
|
0 commit comments