@viveksinghind/narrative-form-native 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +50 -50
- package/LICENSE +0 -21
- package/dist/index.mjs +0 -621
- package/dist/index.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
"name": "@viveksinghind/narrative-form-native",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "React Native components for narrative-form — typewriter-style sign-up flow",
|
|
5
|
+
"author": "Vivek Singh <vivekSinghInd>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"dev": "tsc --watch",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"clean": "rimraf dist"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@viveksinghind/narrative-form-core": "workspace:*"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=17.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "^18.3.1",
|
|
34
|
+
"@types/react-native": "0.70.0",
|
|
35
|
+
"rimraf": "^5.0.5",
|
|
36
|
+
"tsup": "^8.0.2",
|
|
37
|
+
"typescript": "^5.4.5"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"react-native",
|
|
41
|
+
"narrative-form",
|
|
42
|
+
"typewriter",
|
|
43
|
+
"form",
|
|
44
|
+
"onboarding",
|
|
45
|
+
"signup",
|
|
46
|
+
"typescript"
|
|
47
|
+
],
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://github.com/vivekSinghInd/narrative-form.git",
|
|
51
|
+
"directory": "packages/native"
|
|
18
52
|
}
|
|
19
|
-
},
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"@viveksinghind/narrative-form-core": "1.0.2"
|
|
22
|
-
},
|
|
23
|
-
"peerDependencies": {
|
|
24
|
-
"react": ">=17.0.0"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"@types/react": "^18.3.1",
|
|
28
|
-
"@types/react-native": "0.70.0",
|
|
29
|
-
"rimraf": "^5.0.5",
|
|
30
|
-
"tsup": "^8.0.2",
|
|
31
|
-
"typescript": "^5.4.5"
|
|
32
|
-
},
|
|
33
|
-
"keywords": [
|
|
34
|
-
"react-native",
|
|
35
|
-
"narrative-form",
|
|
36
|
-
"typewriter",
|
|
37
|
-
"form",
|
|
38
|
-
"onboarding",
|
|
39
|
-
"signup",
|
|
40
|
-
"typescript"
|
|
41
|
-
],
|
|
42
|
-
"repository": {
|
|
43
|
-
"type": "git",
|
|
44
|
-
"url": "https://github.com/vivekSinghInd/narrative-form.git",
|
|
45
|
-
"directory": "packages/native"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"build": "tsc",
|
|
49
|
-
"dev": "tsc --watch",
|
|
50
|
-
"typecheck": "tsc --noEmit",
|
|
51
|
-
"clean": "rimraf dist"
|
|
52
|
-
}
|
|
53
53
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Vivek Singh
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/index.mjs
DELETED
|
@@ -1,621 +0,0 @@
|
|
|
1
|
-
import { createContext, useState, useRef, useCallback, useMemo, useEffect, useContext } from 'react';
|
|
2
|
-
import { StyleSheet, Animated, Text, Keyboard, View, TouchableOpacity, TextInput, ScrollView } from 'react-native';
|
|
3
|
-
import { FormStateEngine, fetchFormConfig, validateField, hasAsyncValidation, validateFieldAsync, mergeStrings } from '@viveksinghind/narrative-form-core';
|
|
4
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
-
|
|
6
|
-
// src/components/NarrativeForm.tsx
|
|
7
|
-
function useFormState(fields) {
|
|
8
|
-
const [, setTick] = useState(0);
|
|
9
|
-
const engineRef = useRef(null);
|
|
10
|
-
if (engineRef.current === null) {
|
|
11
|
-
engineRef.current = new FormStateEngine(fields, () => {
|
|
12
|
-
setTick((t) => t + 1);
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
const engine = engineRef.current;
|
|
16
|
-
const startTyping = useCallback((key) => engine.startTyping(key), [engine]);
|
|
17
|
-
const activateField = useCallback((key) => engine.activateField(key), [engine]);
|
|
18
|
-
const confirmField = useCallback(
|
|
19
|
-
(key, value) => engine.confirmField(key, value),
|
|
20
|
-
[engine]
|
|
21
|
-
);
|
|
22
|
-
const editField = useCallback((key) => engine.editField(key), [engine]);
|
|
23
|
-
const reconfirmField = useCallback(
|
|
24
|
-
(key, value) => engine.reconfirmField(key, value),
|
|
25
|
-
[engine]
|
|
26
|
-
);
|
|
27
|
-
const next = useCallback(() => engine.next(), [engine]);
|
|
28
|
-
const focusField = useCallback((key) => engine.focusField(key), [engine]);
|
|
29
|
-
const reset = useCallback(() => engine.reset(), [engine]);
|
|
30
|
-
const getValues = useCallback(() => engine.getValues(), [engine]);
|
|
31
|
-
const getMeta = useCallback(
|
|
32
|
-
(formId, formVersion) => engine.getMeta(formId, formVersion),
|
|
33
|
-
[engine]
|
|
34
|
-
);
|
|
35
|
-
const snapshot = engine.getSnapshot();
|
|
36
|
-
return useMemo(
|
|
37
|
-
() => ({
|
|
38
|
-
snapshot,
|
|
39
|
-
startTyping,
|
|
40
|
-
activateField,
|
|
41
|
-
confirmField,
|
|
42
|
-
editField,
|
|
43
|
-
reconfirmField,
|
|
44
|
-
next,
|
|
45
|
-
focusField,
|
|
46
|
-
reset,
|
|
47
|
-
getValues,
|
|
48
|
-
getMeta
|
|
49
|
-
}),
|
|
50
|
-
// snapshot changes every tick, which is what we want
|
|
51
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
-
[snapshot]
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
function useDynamicForm({
|
|
56
|
-
fieldsUrl,
|
|
57
|
-
fieldsUrlHeaders,
|
|
58
|
-
formConfig,
|
|
59
|
-
onFetchError
|
|
60
|
-
}) {
|
|
61
|
-
const [config, setConfig] = useState(formConfig != null ? formConfig : null);
|
|
62
|
-
const [loading, setLoading] = useState(!!fieldsUrl && !formConfig);
|
|
63
|
-
const [error, setError] = useState(null);
|
|
64
|
-
const [retryCount, setRetryCount] = useState(0);
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
if (formConfig) {
|
|
67
|
-
setConfig(formConfig);
|
|
68
|
-
setLoading(false);
|
|
69
|
-
setError(null);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (!fieldsUrl) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
let isMounted = true;
|
|
76
|
-
setLoading(true);
|
|
77
|
-
setError(null);
|
|
78
|
-
fetchFormConfig(fieldsUrl, { headers: fieldsUrlHeaders }).then((data) => {
|
|
79
|
-
if (isMounted) {
|
|
80
|
-
setConfig(data);
|
|
81
|
-
setLoading(false);
|
|
82
|
-
}
|
|
83
|
-
}).catch((err) => {
|
|
84
|
-
if (isMounted) {
|
|
85
|
-
setError(err);
|
|
86
|
-
setLoading(false);
|
|
87
|
-
onFetchError == null ? void 0 : onFetchError(err);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
return () => {
|
|
91
|
-
isMounted = false;
|
|
92
|
-
};
|
|
93
|
-
}, [fieldsUrl, fieldsUrlHeaders, formConfig, retryCount, onFetchError]);
|
|
94
|
-
const retry = useCallback(() => {
|
|
95
|
-
if (fieldsUrl && !formConfig) {
|
|
96
|
-
setRetryCount((c) => c + 1);
|
|
97
|
-
}
|
|
98
|
-
}, [fieldsUrl, formConfig]);
|
|
99
|
-
return { config, loading, error, retry };
|
|
100
|
-
}
|
|
101
|
-
var ThemeContext = createContext({
|
|
102
|
-
theme: void 0,
|
|
103
|
-
isDark: false
|
|
104
|
-
});
|
|
105
|
-
var useTheme = () => useContext(ThemeContext);
|
|
106
|
-
var ThemeProvider = ({ theme, children }) => {
|
|
107
|
-
const value = useMemo(() => {
|
|
108
|
-
return {
|
|
109
|
-
theme,
|
|
110
|
-
isDark: !!(theme == null ? void 0 : theme.mode) && theme.mode === "dark"
|
|
111
|
-
};
|
|
112
|
-
}, [theme]);
|
|
113
|
-
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
|
|
114
|
-
};
|
|
115
|
-
var ErrorMessage = ({ message, config }) => {
|
|
116
|
-
var _a;
|
|
117
|
-
const { theme } = useTheme();
|
|
118
|
-
const opacity = useRef(new Animated.Value(0)).current;
|
|
119
|
-
const translateY = useRef(new Animated.Value(-10)).current;
|
|
120
|
-
useEffect(() => {
|
|
121
|
-
if (message) {
|
|
122
|
-
Animated.parallel([
|
|
123
|
-
Animated.timing(opacity, {
|
|
124
|
-
toValue: 1,
|
|
125
|
-
duration: 200,
|
|
126
|
-
useNativeDriver: true
|
|
127
|
-
}),
|
|
128
|
-
Animated.timing(translateY, {
|
|
129
|
-
toValue: 0,
|
|
130
|
-
duration: 200,
|
|
131
|
-
useNativeDriver: true
|
|
132
|
-
})
|
|
133
|
-
]).start();
|
|
134
|
-
} else {
|
|
135
|
-
opacity.setValue(0);
|
|
136
|
-
translateY.setValue(-10);
|
|
137
|
-
}
|
|
138
|
-
}, [message, opacity, translateY]);
|
|
139
|
-
if (!message) return null;
|
|
140
|
-
return /* @__PURE__ */ jsx(Animated.View, { style: { opacity, transform: [{ translateY }] }, children: /* @__PURE__ */ jsx(Text, { style: [styles.errorText, { color: ((_a = theme == null ? void 0 : theme.colors) == null ? void 0 : _a.error) || "#d32f2f" }], children: message }) });
|
|
141
|
-
};
|
|
142
|
-
var styles = StyleSheet.create({
|
|
143
|
-
errorText: {
|
|
144
|
-
fontSize: 14,
|
|
145
|
-
marginTop: 4,
|
|
146
|
-
marginBottom: 8
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
var Line = ({
|
|
150
|
-
field,
|
|
151
|
-
status,
|
|
152
|
-
value,
|
|
153
|
-
allValues,
|
|
154
|
-
typewriter,
|
|
155
|
-
editable,
|
|
156
|
-
locked,
|
|
157
|
-
editLabel,
|
|
158
|
-
onTypingComplete,
|
|
159
|
-
onConfirm,
|
|
160
|
-
onEdit,
|
|
161
|
-
onError,
|
|
162
|
-
onChange,
|
|
163
|
-
onFocus,
|
|
164
|
-
onBlur
|
|
165
|
-
}) => {
|
|
166
|
-
var _a, _b;
|
|
167
|
-
const { theme, isDark } = useTheme();
|
|
168
|
-
const [typedPrompt, setTypedPrompt] = useState("");
|
|
169
|
-
const [localValue, setLocalValue] = useState(value ? String(value) : "");
|
|
170
|
-
const [errorMsg, setErrorMsg] = useState(null);
|
|
171
|
-
const [isValidating, setIsValidating] = useState(false);
|
|
172
|
-
const inputRef = useRef(null);
|
|
173
|
-
const opacity = useRef(new Animated.Value(0)).current;
|
|
174
|
-
useEffect(() => {
|
|
175
|
-
Animated.timing(opacity, {
|
|
176
|
-
toValue: 1,
|
|
177
|
-
duration: 300,
|
|
178
|
-
useNativeDriver: true
|
|
179
|
-
}).start();
|
|
180
|
-
}, [opacity]);
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
if (value !== void 0 && String(value) !== localValue) {
|
|
183
|
-
setLocalValue(String(value));
|
|
184
|
-
}
|
|
185
|
-
}, [value]);
|
|
186
|
-
useEffect(() => {
|
|
187
|
-
var _a2;
|
|
188
|
-
if (status !== "typing") return;
|
|
189
|
-
if (!typewriter.enabled) {
|
|
190
|
-
setTypedPrompt(field.prompt);
|
|
191
|
-
onTypingComplete(field.key);
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
let i = 0;
|
|
195
|
-
const interval = setInterval(() => {
|
|
196
|
-
setTypedPrompt(field.prompt.slice(0, i + 1));
|
|
197
|
-
i++;
|
|
198
|
-
if (i >= field.prompt.length) {
|
|
199
|
-
clearInterval(interval);
|
|
200
|
-
onTypingComplete(field.key);
|
|
201
|
-
}
|
|
202
|
-
}, (_a2 = typewriter.delayMs) != null ? _a2 : 30);
|
|
203
|
-
return () => clearInterval(interval);
|
|
204
|
-
}, [status, field.prompt, field.key, typewriter, onTypingComplete]);
|
|
205
|
-
useEffect(() => {
|
|
206
|
-
if (status === "active" || status === "editing") {
|
|
207
|
-
setTimeout(() => {
|
|
208
|
-
var _a2;
|
|
209
|
-
(_a2 = inputRef.current) == null ? void 0 : _a2.focus();
|
|
210
|
-
}, 50);
|
|
211
|
-
}
|
|
212
|
-
}, [status]);
|
|
213
|
-
const handleConfirm = useCallback(async () => {
|
|
214
|
-
const syncResult = validateField(field, localValue);
|
|
215
|
-
if (!syncResult.valid) {
|
|
216
|
-
setErrorMsg(syncResult.error || "Invalid input");
|
|
217
|
-
onError(field.key, syncResult.error || "Invalid input");
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
if (hasAsyncValidation(field)) {
|
|
221
|
-
setIsValidating(true);
|
|
222
|
-
const asyncResult = await validateFieldAsync(field, localValue);
|
|
223
|
-
setIsValidating(false);
|
|
224
|
-
if (!asyncResult.valid) {
|
|
225
|
-
setErrorMsg(asyncResult.error || "Invalid input");
|
|
226
|
-
onError(field.key, asyncResult.error || "Invalid input");
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
setErrorMsg(null);
|
|
231
|
-
Keyboard.dismiss();
|
|
232
|
-
onConfirm(field.key, localValue);
|
|
233
|
-
}, [field, localValue, onError, onConfirm]);
|
|
234
|
-
const textColor = ((_a = theme == null ? void 0 : theme.colors) == null ? void 0 : _a.text) || (isDark ? "#fff" : "#000");
|
|
235
|
-
const primaryColor = ((_b = theme == null ? void 0 : theme.colors) == null ? void 0 : _b.primary) || "#007bff";
|
|
236
|
-
const isConfirmed = status === "confirmed";
|
|
237
|
-
const isActive = status === "active" || status === "editing";
|
|
238
|
-
return /* @__PURE__ */ jsxs(Animated.View, { style: [styles2.container, { opacity }], children: [
|
|
239
|
-
/* @__PURE__ */ jsxs(View, { style: styles2.promptRow, children: [
|
|
240
|
-
/* @__PURE__ */ jsx(Text, { style: [styles2.prompt, { color: textColor }], children: status === "typing" ? typedPrompt : field.prompt }),
|
|
241
|
-
isConfirmed && /* @__PURE__ */ jsx(Text, { style: [styles2.value, { color: primaryColor }], children: localValue }),
|
|
242
|
-
isConfirmed && editable && !locked && /* @__PURE__ */ jsx(TouchableOpacity, { onPress: () => onEdit(field.key), style: styles2.editBtn, children: /* @__PURE__ */ jsx(Text, { style: [styles2.editBtnText, { color: primaryColor }], children: editLabel }) })
|
|
243
|
-
] }),
|
|
244
|
-
isActive && /* @__PURE__ */ jsx(View, { style: styles2.inputRow, children: /* @__PURE__ */ jsx(
|
|
245
|
-
TextInput,
|
|
246
|
-
{
|
|
247
|
-
ref: inputRef,
|
|
248
|
-
style: [
|
|
249
|
-
styles2.input,
|
|
250
|
-
{ color: textColor, borderBottomColor: isValidating ? "#ccc" : primaryColor }
|
|
251
|
-
],
|
|
252
|
-
value: localValue,
|
|
253
|
-
onChangeText: (text) => {
|
|
254
|
-
setLocalValue(text);
|
|
255
|
-
onChange(field.key, text);
|
|
256
|
-
if (errorMsg) setErrorMsg(null);
|
|
257
|
-
},
|
|
258
|
-
onFocus: () => onFocus(field.key),
|
|
259
|
-
onBlur: () => onBlur(field.key, localValue),
|
|
260
|
-
onSubmitEditing: handleConfirm,
|
|
261
|
-
keyboardType: field.type === "email" ? "email-address" : "default",
|
|
262
|
-
secureTextEntry: field.type === "password",
|
|
263
|
-
returnKeyType: "next",
|
|
264
|
-
editable: !isValidating
|
|
265
|
-
}
|
|
266
|
-
) }),
|
|
267
|
-
/* @__PURE__ */ jsx(ErrorMessage, { message: errorMsg })
|
|
268
|
-
] });
|
|
269
|
-
};
|
|
270
|
-
var styles2 = StyleSheet.create({
|
|
271
|
-
container: {
|
|
272
|
-
marginVertical: 12
|
|
273
|
-
},
|
|
274
|
-
promptRow: {
|
|
275
|
-
flexDirection: "row",
|
|
276
|
-
alignItems: "center",
|
|
277
|
-
flexWrap: "wrap"
|
|
278
|
-
},
|
|
279
|
-
prompt: {
|
|
280
|
-
fontSize: 18,
|
|
281
|
-
marginRight: 8
|
|
282
|
-
},
|
|
283
|
-
value: {
|
|
284
|
-
fontSize: 18,
|
|
285
|
-
fontWeight: "600",
|
|
286
|
-
marginRight: 8
|
|
287
|
-
},
|
|
288
|
-
editBtn: {
|
|
289
|
-
marginLeft: 8,
|
|
290
|
-
paddingHorizontal: 8,
|
|
291
|
-
paddingVertical: 4,
|
|
292
|
-
borderRadius: 4,
|
|
293
|
-
backgroundColor: "rgba(0,0,0,0.05)"
|
|
294
|
-
},
|
|
295
|
-
editBtnText: {
|
|
296
|
-
fontSize: 12,
|
|
297
|
-
fontWeight: "bold"
|
|
298
|
-
},
|
|
299
|
-
inputRow: {
|
|
300
|
-
marginTop: 8
|
|
301
|
-
},
|
|
302
|
-
input: {
|
|
303
|
-
fontSize: 18,
|
|
304
|
-
paddingVertical: 8,
|
|
305
|
-
borderBottomWidth: 2
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
var ToastContext = createContext({
|
|
309
|
-
showToast: () => {
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
var useToast = () => useContext(ToastContext);
|
|
313
|
-
var ToastProvider = ({ children }) => {
|
|
314
|
-
const [toast, setToast] = useState(null);
|
|
315
|
-
const showToast = useCallback((message, type = "info") => {
|
|
316
|
-
setToast({ message, type });
|
|
317
|
-
setTimeout(() => setToast(null), 3e3);
|
|
318
|
-
}, []);
|
|
319
|
-
return /* @__PURE__ */ jsx(ToastContext.Provider, { value: { showToast }, children: /* @__PURE__ */ jsxs(View, { style: styles3.container, children: [
|
|
320
|
-
children,
|
|
321
|
-
toast && /* @__PURE__ */ jsx(View, { style: [styles3.toast, toast.type === "error" ? styles3.toastError : styles3.toastSuccess], children: /* @__PURE__ */ jsx(Text, { style: styles3.toastText, children: toast.message }) })
|
|
322
|
-
] }) });
|
|
323
|
-
};
|
|
324
|
-
var styles3 = StyleSheet.create({
|
|
325
|
-
container: {
|
|
326
|
-
flex: 1
|
|
327
|
-
},
|
|
328
|
-
toast: {
|
|
329
|
-
position: "absolute",
|
|
330
|
-
bottom: 40,
|
|
331
|
-
left: 20,
|
|
332
|
-
right: 20,
|
|
333
|
-
padding: 16,
|
|
334
|
-
borderRadius: 8,
|
|
335
|
-
backgroundColor: "#333",
|
|
336
|
-
alignItems: "center"
|
|
337
|
-
},
|
|
338
|
-
toastError: {
|
|
339
|
-
backgroundColor: "#d32f2f"
|
|
340
|
-
},
|
|
341
|
-
toastSuccess: {
|
|
342
|
-
backgroundColor: "#2e7d32"
|
|
343
|
-
},
|
|
344
|
-
toastText: {
|
|
345
|
-
color: "#fff",
|
|
346
|
-
fontSize: 14
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
var WelcomeScreen = ({ welcome, typewriter, onStart }) => {
|
|
350
|
-
var _a, _b;
|
|
351
|
-
const { theme, isDark } = useTheme();
|
|
352
|
-
const opacity = useRef(new Animated.Value(0)).current;
|
|
353
|
-
const [typedTitle, setTypedTitle] = useState("");
|
|
354
|
-
useEffect(() => {
|
|
355
|
-
Animated.timing(opacity, {
|
|
356
|
-
toValue: 1,
|
|
357
|
-
duration: 500,
|
|
358
|
-
useNativeDriver: true
|
|
359
|
-
}).start();
|
|
360
|
-
}, [opacity]);
|
|
361
|
-
useEffect(() => {
|
|
362
|
-
var _a2;
|
|
363
|
-
if (!typewriter.enabled) {
|
|
364
|
-
setTypedTitle(welcome.title);
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
let i = 0;
|
|
368
|
-
const interval = setInterval(() => {
|
|
369
|
-
setTypedTitle(welcome.title.slice(0, i + 1));
|
|
370
|
-
i++;
|
|
371
|
-
if (i >= welcome.title.length) {
|
|
372
|
-
clearInterval(interval);
|
|
373
|
-
}
|
|
374
|
-
}, (_a2 = typewriter.delayMs) != null ? _a2 : 30);
|
|
375
|
-
return () => clearInterval(interval);
|
|
376
|
-
}, [welcome.title, typewriter]);
|
|
377
|
-
const textColor = ((_a = theme == null ? void 0 : theme.colors) == null ? void 0 : _a.text) || (isDark ? "#fff" : "#000");
|
|
378
|
-
const primaryColor = ((_b = theme == null ? void 0 : theme.colors) == null ? void 0 : _b.primary) || "#007bff";
|
|
379
|
-
return /* @__PURE__ */ jsxs(Animated.View, { style: [styles4.container, { opacity }], children: [
|
|
380
|
-
/* @__PURE__ */ jsx(Text, { style: [styles4.title, { color: textColor }], children: typedTitle }),
|
|
381
|
-
welcome.subtitle && /* @__PURE__ */ jsx(Text, { style: [styles4.subtitle, { color: textColor, opacity: 0.7 }], children: welcome.subtitle }),
|
|
382
|
-
/* @__PURE__ */ jsx(
|
|
383
|
-
TouchableOpacity,
|
|
384
|
-
{
|
|
385
|
-
style: [styles4.button, { backgroundColor: primaryColor }],
|
|
386
|
-
onPress: onStart,
|
|
387
|
-
accessibilityRole: "button",
|
|
388
|
-
children: /* @__PURE__ */ jsx(Text, { style: styles4.buttonText, children: welcome.startLabel || "Start" })
|
|
389
|
-
}
|
|
390
|
-
)
|
|
391
|
-
] });
|
|
392
|
-
};
|
|
393
|
-
var styles4 = StyleSheet.create({
|
|
394
|
-
container: {
|
|
395
|
-
flex: 1,
|
|
396
|
-
justifyContent: "center",
|
|
397
|
-
alignItems: "flex-start",
|
|
398
|
-
padding: 20
|
|
399
|
-
},
|
|
400
|
-
title: {
|
|
401
|
-
fontSize: 28,
|
|
402
|
-
fontWeight: "bold",
|
|
403
|
-
marginBottom: 8
|
|
404
|
-
},
|
|
405
|
-
subtitle: {
|
|
406
|
-
fontSize: 16,
|
|
407
|
-
marginBottom: 24
|
|
408
|
-
},
|
|
409
|
-
button: {
|
|
410
|
-
paddingHorizontal: 24,
|
|
411
|
-
paddingVertical: 12,
|
|
412
|
-
borderRadius: 8
|
|
413
|
-
},
|
|
414
|
-
buttonText: {
|
|
415
|
-
color: "#fff",
|
|
416
|
-
fontSize: 16,
|
|
417
|
-
fontWeight: "600"
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
var DoneScreen = ({ done, values, meta }) => {
|
|
421
|
-
var _a;
|
|
422
|
-
const { theme, isDark } = useTheme();
|
|
423
|
-
const opacity = useRef(new Animated.Value(0)).current;
|
|
424
|
-
useEffect(() => {
|
|
425
|
-
Animated.timing(opacity, {
|
|
426
|
-
toValue: 1,
|
|
427
|
-
duration: 500,
|
|
428
|
-
useNativeDriver: true
|
|
429
|
-
}).start();
|
|
430
|
-
}, [opacity]);
|
|
431
|
-
const textColor = ((_a = theme == null ? void 0 : theme.colors) == null ? void 0 : _a.text) || (isDark ? "#fff" : "#000");
|
|
432
|
-
return /* @__PURE__ */ jsxs(Animated.View, { style: [styles5.container, { opacity }], children: [
|
|
433
|
-
/* @__PURE__ */ jsx(Text, { style: [styles5.title, { color: textColor }], children: done.title }),
|
|
434
|
-
done.subtitle && /* @__PURE__ */ jsx(Text, { style: [styles5.subtitle, { color: textColor, opacity: 0.7 }], children: done.subtitle })
|
|
435
|
-
] });
|
|
436
|
-
};
|
|
437
|
-
var styles5 = StyleSheet.create({
|
|
438
|
-
container: {
|
|
439
|
-
padding: 20,
|
|
440
|
-
marginTop: 20
|
|
441
|
-
},
|
|
442
|
-
title: {
|
|
443
|
-
fontSize: 24,
|
|
444
|
-
fontWeight: "bold",
|
|
445
|
-
marginBottom: 8
|
|
446
|
-
},
|
|
447
|
-
subtitle: {
|
|
448
|
-
fontSize: 16
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
var NarrativeFormInner = (props) => {
|
|
452
|
-
var _a, _b, _c;
|
|
453
|
-
const { isDark } = useTheme();
|
|
454
|
-
const i18n = useMemo(() => mergeStrings(props.strings), [props.strings]);
|
|
455
|
-
const { config: dynamicConfig, loading, error, retry } = useDynamicForm({ fieldsUrl: props.fieldsUrl, fieldsUrlHeaders: props.fieldsUrlHeaders });
|
|
456
|
-
const resolvedConfig = useMemo(() => {
|
|
457
|
-
if (props.fields) return { fields: props.fields };
|
|
458
|
-
if (props.formConfig) return props.formConfig;
|
|
459
|
-
if (dynamicConfig) return dynamicConfig;
|
|
460
|
-
return null;
|
|
461
|
-
}, [props.fields, props.formConfig, dynamicConfig]);
|
|
462
|
-
const fields = (_a = resolvedConfig == null ? void 0 : resolvedConfig.fields) != null ? _a : [];
|
|
463
|
-
const welcome = (_b = props.welcome) != null ? _b : resolvedConfig && "welcome" in resolvedConfig ? resolvedConfig.welcome : void 0;
|
|
464
|
-
const done = (_c = props.done) != null ? _c : resolvedConfig && "done" in resolvedConfig ? resolvedConfig.done : void 0;
|
|
465
|
-
const effectiveTypewriter = useMemo(() => {
|
|
466
|
-
var _a2, _b2;
|
|
467
|
-
return {
|
|
468
|
-
...props.typewriter,
|
|
469
|
-
enabled: props.reducedMotion ? false : (_b2 = (_a2 = props.typewriter) == null ? void 0 : _a2.enabled) != null ? _b2 : true
|
|
470
|
-
};
|
|
471
|
-
}, [props.typewriter, props.reducedMotion]);
|
|
472
|
-
const {
|
|
473
|
-
snapshot,
|
|
474
|
-
startTyping,
|
|
475
|
-
activateField,
|
|
476
|
-
confirmField,
|
|
477
|
-
editField,
|
|
478
|
-
reconfirmField,
|
|
479
|
-
next,
|
|
480
|
-
focusField,
|
|
481
|
-
reset,
|
|
482
|
-
getValues,
|
|
483
|
-
getMeta
|
|
484
|
-
} = useFormState(fields);
|
|
485
|
-
const showWelcome = (welcome == null ? void 0 : welcome.show) !== false && welcome !== void 0;
|
|
486
|
-
const [welcomeDismissed, setWelcomeDismissed] = useState(!showWelcome);
|
|
487
|
-
const hasStartedRef = useRef(false);
|
|
488
|
-
useEffect(() => {
|
|
489
|
-
if (!welcomeDismissed || hasStartedRef.current || fields.length === 0) return;
|
|
490
|
-
hasStartedRef.current = true;
|
|
491
|
-
const firstField = fields.find((f) => snapshot.statuses[f.key] !== "confirmed");
|
|
492
|
-
if (firstField) {
|
|
493
|
-
startTyping(firstField.key);
|
|
494
|
-
}
|
|
495
|
-
}, [welcomeDismissed, fields, startTyping, snapshot.statuses]);
|
|
496
|
-
const handleConfirm = useCallback((key, value) => {
|
|
497
|
-
var _a2, _b2;
|
|
498
|
-
const status = snapshot.statuses[key];
|
|
499
|
-
if (status === "editing") {
|
|
500
|
-
reconfirmField(key, value);
|
|
501
|
-
} else {
|
|
502
|
-
confirmField(key, value);
|
|
503
|
-
}
|
|
504
|
-
(_b2 = (_a2 = props.callbacks) == null ? void 0 : _a2.onFieldComplete) == null ? void 0 : _b2.call(_a2, key, value, 0);
|
|
505
|
-
const currentIndex = fields.findIndex((f) => f.key === key);
|
|
506
|
-
for (let i = currentIndex + 1; i < fields.length; i++) {
|
|
507
|
-
const nextField = fields[i];
|
|
508
|
-
if (!nextField || snapshot.statuses[nextField.key] === "confirmed") continue;
|
|
509
|
-
startTyping(nextField.key);
|
|
510
|
-
break;
|
|
511
|
-
}
|
|
512
|
-
}, [snapshot, fields, confirmField, reconfirmField, startTyping, props.callbacks]);
|
|
513
|
-
const visibleFields = useMemo(() => {
|
|
514
|
-
return fields.filter((field) => {
|
|
515
|
-
const status = snapshot.statuses[field.key];
|
|
516
|
-
return status === "typing" || status === "active" || status === "confirmed" || status === "editing";
|
|
517
|
-
});
|
|
518
|
-
}, [fields, snapshot.statuses]);
|
|
519
|
-
const scrollViewRef = useRef(null);
|
|
520
|
-
useEffect(() => {
|
|
521
|
-
if (visibleFields.length > 0) {
|
|
522
|
-
setTimeout(() => {
|
|
523
|
-
var _a2;
|
|
524
|
-
(_a2 = scrollViewRef.current) == null ? void 0 : _a2.scrollToEnd({ animated: true });
|
|
525
|
-
}, 100);
|
|
526
|
-
}
|
|
527
|
-
}, [visibleFields.length]);
|
|
528
|
-
return /* @__PURE__ */ jsxs(
|
|
529
|
-
ScrollView,
|
|
530
|
-
{
|
|
531
|
-
ref: scrollViewRef,
|
|
532
|
-
style: [styles6.container, isDark ? styles6.darkContainer : styles6.lightContainer],
|
|
533
|
-
contentContainerStyle: styles6.content,
|
|
534
|
-
keyboardShouldPersistTaps: "handled",
|
|
535
|
-
children: [
|
|
536
|
-
!welcomeDismissed && welcome && /* @__PURE__ */ jsx(
|
|
537
|
-
WelcomeScreen,
|
|
538
|
-
{
|
|
539
|
-
welcome,
|
|
540
|
-
typewriter: effectiveTypewriter,
|
|
541
|
-
onStart: () => setWelcomeDismissed(true)
|
|
542
|
-
}
|
|
543
|
-
),
|
|
544
|
-
welcomeDismissed && /* @__PURE__ */ jsx(View, { style: styles6.formBody, children: visibleFields.map((field) => {
|
|
545
|
-
var _a2, _b2, _c2, _d, _e;
|
|
546
|
-
return /* @__PURE__ */ jsx(
|
|
547
|
-
Line,
|
|
548
|
-
{
|
|
549
|
-
field,
|
|
550
|
-
status: (_a2 = snapshot.statuses[field.key]) != null ? _a2 : "idle",
|
|
551
|
-
value: (_c2 = (_b2 = props.values) == null ? void 0 : _b2[field.key]) != null ? _c2 : snapshot.values[field.key],
|
|
552
|
-
allValues: snapshot.values,
|
|
553
|
-
typewriter: effectiveTypewriter,
|
|
554
|
-
editable: (_d = props.editable) != null ? _d : true,
|
|
555
|
-
locked: false,
|
|
556
|
-
editLabel: (_e = props.editLabel) != null ? _e : i18n.editLabel,
|
|
557
|
-
onTypingComplete: (k) => activateField(k),
|
|
558
|
-
onConfirm: handleConfirm,
|
|
559
|
-
onEdit: (k) => {
|
|
560
|
-
var _a3, _b3;
|
|
561
|
-
editField(k);
|
|
562
|
-
(_b3 = (_a3 = props.callbacks) == null ? void 0 : _a3.onEdit) == null ? void 0 : _b3.call(_a3, k);
|
|
563
|
-
},
|
|
564
|
-
onError: (k, err) => {
|
|
565
|
-
var _a3, _b3;
|
|
566
|
-
return (_b3 = (_a3 = props.callbacks) == null ? void 0 : _a3.onError) == null ? void 0 : _b3.call(_a3, k, err);
|
|
567
|
-
},
|
|
568
|
-
onChange: (k, v) => {
|
|
569
|
-
var _a3, _b3;
|
|
570
|
-
return (_b3 = (_a3 = props.callbacks) == null ? void 0 : _a3.onChange) == null ? void 0 : _b3.call(_a3, k, v);
|
|
571
|
-
},
|
|
572
|
-
onFocus: (k) => {
|
|
573
|
-
var _a3, _b3;
|
|
574
|
-
return (_b3 = (_a3 = props.callbacks) == null ? void 0 : _a3.onFieldFocus) == null ? void 0 : _b3.call(_a3, k);
|
|
575
|
-
},
|
|
576
|
-
onBlur: (k, v) => {
|
|
577
|
-
var _a3, _b3;
|
|
578
|
-
return (_b3 = (_a3 = props.callbacks) == null ? void 0 : _a3.onFieldBlur) == null ? void 0 : _b3.call(_a3, k, v);
|
|
579
|
-
}
|
|
580
|
-
},
|
|
581
|
-
field.key
|
|
582
|
-
);
|
|
583
|
-
}) }),
|
|
584
|
-
snapshot.isComplete && done && /* @__PURE__ */ jsx(
|
|
585
|
-
DoneScreen,
|
|
586
|
-
{
|
|
587
|
-
done,
|
|
588
|
-
values: snapshot.values,
|
|
589
|
-
meta: getMeta(),
|
|
590
|
-
typewriter: effectiveTypewriter
|
|
591
|
-
}
|
|
592
|
-
)
|
|
593
|
-
]
|
|
594
|
-
}
|
|
595
|
-
);
|
|
596
|
-
};
|
|
597
|
-
var NarrativeForm = (props) => {
|
|
598
|
-
return /* @__PURE__ */ jsx(ThemeProvider, { theme: props.theme, children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(NarrativeFormInner, { ...props }) }) });
|
|
599
|
-
};
|
|
600
|
-
var styles6 = StyleSheet.create({
|
|
601
|
-
container: {
|
|
602
|
-
flex: 1
|
|
603
|
-
},
|
|
604
|
-
lightContainer: {
|
|
605
|
-
backgroundColor: "#ffffff"
|
|
606
|
-
},
|
|
607
|
-
darkContainer: {
|
|
608
|
-
backgroundColor: "#121212"
|
|
609
|
-
},
|
|
610
|
-
content: {
|
|
611
|
-
padding: 20,
|
|
612
|
-
flexGrow: 1
|
|
613
|
-
},
|
|
614
|
-
formBody: {
|
|
615
|
-
flex: 1
|
|
616
|
-
}
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
export { DoneScreen, ErrorMessage, Line, NarrativeForm, ThemeProvider, ToastProvider, WelcomeScreen, useDynamicForm, useFormState, useTheme, useToast };
|
|
620
|
-
//# sourceMappingURL=index.mjs.map
|
|
621
|
-
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/useFormState.ts","../src/hooks/useDynamicForm.ts","../src/components/ThemeProvider.tsx","../src/components/ErrorMessage.tsx","../src/components/Line.tsx","../src/components/ToastProvider.tsx","../src/components/WelcomeScreen.tsx","../src/components/DoneScreen.tsx","../src/components/NarrativeForm.tsx"],"names":["useState","useCallback","useMemo","useRef","useEffect","jsx","Animated","_a","styles","Text","StyleSheet","createContext","useContext","jsxs","View","TouchableOpacity","_b","_c"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * React hook wrapping the core FormStateEngine.\r\n *\r\n * @remarks\r\n * Bridges the framework-agnostic FormStateEngine with React's\r\n * state management. Each state mutation in the engine triggers\r\n * a React re-render via `useSyncExternalStore`-like pattern.\r\n */\r\n\r\nimport { useState, useRef, useCallback, useMemo } from \"react\";\r\nimport type { NarrativeField, NarrativeFieldValues, NarrativeMeta } from \"@viveksinghind/narrative-form-core\";\r\nimport { FormStateEngine } from \"@viveksinghind/narrative-form-core\";\r\nimport type { FieldStatus, FormStateSnapshot } from \"@viveksinghind/narrative-form-core\";\r\n\r\n/** Return value of the useFormState hook. */\r\nexport interface UseFormStateResult {\r\n /** Current snapshot of the form state. */\r\n snapshot: FormStateSnapshot;\r\n /** Start the typewriter animation for a field. */\r\n startTyping: (key: string) => void;\r\n /** Mark a field as active (typewriter done, input visible). */\r\n activateField: (key: string) => void;\r\n /** Confirm a field with a value. */\r\n confirmField: (key: string, value: string | string[]) => void;\r\n /** Reopen a confirmed field for editing. */\r\n editField: (key: string) => void;\r\n /** Re-confirm a field after editing. */\r\n reconfirmField: (key: string, value: string | string[]) => void;\r\n /** Move to the next field. */\r\n next: () => void;\r\n /** Focus a specific field by key. */\r\n focusField: (key: string) => void;\r\n /** Reset all form state. */\r\n reset: () => void;\r\n /** Get all confirmed values. */\r\n getValues: () => NarrativeFieldValues;\r\n /** Get analytics metadata. */\r\n getMeta: (formId?: string, formVersion?: number) => NarrativeMeta;\r\n}\r\n\r\n/**\r\n * React hook that manages the narrative form state.\r\n *\r\n * @param fields - Ordered array of field configurations\r\n * @returns Form state and mutation methods\r\n */\r\nexport function useFormState(fields: readonly NarrativeField[]): UseFormStateResult {\r\n // Use a counter to force re-renders when the engine mutates\r\n const [, setTick] = useState(0);\r\n\r\n const engineRef = useRef<FormStateEngine | null>(null);\r\n\r\n // Lazily initialise the engine\r\n if (engineRef.current === null) {\r\n engineRef.current = new FormStateEngine(fields, () => {\r\n setTick((t) => t + 1);\r\n });\r\n }\r\n\r\n const engine = engineRef.current;\r\n\r\n // Stable method references\r\n const startTyping = useCallback((key: string) => engine.startTyping(key), [engine]);\r\n const activateField = useCallback((key: string) => engine.activateField(key), [engine]);\r\n const confirmField = useCallback(\r\n (key: string, value: string | string[]) => engine.confirmField(key, value),\r\n [engine],\r\n );\r\n const editField = useCallback((key: string) => engine.editField(key), [engine]);\r\n const reconfirmField = useCallback(\r\n (key: string, value: string | string[]) => engine.reconfirmField(key, value),\r\n [engine],\r\n );\r\n const next = useCallback(() => engine.next(), [engine]);\r\n const focusField = useCallback((key: string) => engine.focusField(key), [engine]);\r\n const reset = useCallback(() => engine.reset(), [engine]);\r\n const getValues = useCallback(() => engine.getValues(), [engine]);\r\n const getMeta = useCallback(\r\n (formId?: string, formVersion?: number) => engine.getMeta(formId, formVersion),\r\n [engine],\r\n );\r\n\r\n const snapshot = engine.getSnapshot();\r\n\r\n return useMemo(\r\n () => ({\r\n snapshot,\r\n startTyping,\r\n activateField,\r\n confirmField,\r\n editField,\r\n reconfirmField,\r\n next,\r\n focusField,\r\n reset,\r\n getValues,\r\n getMeta,\r\n }),\r\n // snapshot changes every tick, which is what we want\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n [snapshot],\r\n );\r\n}\r\n","import { useState, useEffect, useCallback } from \"react\";\r\nimport type { NarrativeFormConfig } from \"@viveksinghind/narrative-form-core\";\r\nimport { fetchFormConfig, ConfigFetchError } from \"@viveksinghind/narrative-form-core\";\r\n\r\nexport interface UseDynamicFormProps {\r\n fieldsUrl?: string;\r\n fieldsUrlHeaders?: Record<string, string>;\r\n formConfig?: NarrativeFormConfig;\r\n onFetchError?: (error: Error) => void;\r\n}\r\n\r\nexport interface UseDynamicFormResult {\r\n config: NarrativeFormConfig | null;\r\n loading: boolean;\r\n error: Error | null;\r\n retry: () => void;\r\n}\r\n\r\n/**\r\n * Hook to manage fetching and parsing server-driven form configuration.\r\n */\r\nexport function useDynamicForm({\r\n fieldsUrl,\r\n fieldsUrlHeaders,\r\n formConfig,\r\n onFetchError,\r\n}: UseDynamicFormProps): UseDynamicFormResult {\r\n const [config, setConfig] = useState<NarrativeFormConfig | null>(formConfig ?? null);\r\n const [loading, setLoading] = useState<boolean>(!!fieldsUrl && !formConfig);\r\n const [error, setError] = useState<Error | null>(null);\r\n const [retryCount, setRetryCount] = useState(0);\r\n\r\n useEffect(() => {\r\n // If a complete config is passed via props, use it immediately\r\n if (formConfig) {\r\n setConfig(formConfig);\r\n setLoading(false);\r\n setError(null);\r\n return;\r\n }\r\n\r\n if (!fieldsUrl) {\r\n return;\r\n }\r\n\r\n let isMounted = true;\r\n setLoading(true);\r\n setError(null);\r\n\r\n fetchFormConfig(fieldsUrl, { headers: fieldsUrlHeaders })\r\n .then((data) => {\r\n if (isMounted) {\r\n setConfig(data);\r\n setLoading(false);\r\n }\r\n })\r\n .catch((err: Error) => {\r\n if (isMounted) {\r\n setError(err);\r\n setLoading(false);\r\n onFetchError?.(err);\r\n }\r\n });\r\n\r\n return () => {\r\n isMounted = false;\r\n };\r\n }, [fieldsUrl, fieldsUrlHeaders, formConfig, retryCount, onFetchError]);\r\n\r\n const retry = useCallback(() => {\r\n if (fieldsUrl && !formConfig) {\r\n setRetryCount((c) => c + 1);\r\n }\r\n }, [fieldsUrl, formConfig]);\r\n\r\n return { config, loading, error, retry };\r\n}\r\n","import React, { createContext, useContext, useMemo } from \"react\";\nimport type { NarrativeTheme } from \"@viveksinghind/narrative-form-core\";\n\nexport interface ThemeContextValue {\n theme: NarrativeTheme | undefined;\n isDark: boolean;\n}\n\nconst ThemeContext = createContext<ThemeContextValue>({\n theme: undefined,\n isDark: false,\n});\n\nexport const useTheme = () => useContext(ThemeContext);\n\nexport interface ThemeProviderProps {\n theme?: NarrativeTheme;\n children: React.ReactNode;\n}\n\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({ theme, children }) => {\n const value = useMemo(() => {\n // In React Native we can check Appearance.getColorScheme() if we want auto dark mode,\n // but for now we just rely on the theme prop.\n return {\n theme,\n isDark: !!theme?.mode && theme.mode === \"dark\",\n };\n }, [theme]);\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n};\n","import React, { useEffect, useRef } from \"react\";\nimport { Animated, Text, StyleSheet } from \"react-native\";\nimport type { NarrativeErrorDisplay } from \"@viveksinghind/narrative-form-core\";\nimport { useTheme } from \"./ThemeProvider\";\n\nexport interface ErrorMessageProps {\n message: string | null;\n config?: NarrativeErrorDisplay;\n}\n\nexport const ErrorMessage: React.FC<ErrorMessageProps> = ({ message, config }) => {\n const { theme } = useTheme();\n const opacity = useRef(new Animated.Value(0)).current;\n const translateY = useRef(new Animated.Value(-10)).current;\n\n useEffect(() => {\n if (message) {\n Animated.parallel([\n Animated.timing(opacity, {\n toValue: 1,\n duration: 200,\n useNativeDriver: true,\n }),\n Animated.timing(translateY, {\n toValue: 0,\n duration: 200,\n useNativeDriver: true,\n })\n ]).start();\n } else {\n opacity.setValue(0);\n translateY.setValue(-10);\n }\n }, [message, opacity, translateY]);\n\n if (!message) return null;\n\n return (\n <Animated.View style={{ opacity, transform: [{ translateY }] }}>\n <Text style={[styles.errorText, { color: theme?.colors?.error || \"#d32f2f\" }]}>\n {message}\n </Text>\n </Animated.View>\n );\n};\n\nconst styles = StyleSheet.create({\n errorText: {\n fontSize: 14,\n marginTop: 4,\n marginBottom: 8,\n }\n});\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Animated, View, Text, TextInput, TouchableOpacity, StyleSheet, Easing, Keyboard } from \"react-native\";\nimport type { NarrativeField, FieldStatus, NarrativeTypewriter, NarrativeFieldValues } from \"@viveksinghind/narrative-form-core\";\nimport { validateField, validateFieldAsync, hasAsyncValidation } from \"@viveksinghind/narrative-form-core\";\nimport { ErrorMessage } from \"./ErrorMessage\";\nimport { useTheme } from \"./ThemeProvider\";\n\nexport interface LineProps {\n field: NarrativeField;\n status: FieldStatus;\n value: any;\n allValues: NarrativeFieldValues;\n typewriter: NarrativeTypewriter;\n editable: boolean;\n locked: boolean;\n editLabel: string;\n onTypingComplete: (key: string) => void;\n onConfirm: (key: string, value: string) => void;\n onEdit: (key: string) => void;\n onError: (key: string, error: string) => void;\n onChange: (key: string, value: string) => void;\n onFocus: (key: string) => void;\n onBlur: (key: string, value: string) => void;\n}\n\nexport const Line: React.FC<LineProps> = ({\n field,\n status,\n value,\n allValues,\n typewriter,\n editable,\n locked,\n editLabel,\n onTypingComplete,\n onConfirm,\n onEdit,\n onError,\n onChange,\n onFocus,\n onBlur,\n}) => {\n const { theme, isDark } = useTheme();\n \n const [typedPrompt, setTypedPrompt] = useState(\"\");\n const [localValue, setLocalValue] = useState(value ? String(value) : \"\");\n const [errorMsg, setErrorMsg] = useState<string | null>(null);\n const [isValidating, setIsValidating] = useState(false);\n \n const inputRef = useRef<TextInput>(null);\n const opacity = useRef(new Animated.Value(0)).current;\n\n // Fade in on mount\n useEffect(() => {\n Animated.timing(opacity, {\n toValue: 1,\n duration: 300,\n useNativeDriver: true,\n }).start();\n }, [opacity]);\n\n // Sync external value\n useEffect(() => {\n if (value !== undefined && String(value) !== localValue) {\n setLocalValue(String(value));\n }\n }, [value]);\n\n // Typewriter effect\n useEffect(() => {\n if (status !== \"typing\") return;\n \n if (!typewriter.enabled) {\n setTypedPrompt(field.prompt);\n onTypingComplete(field.key);\n return;\n }\n\n let i = 0;\n const interval = setInterval(() => {\n setTypedPrompt(field.prompt.slice(0, i + 1));\n i++;\n if (i >= field.prompt.length) {\n clearInterval(interval);\n onTypingComplete(field.key);\n }\n }, typewriter.delayMs ?? 30);\n\n return () => clearInterval(interval);\n }, [status, field.prompt, field.key, typewriter, onTypingComplete]);\n\n // Focus input when active\n useEffect(() => {\n if (status === \"active\" || status === \"editing\") {\n setTimeout(() => {\n inputRef.current?.focus();\n }, 50); // slight delay for layout\n }\n }, [status]);\n\n const handleConfirm = useCallback(async () => {\n // Sync validation\n const syncResult = validateField(field, localValue);\n if (!syncResult.valid) {\n setErrorMsg(syncResult.error || \"Invalid input\");\n onError(field.key, syncResult.error || \"Invalid input\");\n return;\n }\n\n // Async validation\n if (hasAsyncValidation(field)) {\n setIsValidating(true);\n const asyncResult = await validateFieldAsync(field, localValue);\n setIsValidating(false);\n \n if (!asyncResult.valid) {\n setErrorMsg(asyncResult.error || \"Invalid input\");\n onError(field.key, asyncResult.error || \"Invalid input\");\n return;\n }\n }\n\n setErrorMsg(null);\n Keyboard.dismiss();\n onConfirm(field.key, localValue);\n }, [field, localValue, onError, onConfirm]);\n\n const textColor = theme?.colors?.text || (isDark ? \"#fff\" : \"#000\");\n const primaryColor = theme?.colors?.primary || \"#007bff\";\n\n const isConfirmed = status === \"confirmed\";\n const isActive = status === \"active\" || status === \"editing\";\n\n return (\n <Animated.View style={[styles.container, { opacity }]}>\n <View style={styles.promptRow}>\n <Text style={[styles.prompt, { color: textColor }]}>\n {status === \"typing\" ? typedPrompt : field.prompt}\n </Text>\n \n {isConfirmed && (\n <Text style={[styles.value, { color: primaryColor }]}>{localValue}</Text>\n )}\n \n {isConfirmed && editable && !locked && (\n <TouchableOpacity onPress={() => onEdit(field.key)} style={styles.editBtn}>\n <Text style={[styles.editBtnText, { color: primaryColor }]}>{editLabel}</Text>\n </TouchableOpacity>\n )}\n </View>\n\n {isActive && (\n <View style={styles.inputRow}>\n <TextInput\n ref={inputRef}\n style={[\n styles.input,\n { color: textColor, borderBottomColor: isValidating ? \"#ccc\" : primaryColor }\n ]}\n value={localValue}\n onChangeText={(text) => {\n setLocalValue(text);\n onChange(field.key, text);\n if (errorMsg) setErrorMsg(null);\n }}\n onFocus={() => onFocus(field.key)}\n onBlur={() => onBlur(field.key, localValue)}\n onSubmitEditing={handleConfirm}\n keyboardType={field.type === \"email\" ? \"email-address\" : \"default\"}\n secureTextEntry={field.type === \"password\"}\n returnKeyType=\"next\"\n editable={!isValidating}\n />\n </View>\n )}\n \n <ErrorMessage message={errorMsg} />\n </Animated.View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n marginVertical: 12,\n },\n promptRow: {\n flexDirection: \"row\",\n alignItems: \"center\",\n flexWrap: \"wrap\",\n },\n prompt: {\n fontSize: 18,\n marginRight: 8,\n },\n value: {\n fontSize: 18,\n fontWeight: \"600\",\n marginRight: 8,\n },\n editBtn: {\n marginLeft: 8,\n paddingHorizontal: 8,\n paddingVertical: 4,\n borderRadius: 4,\n backgroundColor: \"rgba(0,0,0,0.05)\",\n },\n editBtnText: {\n fontSize: 12,\n fontWeight: \"bold\",\n },\n inputRow: {\n marginTop: 8,\n },\n input: {\n fontSize: 18,\n paddingVertical: 8,\n borderBottomWidth: 2,\n }\n});\n","import React, { createContext, useContext, useState, useCallback } from \"react\";\nimport { View, Text, StyleSheet, Animated } from \"react-native\";\n\nexport interface ToastContextValue {\n showToast: (message: string, type?: \"error\" | \"success\" | \"info\") => void;\n}\n\nconst ToastContext = createContext<ToastContextValue>({\n showToast: () => {},\n});\n\nexport const useToast = () => useContext(ToastContext);\n\nexport const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [toast, setToast] = useState<{ message: string; type: \"error\" | \"success\" | \"info\" } | null>(null);\n\n const showToast = useCallback((message: string, type: \"error\" | \"success\" | \"info\" = \"info\") => {\n setToast({ message, type });\n setTimeout(() => setToast(null), 3000);\n }, []);\n\n return (\n <ToastContext.Provider value={{ showToast }}>\n <View style={styles.container}>\n {children}\n {toast && (\n <View style={[styles.toast, toast.type === \"error\" ? styles.toastError : styles.toastSuccess]}>\n <Text style={styles.toastText}>{toast.message}</Text>\n </View>\n )}\n </View>\n </ToastContext.Provider>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n },\n toast: {\n position: \"absolute\",\n bottom: 40,\n left: 20,\n right: 20,\n padding: 16,\n borderRadius: 8,\n backgroundColor: \"#333\",\n alignItems: \"center\",\n },\n toastError: {\n backgroundColor: \"#d32f2f\",\n },\n toastSuccess: {\n backgroundColor: \"#2e7d32\",\n },\n toastText: {\n color: \"#fff\",\n fontSize: 14,\n },\n});\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { Animated, View, Text, TouchableOpacity, StyleSheet } from \"react-native\";\nimport type { NarrativeWelcome, NarrativeTypewriter } from \"@viveksinghind/narrative-form-core\";\nimport { useTheme } from \"./ThemeProvider\";\n\nexport interface WelcomeScreenProps {\n welcome: NarrativeWelcome;\n typewriter: NarrativeTypewriter;\n onStart: () => void;\n}\n\nexport const WelcomeScreen: React.FC<WelcomeScreenProps> = ({ welcome, typewriter, onStart }) => {\n const { theme, isDark } = useTheme();\n const opacity = useRef(new Animated.Value(0)).current;\n const [typedTitle, setTypedTitle] = useState(\"\");\n \n useEffect(() => {\n Animated.timing(opacity, {\n toValue: 1,\n duration: 500,\n useNativeDriver: true,\n }).start();\n }, [opacity]);\n\n useEffect(() => {\n if (!typewriter.enabled) {\n setTypedTitle(welcome.title);\n return;\n }\n\n let i = 0;\n const interval = setInterval(() => {\n setTypedTitle(welcome.title.slice(0, i + 1));\n i++;\n if (i >= welcome.title.length) {\n clearInterval(interval);\n }\n }, typewriter.delayMs ?? 30);\n\n return () => clearInterval(interval);\n }, [welcome.title, typewriter]);\n\n const textColor = theme?.colors?.text || (isDark ? \"#fff\" : \"#000\");\n const primaryColor = theme?.colors?.primary || \"#007bff\";\n\n return (\n <Animated.View style={[styles.container, { opacity }]}>\n <Text style={[styles.title, { color: textColor }]}>{typedTitle}</Text>\n {welcome.subtitle && (\n <Text style={[styles.subtitle, { color: textColor, opacity: 0.7 }]}>{welcome.subtitle}</Text>\n )}\n <TouchableOpacity \n style={[styles.button, { backgroundColor: primaryColor }]} \n onPress={onStart}\n accessibilityRole=\"button\"\n >\n <Text style={styles.buttonText}>{welcome.startLabel || \"Start\"}</Text>\n </TouchableOpacity>\n </Animated.View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n justifyContent: \"center\",\n alignItems: \"flex-start\",\n padding: 20,\n },\n title: {\n fontSize: 28,\n fontWeight: \"bold\",\n marginBottom: 8,\n },\n subtitle: {\n fontSize: 16,\n marginBottom: 24,\n },\n button: {\n paddingHorizontal: 24,\n paddingVertical: 12,\n borderRadius: 8,\n },\n buttonText: {\n color: \"#fff\",\n fontSize: 16,\n fontWeight: \"600\",\n }\n});\n","import React, { useEffect, useRef } from \"react\";\nimport { Animated, View, Text, StyleSheet } from \"react-native\";\nimport type { NarrativeDone, NarrativeFieldValues, NarrativeMeta, NarrativeTypewriter } from \"@viveksinghind/narrative-form-core\";\nimport { useTheme } from \"./ThemeProvider\";\n\nexport interface DoneScreenProps {\n done: NarrativeDone;\n values: NarrativeFieldValues;\n meta: NarrativeMeta;\n typewriter: NarrativeTypewriter;\n}\n\nexport const DoneScreen: React.FC<DoneScreenProps> = ({ done, values, meta }) => {\n const { theme, isDark } = useTheme();\n const opacity = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n Animated.timing(opacity, {\n toValue: 1,\n duration: 500,\n useNativeDriver: true,\n }).start();\n }, [opacity]);\n\n const textColor = theme?.colors?.text || (isDark ? \"#fff\" : \"#000\");\n\n return (\n <Animated.View style={[styles.container, { opacity }]}>\n <Text style={[styles.title, { color: textColor }]}>{done.title}</Text>\n {done.subtitle && (\n <Text style={[styles.subtitle, { color: textColor, opacity: 0.7 }]}>{done.subtitle}</Text>\n )}\n </Animated.View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n padding: 20,\n marginTop: 20,\n },\n title: {\n fontSize: 24,\n fontWeight: \"bold\",\n marginBottom: 8,\n },\n subtitle: {\n fontSize: 16,\n }\n});\n","import React, { useCallback, useEffect, useRef, useState, useMemo } from \"react\";\nimport { View, StyleSheet, ScrollView } from \"react-native\";\nimport type {\n NarrativeField,\n NarrativeTheme,\n NarrativeTypewriter,\n NarrativeWelcome,\n NarrativeDone,\n NarrativeCallbacks,\n NarrativeFieldValues,\n NarrativeMeta,\n NarrativeRefHandle,\n NarrativeI18n,\n NarrativeFormConfig,\n NarrativeCrossFieldValidator,\n} from \"@viveksinghind/narrative-form-core\";\nimport { mergeStrings } from \"@viveksinghind/narrative-form-core\";\nimport { useFormState } from \"../hooks/useFormState\";\nimport { useDynamicForm } from \"../hooks/useDynamicForm\";\nimport { Line } from \"./Line\";\nimport { ToastProvider } from \"./ToastProvider\";\nimport { ThemeProvider, useTheme } from \"./ThemeProvider\";\nimport { WelcomeScreen } from \"./WelcomeScreen\";\nimport { DoneScreen } from \"./DoneScreen\";\n\nexport interface NarrativeFormProps {\n fields?: NarrativeField[];\n fieldsUrl?: string;\n fieldsUrlHeaders?: Record<string, string>;\n formConfig?: NarrativeFormConfig;\n theme?: NarrativeTheme;\n typewriter?: NarrativeTypewriter;\n welcome?: NarrativeWelcome;\n done?: NarrativeDone;\n editable?: boolean;\n editLabel?: string;\n callbacks?: NarrativeCallbacks;\n formRef?: React.Ref<NarrativeRefHandle>;\n defaultValues?: NarrativeFieldValues;\n values?: NarrativeFieldValues;\n strings?: Partial<NarrativeI18n>;\n locale?: string;\n direction?: \"ltr\" | \"rtl\";\n crossFieldValidators?: NarrativeCrossFieldValidator[];\n reducedMotion?: boolean;\n}\n\nconst NarrativeFormInner: React.FC<NarrativeFormProps> = (props) => {\n const { isDark } = useTheme();\n const i18n = useMemo(() => mergeStrings(props.strings), [props.strings]);\n\n const { config: dynamicConfig, loading, error, retry } =\n useDynamicForm({ fieldsUrl: props.fieldsUrl, fieldsUrlHeaders: props.fieldsUrlHeaders });\n\n const resolvedConfig = useMemo(() => {\n if (props.fields) return { fields: props.fields };\n if (props.formConfig) return props.formConfig;\n if (dynamicConfig) return dynamicConfig;\n return null;\n }, [props.fields, props.formConfig, dynamicConfig]);\n\n const fields = resolvedConfig?.fields ?? [];\n const welcome = props.welcome ?? (resolvedConfig && \"welcome\" in resolvedConfig ? (resolvedConfig as any).welcome : undefined);\n const done = props.done ?? (resolvedConfig && \"done\" in resolvedConfig ? (resolvedConfig as any).done : undefined);\n\n const effectiveTypewriter = useMemo<NarrativeTypewriter>(() => ({\n ...props.typewriter,\n enabled: props.reducedMotion ? false : (props.typewriter?.enabled ?? true),\n }), [props.typewriter, props.reducedMotion]);\n\n const {\n snapshot,\n startTyping,\n activateField,\n confirmField,\n editField,\n reconfirmField,\n next,\n focusField,\n reset,\n getValues,\n getMeta,\n } = useFormState(fields);\n\n const showWelcome = welcome?.show !== false && welcome !== undefined;\n const [welcomeDismissed, setWelcomeDismissed] = useState(!showWelcome);\n \n const hasStartedRef = useRef(false);\n\n useEffect(() => {\n if (!welcomeDismissed || hasStartedRef.current || fields.length === 0) return;\n hasStartedRef.current = true;\n const firstField = fields.find((f) => snapshot.statuses[f.key] !== \"confirmed\");\n if (firstField) {\n startTyping(firstField.key);\n }\n }, [welcomeDismissed, fields, startTyping, snapshot.statuses]);\n\n const handleConfirm = useCallback((key: string, value: string) => {\n const status = snapshot.statuses[key];\n if (status === \"editing\") {\n reconfirmField(key, value);\n } else {\n confirmField(key, value);\n }\n props.callbacks?.onFieldComplete?.(key, value, 0);\n\n const currentIndex = fields.findIndex((f) => f.key === key);\n for (let i = currentIndex + 1; i < fields.length; i++) {\n const nextField = fields[i];\n if (!nextField || snapshot.statuses[nextField.key] === \"confirmed\") continue;\n startTyping(nextField.key);\n break;\n }\n }, [snapshot, fields, confirmField, reconfirmField, startTyping, props.callbacks]);\n\n const visibleFields = useMemo(() => {\n return fields.filter((field) => {\n const status = snapshot.statuses[field.key];\n return status === \"typing\" || status === \"active\" || status === \"confirmed\" || status === \"editing\";\n });\n }, [fields, snapshot.statuses]);\n\n const scrollViewRef = useRef<ScrollView>(null);\n\n useEffect(() => {\n if (visibleFields.length > 0) {\n setTimeout(() => {\n scrollViewRef.current?.scrollToEnd({ animated: true });\n }, 100);\n }\n }, [visibleFields.length]);\n\n return (\n <ScrollView \n ref={scrollViewRef}\n style={[styles.container, isDark ? styles.darkContainer : styles.lightContainer]}\n contentContainerStyle={styles.content}\n keyboardShouldPersistTaps=\"handled\"\n >\n {!welcomeDismissed && welcome && (\n <WelcomeScreen\n welcome={welcome}\n typewriter={effectiveTypewriter}\n onStart={() => setWelcomeDismissed(true)}\n />\n )}\n\n {welcomeDismissed && (\n <View style={styles.formBody}>\n {visibleFields.map((field) => (\n <Line\n key={field.key}\n field={field}\n status={snapshot.statuses[field.key] ?? \"idle\"}\n value={props.values?.[field.key] ?? snapshot.values[field.key]}\n allValues={snapshot.values}\n typewriter={effectiveTypewriter}\n editable={props.editable ?? true}\n locked={false}\n editLabel={props.editLabel ?? i18n.editLabel}\n onTypingComplete={(k) => activateField(k)}\n onConfirm={handleConfirm}\n onEdit={(k) => {\n editField(k);\n props.callbacks?.onEdit?.(k);\n }}\n onError={(k, err) => props.callbacks?.onError?.(k, err)}\n onChange={(k, v) => props.callbacks?.onChange?.(k, v)}\n onFocus={(k) => props.callbacks?.onFieldFocus?.(k)}\n onBlur={(k, v) => props.callbacks?.onFieldBlur?.(k, v)}\n />\n ))}\n </View>\n )}\n\n {snapshot.isComplete && done && (\n <DoneScreen\n done={done}\n values={snapshot.values}\n meta={getMeta()}\n typewriter={effectiveTypewriter}\n />\n )}\n </ScrollView>\n );\n};\n\nexport const NarrativeForm: React.FC<NarrativeFormProps> = (props) => {\n return (\n <ThemeProvider theme={props.theme}>\n <ToastProvider>\n <NarrativeFormInner {...props} />\n </ToastProvider>\n </ThemeProvider>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n },\n lightContainer: {\n backgroundColor: \"#ffffff\",\n },\n darkContainer: {\n backgroundColor: \"#121212\",\n },\n content: {\n padding: 20,\n flexGrow: 1,\n },\n formBody: {\n flex: 1,\n }\n});\n"]}
|