@aifeatures/react 0.0.1

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/dist/index.cjs ADDED
@@ -0,0 +1,336 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ AiFeaturesForm: () => AiFeaturesForm,
25
+ Controller: () => import_react_hook_form4.Controller,
26
+ FormActions: () => FormActions,
27
+ FormField: () => FormField,
28
+ FormStatus: () => FormStatus,
29
+ SubmitButton: () => SubmitButton,
30
+ useAiFeaturesForm: () => useAiFeaturesForm,
31
+ useFormContext: () => import_react_hook_form4.useFormContext
32
+ });
33
+ module.exports = __toCommonJS(index_exports);
34
+
35
+ // src/AiFeaturesForm.tsx
36
+ var import_react = require("react");
37
+ var import_react_hook_form = require("react-hook-form");
38
+ var import_jsx_runtime = require("react/jsx-runtime");
39
+ var AIFEATURES_API_URL = "https://aifeatures.dev";
40
+ var FormContext = (0, import_react.createContext)(null);
41
+ function useAiFeaturesForm() {
42
+ const context = (0, import_react.useContext)(FormContext);
43
+ if (!context) {
44
+ throw new Error("useAiFeaturesForm must be used within an AiFeaturesForm");
45
+ }
46
+ return context;
47
+ }
48
+ function AiFeaturesForm({
49
+ formId,
50
+ onSuccess,
51
+ onError,
52
+ className,
53
+ children
54
+ }) {
55
+ const [config, setConfig] = (0, import_react.useState)(null);
56
+ const [configError, setConfigError] = (0, import_react.useState)(null);
57
+ const [formState, setFormState] = (0, import_react.useState)({
58
+ isSubmitting: false,
59
+ isSuccess: false,
60
+ isError: false,
61
+ error: null
62
+ });
63
+ const form = (0, import_react_hook_form.useForm)();
64
+ (0, import_react.useEffect)(() => {
65
+ async function fetchConfig() {
66
+ try {
67
+ const response = await fetch(`${AIFEATURES_API_URL}/f/${formId}/config`);
68
+ if (!response.ok) {
69
+ throw new Error(`Failed to load form: ${response.status}`);
70
+ }
71
+ const data = await response.json();
72
+ setConfig(data);
73
+ } catch (err) {
74
+ const error = err instanceof Error ? err : new Error("Failed to load form config");
75
+ setConfigError(error);
76
+ onError?.(error);
77
+ }
78
+ }
79
+ fetchConfig();
80
+ }, [formId, onError]);
81
+ const onSubmit = async (data) => {
82
+ if (!config) return;
83
+ setFormState({
84
+ isSubmitting: true,
85
+ isSuccess: false,
86
+ isError: false,
87
+ error: null
88
+ });
89
+ try {
90
+ const formData = new FormData();
91
+ for (const [key, value] of Object.entries(data)) {
92
+ if (value instanceof FileList) {
93
+ for (const file of Array.from(value)) {
94
+ formData.append(key, file);
95
+ }
96
+ } else if (value !== void 0 && value !== null) {
97
+ formData.append(key, String(value));
98
+ }
99
+ }
100
+ const response = await fetch(config.endpoint_url, {
101
+ method: "POST",
102
+ body: formData
103
+ });
104
+ if (!response.ok) {
105
+ const errorText = await response.text();
106
+ throw new Error(errorText || `Submission failed: ${response.status}`);
107
+ }
108
+ setFormState({
109
+ isSubmitting: false,
110
+ isSuccess: true,
111
+ isError: false,
112
+ error: null
113
+ });
114
+ form.reset();
115
+ if (typeof window !== "undefined" && window.turnstile) {
116
+ window.turnstile.reset();
117
+ }
118
+ onSuccess?.(data);
119
+ } catch (err) {
120
+ const error = err instanceof Error ? err : new Error("Form submission failed");
121
+ setFormState({
122
+ isSubmitting: false,
123
+ isSuccess: false,
124
+ isError: true,
125
+ error
126
+ });
127
+ onError?.(error);
128
+ }
129
+ };
130
+ if (!config && !configError) {
131
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: "Loading form..." });
132
+ }
133
+ if (configError) {
134
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "Failed to load form. Please try again later." }) });
135
+ }
136
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FormContext.Provider, { value: { formState, config }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_hook_form.FormProvider, { ...form, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: form.handleSubmit(onSubmit), className, children: [
137
+ children,
138
+ config?.turnstile_sitekey && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
139
+ "input",
140
+ {
141
+ type: "hidden",
142
+ ...form.register("cf-turnstile-response", {
143
+ required: "Please complete the CAPTCHA verification"
144
+ })
145
+ }
146
+ )
147
+ ] }) }) });
148
+ }
149
+
150
+ // src/FormField.tsx
151
+ var import_react_hook_form2 = require("react-hook-form");
152
+ var import_jsx_runtime2 = require("react/jsx-runtime");
153
+ function FormField({
154
+ name,
155
+ label,
156
+ type = "text",
157
+ placeholder,
158
+ required = false,
159
+ className,
160
+ options,
161
+ accept,
162
+ multiple
163
+ }) {
164
+ const { register, formState: { errors } } = (0, import_react_hook_form2.useFormContext)();
165
+ const error = errors[name];
166
+ const errorMessage = error?.message;
167
+ const inputClassName = className || "";
168
+ const registerOptions = {
169
+ required: required ? `${label || name} is required` : false
170
+ };
171
+ if (type === "textarea") {
172
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
173
+ label && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: name, children: [
174
+ label,
175
+ required && " *"
176
+ ] }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
178
+ "textarea",
179
+ {
180
+ id: name,
181
+ placeholder,
182
+ className: inputClassName,
183
+ ...register(name, registerOptions)
184
+ }
185
+ ),
186
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: errorMessage })
187
+ ] });
188
+ }
189
+ if (type === "select" && options) {
190
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
191
+ label && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: name, children: [
192
+ label,
193
+ required && " *"
194
+ ] }),
195
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
196
+ "select",
197
+ {
198
+ id: name,
199
+ className: inputClassName,
200
+ ...register(name, registerOptions),
201
+ children: [
202
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: placeholder || "Select an option" }),
203
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: option.value, children: option.label }, option.value))
204
+ ]
205
+ }
206
+ ),
207
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: errorMessage })
208
+ ] });
209
+ }
210
+ if (type === "file") {
211
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
212
+ label && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: name, children: [
213
+ label,
214
+ required && " *"
215
+ ] }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
217
+ "input",
218
+ {
219
+ id: name,
220
+ type: "file",
221
+ accept,
222
+ multiple,
223
+ className: inputClassName,
224
+ ...register(name, registerOptions)
225
+ }
226
+ ),
227
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: errorMessage })
228
+ ] });
229
+ }
230
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
231
+ label && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { htmlFor: name, children: [
232
+ label,
233
+ required && " *"
234
+ ] }),
235
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
236
+ "input",
237
+ {
238
+ id: name,
239
+ type,
240
+ placeholder,
241
+ className: inputClassName,
242
+ ...register(name, registerOptions)
243
+ }
244
+ ),
245
+ errorMessage && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: errorMessage })
246
+ ] });
247
+ }
248
+
249
+ // src/FormActions.tsx
250
+ var import_react2 = require("react");
251
+ var import_react_hook_form3 = require("react-hook-form");
252
+ var import_react_turnstile = require("@marsidev/react-turnstile");
253
+ var import_jsx_runtime3 = require("react/jsx-runtime");
254
+ function FormActions({
255
+ children,
256
+ className,
257
+ turnstileClassName
258
+ }) {
259
+ const { config } = useAiFeaturesForm();
260
+ const form = (0, import_react_hook_form3.useFormContext)();
261
+ const handleTurnstileSuccess = (0, import_react2.useCallback)((token) => {
262
+ form.setValue("cf-turnstile-response", token, { shouldValidate: true });
263
+ }, [form]);
264
+ const handleTurnstileError = (0, import_react2.useCallback)(() => {
265
+ form.setValue("cf-turnstile-response", "", { shouldValidate: true });
266
+ }, [form]);
267
+ const handleTurnstileExpire = (0, import_react2.useCallback)(() => {
268
+ form.setValue("cf-turnstile-response", "", { shouldValidate: true });
269
+ }, [form]);
270
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className, children: [
271
+ config?.turnstile_sitekey && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: turnstileClassName, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
272
+ import_react_turnstile.Turnstile,
273
+ {
274
+ siteKey: config.turnstile_sitekey,
275
+ onSuccess: handleTurnstileSuccess,
276
+ onError: handleTurnstileError,
277
+ onExpire: handleTurnstileExpire,
278
+ options: {
279
+ theme: "auto"
280
+ }
281
+ }
282
+ ) }),
283
+ children
284
+ ] });
285
+ }
286
+
287
+ // src/SubmitButton.tsx
288
+ var import_jsx_runtime4 = require("react/jsx-runtime");
289
+ function SubmitButton({
290
+ children,
291
+ loadingText = "Sending...",
292
+ className
293
+ }) {
294
+ const { formState } = useAiFeaturesForm();
295
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
296
+ "button",
297
+ {
298
+ type: "submit",
299
+ disabled: formState.isSubmitting,
300
+ className,
301
+ children: formState.isSubmitting ? loadingText : children
302
+ }
303
+ );
304
+ }
305
+
306
+ // src/FormStatus.tsx
307
+ var import_jsx_runtime5 = require("react/jsx-runtime");
308
+ function FormStatus({
309
+ successMessage = "Thank you! Your message has been sent.",
310
+ successClassName,
311
+ errorClassName
312
+ }) {
313
+ const { formState } = useAiFeaturesForm();
314
+ if (formState.isSuccess) {
315
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: successClassName, children: successMessage });
316
+ }
317
+ if (formState.isError && formState.error) {
318
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: errorClassName, children: formState.error.message });
319
+ }
320
+ return null;
321
+ }
322
+
323
+ // src/index.ts
324
+ var import_react_hook_form4 = require("react-hook-form");
325
+ // Annotate the CommonJS export names for ESM import in node:
326
+ 0 && (module.exports = {
327
+ AiFeaturesForm,
328
+ Controller,
329
+ FormActions,
330
+ FormField,
331
+ FormStatus,
332
+ SubmitButton,
333
+ useAiFeaturesForm,
334
+ useFormContext
335
+ });
336
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/AiFeaturesForm.tsx","../src/FormField.tsx","../src/FormActions.tsx","../src/SubmitButton.tsx","../src/FormStatus.tsx"],"sourcesContent":["// Components\nexport { AiFeaturesForm, useAiFeaturesForm } from './AiFeaturesForm'\nexport { FormField } from './FormField'\nexport { FormActions } from './FormActions'\nexport { SubmitButton } from './SubmitButton'\nexport { FormStatus } from './FormStatus'\n\n// Types\nexport type {\n FormConfig,\n AiFeaturesFormProps,\n FormFieldProps,\n SubmitButtonProps,\n FormState,\n} from './types'\n\n// Re-export react-hook-form utilities for advanced usage\nexport { useFormContext, Controller } from 'react-hook-form'\n","import { useEffect, useState, createContext, useContext } from 'react'\nimport { useForm, FormProvider } from 'react-hook-form'\nimport type { FormConfig, AiFeaturesFormProps, FormState } from './types'\n\nconst AIFEATURES_API_URL = 'https://aifeatures.dev'\n\ninterface FormContextValue {\n formState: FormState\n config: FormConfig | null\n}\n\nconst FormContext = createContext<FormContextValue | null>(null)\n\nexport function useAiFeaturesForm() {\n const context = useContext(FormContext)\n if (!context) {\n throw new Error('useAiFeaturesForm must be used within an AiFeaturesForm')\n }\n return context\n}\n\nexport function AiFeaturesForm({\n formId,\n onSuccess,\n onError,\n className,\n children,\n}: AiFeaturesFormProps) {\n const [config, setConfig] = useState<FormConfig | null>(null)\n const [configError, setConfigError] = useState<Error | null>(null)\n const [formState, setFormState] = useState<FormState>({\n isSubmitting: false,\n isSuccess: false,\n isError: false,\n error: null,\n })\n\n const form = useForm()\n\n // Fetch form config on mount\n useEffect(() => {\n async function fetchConfig() {\n try {\n const response = await fetch(`${AIFEATURES_API_URL}/f/${formId}/config`)\n if (!response.ok) {\n throw new Error(`Failed to load form: ${response.status}`)\n }\n const data = await response.json()\n setConfig(data)\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load form config')\n setConfigError(error)\n onError?.(error)\n }\n }\n fetchConfig()\n }, [formId, onError])\n\n const onSubmit = async (data: Record<string, unknown>) => {\n if (!config) return\n\n setFormState({\n isSubmitting: true,\n isSuccess: false,\n isError: false,\n error: null,\n })\n\n try {\n const formData = new FormData()\n\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof FileList) {\n for (const file of Array.from(value)) {\n formData.append(key, file)\n }\n } else if (value !== undefined && value !== null) {\n formData.append(key, String(value))\n }\n }\n\n const response = await fetch(config.endpoint_url, {\n method: 'POST',\n body: formData,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new Error(errorText || `Submission failed: ${response.status}`)\n }\n\n setFormState({\n isSubmitting: false,\n isSuccess: true,\n isError: false,\n error: null,\n })\n\n form.reset()\n // Reset Turnstile widget\n if (typeof window !== 'undefined' && (window as unknown as { turnstile?: { reset: () => void } }).turnstile) {\n (window as unknown as { turnstile: { reset: () => void } }).turnstile.reset()\n }\n\n onSuccess?.(data)\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Form submission failed')\n setFormState({\n isSubmitting: false,\n isSuccess: false,\n isError: true,\n error,\n })\n onError?.(error)\n }\n }\n\n // Show loading state while fetching config\n if (!config && !configError) {\n return <div className={className}>Loading form...</div>\n }\n\n // Show error if config fetch failed\n if (configError) {\n return (\n <div className={className}>\n <p>Failed to load form. Please try again later.</p>\n </div>\n )\n }\n\n return (\n <FormContext.Provider value={{ formState, config }}>\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className={className}>\n {children}\n\n {/* Hidden field for Turnstile token - registered for validation */}\n {/* The actual Turnstile widget is rendered by FormActions */}\n {config?.turnstile_sitekey && (\n <input\n type=\"hidden\"\n {...form.register('cf-turnstile-response', {\n required: 'Please complete the CAPTCHA verification',\n })}\n />\n )}\n </form>\n </FormProvider>\n </FormContext.Provider>\n )\n}\n","import { useFormContext } from 'react-hook-form'\nimport type { FormFieldProps } from './types'\n\nexport function FormField({\n name,\n label,\n type = 'text',\n placeholder,\n required = false,\n className,\n options,\n accept,\n multiple,\n}: FormFieldProps) {\n const { register, formState: { errors } } = useFormContext()\n\n const error = errors[name]\n const errorMessage = error?.message as string | undefined\n\n const inputClassName = className || ''\n\n const registerOptions = {\n required: required ? `${label || name} is required` : false,\n }\n\n // Textarea\n if (type === 'textarea') {\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <textarea\n id={name}\n placeholder={placeholder}\n className={inputClassName}\n {...register(name, registerOptions)}\n />\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n }\n\n // Select\n if (type === 'select' && options) {\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <select\n id={name}\n className={inputClassName}\n {...register(name, registerOptions)}\n >\n <option value=\"\">{placeholder || 'Select an option'}</option>\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n }\n\n // File input\n if (type === 'file') {\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <input\n id={name}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n className={inputClassName}\n {...register(name, registerOptions)}\n />\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n }\n\n // Standard input (text, email, tel, url, number)\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <input\n id={name}\n type={type}\n placeholder={placeholder}\n className={inputClassName}\n {...register(name, registerOptions)}\n />\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n}\n","import { useCallback } from 'react'\nimport { useFormContext } from 'react-hook-form'\nimport { Turnstile } from '@marsidev/react-turnstile'\nimport { useAiFeaturesForm } from './AiFeaturesForm'\n\ninterface FormActionsProps {\n /** Children (typically SubmitButton) */\n children: React.ReactNode\n /** Custom class name for the container */\n className?: string\n /** Custom class name for the Turnstile container */\n turnstileClassName?: string\n}\n\nexport function FormActions({\n children,\n className,\n turnstileClassName,\n}: FormActionsProps) {\n const { config } = useAiFeaturesForm()\n const form = useFormContext()\n\n const handleTurnstileSuccess = useCallback((token: string) => {\n form.setValue('cf-turnstile-response', token, { shouldValidate: true })\n }, [form])\n\n const handleTurnstileError = useCallback(() => {\n form.setValue('cf-turnstile-response', '', { shouldValidate: true })\n }, [form])\n\n const handleTurnstileExpire = useCallback(() => {\n form.setValue('cf-turnstile-response', '', { shouldValidate: true })\n }, [form])\n\n return (\n <div className={className}>\n {/* Turnstile CAPTCHA - rendered above submit button */}\n {config?.turnstile_sitekey && (\n <div className={turnstileClassName}>\n <Turnstile\n siteKey={config.turnstile_sitekey}\n onSuccess={handleTurnstileSuccess}\n onError={handleTurnstileError}\n onExpire={handleTurnstileExpire}\n options={{\n theme: 'auto',\n }}\n />\n </div>\n )}\n\n {/* Submit button and any other actions */}\n {children}\n </div>\n )\n}\n","import { useAiFeaturesForm } from './AiFeaturesForm'\nimport type { SubmitButtonProps } from './types'\n\nexport function SubmitButton({\n children,\n loadingText = 'Sending...',\n className,\n}: SubmitButtonProps) {\n const { formState } = useAiFeaturesForm()\n\n return (\n <button\n type=\"submit\"\n disabled={formState.isSubmitting}\n className={className}\n >\n {formState.isSubmitting ? loadingText : children}\n </button>\n )\n}\n","import { useAiFeaturesForm } from './AiFeaturesForm'\n\ninterface FormStatusProps {\n /** Message to show on success */\n successMessage?: string\n /** Custom class name for success state */\n successClassName?: string\n /** Custom class name for error state */\n errorClassName?: string\n}\n\nexport function FormStatus({\n successMessage = 'Thank you! Your message has been sent.',\n successClassName,\n errorClassName,\n}: FormStatusProps) {\n const { formState } = useAiFeaturesForm()\n\n if (formState.isSuccess) {\n return (\n <div className={successClassName}>\n {successMessage}\n </div>\n )\n }\n\n if (formState.isError && formState.error) {\n return (\n <div className={errorClassName}>\n {formState.error.message}\n </div>\n )\n }\n\n return null\n}\n"],"mappings":"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","names":["import_react_hook_form","import_jsx_runtime","import_react","import_react_hook_form","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react_hook_form"]}
@@ -0,0 +1,91 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ export { Controller, useFormContext } from 'react-hook-form';
3
+
4
+ interface FormConfig {
5
+ id: string;
6
+ name: string;
7
+ endpoint_url: string;
8
+ turnstile_sitekey: string | null;
9
+ }
10
+ interface AiFeaturesFormProps {
11
+ /** Form ID from aifeatures.dev */
12
+ formId: string;
13
+ /** Called on successful submission */
14
+ onSuccess?: (data: Record<string, unknown>) => void;
15
+ /** Called on submission error */
16
+ onError?: (error: Error) => void;
17
+ /** Custom class name for the form element */
18
+ className?: string;
19
+ /** Children (FormField, SubmitButton, etc.) */
20
+ children: React.ReactNode;
21
+ }
22
+ interface FormFieldProps {
23
+ /** Field name - used as the form data key */
24
+ name: string;
25
+ /** Field label */
26
+ label?: string;
27
+ /** Field type */
28
+ type?: 'text' | 'email' | 'tel' | 'url' | 'number' | 'textarea' | 'select' | 'file';
29
+ /** Placeholder text */
30
+ placeholder?: string;
31
+ /** Whether the field is required */
32
+ required?: boolean;
33
+ /** Custom class name */
34
+ className?: string;
35
+ /** Options for select fields */
36
+ options?: {
37
+ value: string;
38
+ label: string;
39
+ }[];
40
+ /** Accept attribute for file inputs */
41
+ accept?: string;
42
+ /** Multiple files allowed */
43
+ multiple?: boolean;
44
+ }
45
+ interface SubmitButtonProps {
46
+ /** Button text (or children) */
47
+ children: React.ReactNode;
48
+ /** Text to show while submitting */
49
+ loadingText?: string;
50
+ /** Custom class name */
51
+ className?: string;
52
+ }
53
+ interface FormState {
54
+ isSubmitting: boolean;
55
+ isSuccess: boolean;
56
+ isError: boolean;
57
+ error: Error | null;
58
+ }
59
+
60
+ interface FormContextValue {
61
+ formState: FormState;
62
+ config: FormConfig | null;
63
+ }
64
+ declare function useAiFeaturesForm(): FormContextValue;
65
+ declare function AiFeaturesForm({ formId, onSuccess, onError, className, children, }: AiFeaturesFormProps): react_jsx_runtime.JSX.Element;
66
+
67
+ declare function FormField({ name, label, type, placeholder, required, className, options, accept, multiple, }: FormFieldProps): react_jsx_runtime.JSX.Element;
68
+
69
+ interface FormActionsProps {
70
+ /** Children (typically SubmitButton) */
71
+ children: React.ReactNode;
72
+ /** Custom class name for the container */
73
+ className?: string;
74
+ /** Custom class name for the Turnstile container */
75
+ turnstileClassName?: string;
76
+ }
77
+ declare function FormActions({ children, className, turnstileClassName, }: FormActionsProps): react_jsx_runtime.JSX.Element;
78
+
79
+ declare function SubmitButton({ children, loadingText, className, }: SubmitButtonProps): react_jsx_runtime.JSX.Element;
80
+
81
+ interface FormStatusProps {
82
+ /** Message to show on success */
83
+ successMessage?: string;
84
+ /** Custom class name for success state */
85
+ successClassName?: string;
86
+ /** Custom class name for error state */
87
+ errorClassName?: string;
88
+ }
89
+ declare function FormStatus({ successMessage, successClassName, errorClassName, }: FormStatusProps): react_jsx_runtime.JSX.Element | null;
90
+
91
+ export { AiFeaturesForm, type AiFeaturesFormProps, FormActions, type FormConfig, FormField, type FormFieldProps, type FormState, FormStatus, SubmitButton, type SubmitButtonProps, useAiFeaturesForm };
@@ -0,0 +1,91 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ export { Controller, useFormContext } from 'react-hook-form';
3
+
4
+ interface FormConfig {
5
+ id: string;
6
+ name: string;
7
+ endpoint_url: string;
8
+ turnstile_sitekey: string | null;
9
+ }
10
+ interface AiFeaturesFormProps {
11
+ /** Form ID from aifeatures.dev */
12
+ formId: string;
13
+ /** Called on successful submission */
14
+ onSuccess?: (data: Record<string, unknown>) => void;
15
+ /** Called on submission error */
16
+ onError?: (error: Error) => void;
17
+ /** Custom class name for the form element */
18
+ className?: string;
19
+ /** Children (FormField, SubmitButton, etc.) */
20
+ children: React.ReactNode;
21
+ }
22
+ interface FormFieldProps {
23
+ /** Field name - used as the form data key */
24
+ name: string;
25
+ /** Field label */
26
+ label?: string;
27
+ /** Field type */
28
+ type?: 'text' | 'email' | 'tel' | 'url' | 'number' | 'textarea' | 'select' | 'file';
29
+ /** Placeholder text */
30
+ placeholder?: string;
31
+ /** Whether the field is required */
32
+ required?: boolean;
33
+ /** Custom class name */
34
+ className?: string;
35
+ /** Options for select fields */
36
+ options?: {
37
+ value: string;
38
+ label: string;
39
+ }[];
40
+ /** Accept attribute for file inputs */
41
+ accept?: string;
42
+ /** Multiple files allowed */
43
+ multiple?: boolean;
44
+ }
45
+ interface SubmitButtonProps {
46
+ /** Button text (or children) */
47
+ children: React.ReactNode;
48
+ /** Text to show while submitting */
49
+ loadingText?: string;
50
+ /** Custom class name */
51
+ className?: string;
52
+ }
53
+ interface FormState {
54
+ isSubmitting: boolean;
55
+ isSuccess: boolean;
56
+ isError: boolean;
57
+ error: Error | null;
58
+ }
59
+
60
+ interface FormContextValue {
61
+ formState: FormState;
62
+ config: FormConfig | null;
63
+ }
64
+ declare function useAiFeaturesForm(): FormContextValue;
65
+ declare function AiFeaturesForm({ formId, onSuccess, onError, className, children, }: AiFeaturesFormProps): react_jsx_runtime.JSX.Element;
66
+
67
+ declare function FormField({ name, label, type, placeholder, required, className, options, accept, multiple, }: FormFieldProps): react_jsx_runtime.JSX.Element;
68
+
69
+ interface FormActionsProps {
70
+ /** Children (typically SubmitButton) */
71
+ children: React.ReactNode;
72
+ /** Custom class name for the container */
73
+ className?: string;
74
+ /** Custom class name for the Turnstile container */
75
+ turnstileClassName?: string;
76
+ }
77
+ declare function FormActions({ children, className, turnstileClassName, }: FormActionsProps): react_jsx_runtime.JSX.Element;
78
+
79
+ declare function SubmitButton({ children, loadingText, className, }: SubmitButtonProps): react_jsx_runtime.JSX.Element;
80
+
81
+ interface FormStatusProps {
82
+ /** Message to show on success */
83
+ successMessage?: string;
84
+ /** Custom class name for success state */
85
+ successClassName?: string;
86
+ /** Custom class name for error state */
87
+ errorClassName?: string;
88
+ }
89
+ declare function FormStatus({ successMessage, successClassName, errorClassName, }: FormStatusProps): react_jsx_runtime.JSX.Element | null;
90
+
91
+ export { AiFeaturesForm, type AiFeaturesFormProps, FormActions, type FormConfig, FormField, type FormFieldProps, type FormState, FormStatus, SubmitButton, type SubmitButtonProps, useAiFeaturesForm };
package/dist/index.js ADDED
@@ -0,0 +1,303 @@
1
+ "use client";
2
+
3
+ // src/AiFeaturesForm.tsx
4
+ import { useEffect, useState, createContext, useContext } from "react";
5
+ import { useForm, FormProvider } from "react-hook-form";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var AIFEATURES_API_URL = "https://aifeatures.dev";
8
+ var FormContext = createContext(null);
9
+ function useAiFeaturesForm() {
10
+ const context = useContext(FormContext);
11
+ if (!context) {
12
+ throw new Error("useAiFeaturesForm must be used within an AiFeaturesForm");
13
+ }
14
+ return context;
15
+ }
16
+ function AiFeaturesForm({
17
+ formId,
18
+ onSuccess,
19
+ onError,
20
+ className,
21
+ children
22
+ }) {
23
+ const [config, setConfig] = useState(null);
24
+ const [configError, setConfigError] = useState(null);
25
+ const [formState, setFormState] = useState({
26
+ isSubmitting: false,
27
+ isSuccess: false,
28
+ isError: false,
29
+ error: null
30
+ });
31
+ const form = useForm();
32
+ useEffect(() => {
33
+ async function fetchConfig() {
34
+ try {
35
+ const response = await fetch(`${AIFEATURES_API_URL}/f/${formId}/config`);
36
+ if (!response.ok) {
37
+ throw new Error(`Failed to load form: ${response.status}`);
38
+ }
39
+ const data = await response.json();
40
+ setConfig(data);
41
+ } catch (err) {
42
+ const error = err instanceof Error ? err : new Error("Failed to load form config");
43
+ setConfigError(error);
44
+ onError?.(error);
45
+ }
46
+ }
47
+ fetchConfig();
48
+ }, [formId, onError]);
49
+ const onSubmit = async (data) => {
50
+ if (!config) return;
51
+ setFormState({
52
+ isSubmitting: true,
53
+ isSuccess: false,
54
+ isError: false,
55
+ error: null
56
+ });
57
+ try {
58
+ const formData = new FormData();
59
+ for (const [key, value] of Object.entries(data)) {
60
+ if (value instanceof FileList) {
61
+ for (const file of Array.from(value)) {
62
+ formData.append(key, file);
63
+ }
64
+ } else if (value !== void 0 && value !== null) {
65
+ formData.append(key, String(value));
66
+ }
67
+ }
68
+ const response = await fetch(config.endpoint_url, {
69
+ method: "POST",
70
+ body: formData
71
+ });
72
+ if (!response.ok) {
73
+ const errorText = await response.text();
74
+ throw new Error(errorText || `Submission failed: ${response.status}`);
75
+ }
76
+ setFormState({
77
+ isSubmitting: false,
78
+ isSuccess: true,
79
+ isError: false,
80
+ error: null
81
+ });
82
+ form.reset();
83
+ if (typeof window !== "undefined" && window.turnstile) {
84
+ window.turnstile.reset();
85
+ }
86
+ onSuccess?.(data);
87
+ } catch (err) {
88
+ const error = err instanceof Error ? err : new Error("Form submission failed");
89
+ setFormState({
90
+ isSubmitting: false,
91
+ isSuccess: false,
92
+ isError: true,
93
+ error
94
+ });
95
+ onError?.(error);
96
+ }
97
+ };
98
+ if (!config && !configError) {
99
+ return /* @__PURE__ */ jsx("div", { className, children: "Loading form..." });
100
+ }
101
+ if (configError) {
102
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsx("p", { children: "Failed to load form. Please try again later." }) });
103
+ }
104
+ return /* @__PURE__ */ jsx(FormContext.Provider, { value: { formState, config }, children: /* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className, children: [
105
+ children,
106
+ config?.turnstile_sitekey && /* @__PURE__ */ jsx(
107
+ "input",
108
+ {
109
+ type: "hidden",
110
+ ...form.register("cf-turnstile-response", {
111
+ required: "Please complete the CAPTCHA verification"
112
+ })
113
+ }
114
+ )
115
+ ] }) }) });
116
+ }
117
+
118
+ // src/FormField.tsx
119
+ import { useFormContext } from "react-hook-form";
120
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
121
+ function FormField({
122
+ name,
123
+ label,
124
+ type = "text",
125
+ placeholder,
126
+ required = false,
127
+ className,
128
+ options,
129
+ accept,
130
+ multiple
131
+ }) {
132
+ const { register, formState: { errors } } = useFormContext();
133
+ const error = errors[name];
134
+ const errorMessage = error?.message;
135
+ const inputClassName = className || "";
136
+ const registerOptions = {
137
+ required: required ? `${label || name} is required` : false
138
+ };
139
+ if (type === "textarea") {
140
+ return /* @__PURE__ */ jsxs2("div", { children: [
141
+ label && /* @__PURE__ */ jsxs2("label", { htmlFor: name, children: [
142
+ label,
143
+ required && " *"
144
+ ] }),
145
+ /* @__PURE__ */ jsx2(
146
+ "textarea",
147
+ {
148
+ id: name,
149
+ placeholder,
150
+ className: inputClassName,
151
+ ...register(name, registerOptions)
152
+ }
153
+ ),
154
+ errorMessage && /* @__PURE__ */ jsx2("span", { children: errorMessage })
155
+ ] });
156
+ }
157
+ if (type === "select" && options) {
158
+ return /* @__PURE__ */ jsxs2("div", { children: [
159
+ label && /* @__PURE__ */ jsxs2("label", { htmlFor: name, children: [
160
+ label,
161
+ required && " *"
162
+ ] }),
163
+ /* @__PURE__ */ jsxs2(
164
+ "select",
165
+ {
166
+ id: name,
167
+ className: inputClassName,
168
+ ...register(name, registerOptions),
169
+ children: [
170
+ /* @__PURE__ */ jsx2("option", { value: "", children: placeholder || "Select an option" }),
171
+ options.map((option) => /* @__PURE__ */ jsx2("option", { value: option.value, children: option.label }, option.value))
172
+ ]
173
+ }
174
+ ),
175
+ errorMessage && /* @__PURE__ */ jsx2("span", { children: errorMessage })
176
+ ] });
177
+ }
178
+ if (type === "file") {
179
+ return /* @__PURE__ */ jsxs2("div", { children: [
180
+ label && /* @__PURE__ */ jsxs2("label", { htmlFor: name, children: [
181
+ label,
182
+ required && " *"
183
+ ] }),
184
+ /* @__PURE__ */ jsx2(
185
+ "input",
186
+ {
187
+ id: name,
188
+ type: "file",
189
+ accept,
190
+ multiple,
191
+ className: inputClassName,
192
+ ...register(name, registerOptions)
193
+ }
194
+ ),
195
+ errorMessage && /* @__PURE__ */ jsx2("span", { children: errorMessage })
196
+ ] });
197
+ }
198
+ return /* @__PURE__ */ jsxs2("div", { children: [
199
+ label && /* @__PURE__ */ jsxs2("label", { htmlFor: name, children: [
200
+ label,
201
+ required && " *"
202
+ ] }),
203
+ /* @__PURE__ */ jsx2(
204
+ "input",
205
+ {
206
+ id: name,
207
+ type,
208
+ placeholder,
209
+ className: inputClassName,
210
+ ...register(name, registerOptions)
211
+ }
212
+ ),
213
+ errorMessage && /* @__PURE__ */ jsx2("span", { children: errorMessage })
214
+ ] });
215
+ }
216
+
217
+ // src/FormActions.tsx
218
+ import { useCallback } from "react";
219
+ import { useFormContext as useFormContext2 } from "react-hook-form";
220
+ import { Turnstile } from "@marsidev/react-turnstile";
221
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
222
+ function FormActions({
223
+ children,
224
+ className,
225
+ turnstileClassName
226
+ }) {
227
+ const { config } = useAiFeaturesForm();
228
+ const form = useFormContext2();
229
+ const handleTurnstileSuccess = useCallback((token) => {
230
+ form.setValue("cf-turnstile-response", token, { shouldValidate: true });
231
+ }, [form]);
232
+ const handleTurnstileError = useCallback(() => {
233
+ form.setValue("cf-turnstile-response", "", { shouldValidate: true });
234
+ }, [form]);
235
+ const handleTurnstileExpire = useCallback(() => {
236
+ form.setValue("cf-turnstile-response", "", { shouldValidate: true });
237
+ }, [form]);
238
+ return /* @__PURE__ */ jsxs3("div", { className, children: [
239
+ config?.turnstile_sitekey && /* @__PURE__ */ jsx3("div", { className: turnstileClassName, children: /* @__PURE__ */ jsx3(
240
+ Turnstile,
241
+ {
242
+ siteKey: config.turnstile_sitekey,
243
+ onSuccess: handleTurnstileSuccess,
244
+ onError: handleTurnstileError,
245
+ onExpire: handleTurnstileExpire,
246
+ options: {
247
+ theme: "auto"
248
+ }
249
+ }
250
+ ) }),
251
+ children
252
+ ] });
253
+ }
254
+
255
+ // src/SubmitButton.tsx
256
+ import { jsx as jsx4 } from "react/jsx-runtime";
257
+ function SubmitButton({
258
+ children,
259
+ loadingText = "Sending...",
260
+ className
261
+ }) {
262
+ const { formState } = useAiFeaturesForm();
263
+ return /* @__PURE__ */ jsx4(
264
+ "button",
265
+ {
266
+ type: "submit",
267
+ disabled: formState.isSubmitting,
268
+ className,
269
+ children: formState.isSubmitting ? loadingText : children
270
+ }
271
+ );
272
+ }
273
+
274
+ // src/FormStatus.tsx
275
+ import { jsx as jsx5 } from "react/jsx-runtime";
276
+ function FormStatus({
277
+ successMessage = "Thank you! Your message has been sent.",
278
+ successClassName,
279
+ errorClassName
280
+ }) {
281
+ const { formState } = useAiFeaturesForm();
282
+ if (formState.isSuccess) {
283
+ return /* @__PURE__ */ jsx5("div", { className: successClassName, children: successMessage });
284
+ }
285
+ if (formState.isError && formState.error) {
286
+ return /* @__PURE__ */ jsx5("div", { className: errorClassName, children: formState.error.message });
287
+ }
288
+ return null;
289
+ }
290
+
291
+ // src/index.ts
292
+ import { useFormContext as useFormContext3, Controller } from "react-hook-form";
293
+ export {
294
+ AiFeaturesForm,
295
+ Controller,
296
+ FormActions,
297
+ FormField,
298
+ FormStatus,
299
+ SubmitButton,
300
+ useAiFeaturesForm,
301
+ useFormContext3 as useFormContext
302
+ };
303
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/AiFeaturesForm.tsx","../src/FormField.tsx","../src/FormActions.tsx","../src/SubmitButton.tsx","../src/FormStatus.tsx","../src/index.ts"],"sourcesContent":["import { useEffect, useState, createContext, useContext } from 'react'\nimport { useForm, FormProvider } from 'react-hook-form'\nimport type { FormConfig, AiFeaturesFormProps, FormState } from './types'\n\nconst AIFEATURES_API_URL = 'https://aifeatures.dev'\n\ninterface FormContextValue {\n formState: FormState\n config: FormConfig | null\n}\n\nconst FormContext = createContext<FormContextValue | null>(null)\n\nexport function useAiFeaturesForm() {\n const context = useContext(FormContext)\n if (!context) {\n throw new Error('useAiFeaturesForm must be used within an AiFeaturesForm')\n }\n return context\n}\n\nexport function AiFeaturesForm({\n formId,\n onSuccess,\n onError,\n className,\n children,\n}: AiFeaturesFormProps) {\n const [config, setConfig] = useState<FormConfig | null>(null)\n const [configError, setConfigError] = useState<Error | null>(null)\n const [formState, setFormState] = useState<FormState>({\n isSubmitting: false,\n isSuccess: false,\n isError: false,\n error: null,\n })\n\n const form = useForm()\n\n // Fetch form config on mount\n useEffect(() => {\n async function fetchConfig() {\n try {\n const response = await fetch(`${AIFEATURES_API_URL}/f/${formId}/config`)\n if (!response.ok) {\n throw new Error(`Failed to load form: ${response.status}`)\n }\n const data = await response.json()\n setConfig(data)\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to load form config')\n setConfigError(error)\n onError?.(error)\n }\n }\n fetchConfig()\n }, [formId, onError])\n\n const onSubmit = async (data: Record<string, unknown>) => {\n if (!config) return\n\n setFormState({\n isSubmitting: true,\n isSuccess: false,\n isError: false,\n error: null,\n })\n\n try {\n const formData = new FormData()\n\n for (const [key, value] of Object.entries(data)) {\n if (value instanceof FileList) {\n for (const file of Array.from(value)) {\n formData.append(key, file)\n }\n } else if (value !== undefined && value !== null) {\n formData.append(key, String(value))\n }\n }\n\n const response = await fetch(config.endpoint_url, {\n method: 'POST',\n body: formData,\n })\n\n if (!response.ok) {\n const errorText = await response.text()\n throw new Error(errorText || `Submission failed: ${response.status}`)\n }\n\n setFormState({\n isSubmitting: false,\n isSuccess: true,\n isError: false,\n error: null,\n })\n\n form.reset()\n // Reset Turnstile widget\n if (typeof window !== 'undefined' && (window as unknown as { turnstile?: { reset: () => void } }).turnstile) {\n (window as unknown as { turnstile: { reset: () => void } }).turnstile.reset()\n }\n\n onSuccess?.(data)\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Form submission failed')\n setFormState({\n isSubmitting: false,\n isSuccess: false,\n isError: true,\n error,\n })\n onError?.(error)\n }\n }\n\n // Show loading state while fetching config\n if (!config && !configError) {\n return <div className={className}>Loading form...</div>\n }\n\n // Show error if config fetch failed\n if (configError) {\n return (\n <div className={className}>\n <p>Failed to load form. Please try again later.</p>\n </div>\n )\n }\n\n return (\n <FormContext.Provider value={{ formState, config }}>\n <FormProvider {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className={className}>\n {children}\n\n {/* Hidden field for Turnstile token - registered for validation */}\n {/* The actual Turnstile widget is rendered by FormActions */}\n {config?.turnstile_sitekey && (\n <input\n type=\"hidden\"\n {...form.register('cf-turnstile-response', {\n required: 'Please complete the CAPTCHA verification',\n })}\n />\n )}\n </form>\n </FormProvider>\n </FormContext.Provider>\n )\n}\n","import { useFormContext } from 'react-hook-form'\nimport type { FormFieldProps } from './types'\n\nexport function FormField({\n name,\n label,\n type = 'text',\n placeholder,\n required = false,\n className,\n options,\n accept,\n multiple,\n}: FormFieldProps) {\n const { register, formState: { errors } } = useFormContext()\n\n const error = errors[name]\n const errorMessage = error?.message as string | undefined\n\n const inputClassName = className || ''\n\n const registerOptions = {\n required: required ? `${label || name} is required` : false,\n }\n\n // Textarea\n if (type === 'textarea') {\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <textarea\n id={name}\n placeholder={placeholder}\n className={inputClassName}\n {...register(name, registerOptions)}\n />\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n }\n\n // Select\n if (type === 'select' && options) {\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <select\n id={name}\n className={inputClassName}\n {...register(name, registerOptions)}\n >\n <option value=\"\">{placeholder || 'Select an option'}</option>\n {options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n }\n\n // File input\n if (type === 'file') {\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <input\n id={name}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n className={inputClassName}\n {...register(name, registerOptions)}\n />\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n }\n\n // Standard input (text, email, tel, url, number)\n return (\n <div>\n {label && <label htmlFor={name}>{label}{required && ' *'}</label>}\n <input\n id={name}\n type={type}\n placeholder={placeholder}\n className={inputClassName}\n {...register(name, registerOptions)}\n />\n {errorMessage && <span>{errorMessage}</span>}\n </div>\n )\n}\n","import { useCallback } from 'react'\nimport { useFormContext } from 'react-hook-form'\nimport { Turnstile } from '@marsidev/react-turnstile'\nimport { useAiFeaturesForm } from './AiFeaturesForm'\n\ninterface FormActionsProps {\n /** Children (typically SubmitButton) */\n children: React.ReactNode\n /** Custom class name for the container */\n className?: string\n /** Custom class name for the Turnstile container */\n turnstileClassName?: string\n}\n\nexport function FormActions({\n children,\n className,\n turnstileClassName,\n}: FormActionsProps) {\n const { config } = useAiFeaturesForm()\n const form = useFormContext()\n\n const handleTurnstileSuccess = useCallback((token: string) => {\n form.setValue('cf-turnstile-response', token, { shouldValidate: true })\n }, [form])\n\n const handleTurnstileError = useCallback(() => {\n form.setValue('cf-turnstile-response', '', { shouldValidate: true })\n }, [form])\n\n const handleTurnstileExpire = useCallback(() => {\n form.setValue('cf-turnstile-response', '', { shouldValidate: true })\n }, [form])\n\n return (\n <div className={className}>\n {/* Turnstile CAPTCHA - rendered above submit button */}\n {config?.turnstile_sitekey && (\n <div className={turnstileClassName}>\n <Turnstile\n siteKey={config.turnstile_sitekey}\n onSuccess={handleTurnstileSuccess}\n onError={handleTurnstileError}\n onExpire={handleTurnstileExpire}\n options={{\n theme: 'auto',\n }}\n />\n </div>\n )}\n\n {/* Submit button and any other actions */}\n {children}\n </div>\n )\n}\n","import { useAiFeaturesForm } from './AiFeaturesForm'\nimport type { SubmitButtonProps } from './types'\n\nexport function SubmitButton({\n children,\n loadingText = 'Sending...',\n className,\n}: SubmitButtonProps) {\n const { formState } = useAiFeaturesForm()\n\n return (\n <button\n type=\"submit\"\n disabled={formState.isSubmitting}\n className={className}\n >\n {formState.isSubmitting ? loadingText : children}\n </button>\n )\n}\n","import { useAiFeaturesForm } from './AiFeaturesForm'\n\ninterface FormStatusProps {\n /** Message to show on success */\n successMessage?: string\n /** Custom class name for success state */\n successClassName?: string\n /** Custom class name for error state */\n errorClassName?: string\n}\n\nexport function FormStatus({\n successMessage = 'Thank you! Your message has been sent.',\n successClassName,\n errorClassName,\n}: FormStatusProps) {\n const { formState } = useAiFeaturesForm()\n\n if (formState.isSuccess) {\n return (\n <div className={successClassName}>\n {successMessage}\n </div>\n )\n }\n\n if (formState.isError && formState.error) {\n return (\n <div className={errorClassName}>\n {formState.error.message}\n </div>\n )\n }\n\n return null\n}\n","// Components\nexport { AiFeaturesForm, useAiFeaturesForm } from './AiFeaturesForm'\nexport { FormField } from './FormField'\nexport { FormActions } from './FormActions'\nexport { SubmitButton } from './SubmitButton'\nexport { FormStatus } from './FormStatus'\n\n// Types\nexport type {\n FormConfig,\n AiFeaturesFormProps,\n FormFieldProps,\n SubmitButtonProps,\n FormState,\n} from './types'\n\n// Re-export react-hook-form utilities for advanced usage\nexport { useFormContext, Controller } from 'react-hook-form'\n"],"mappings":";;;AAAA,SAAS,WAAW,UAAU,eAAe,kBAAkB;AAC/D,SAAS,SAAS,oBAAoB;AAsH3B,cAeH,YAfG;AAnHX,IAAM,qBAAqB;AAO3B,IAAM,cAAc,cAAuC,IAAI;AAExD,SAAS,oBAAoB;AAClC,QAAM,UAAU,WAAW,WAAW;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO;AACT;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA4B,IAAI;AAC5D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAuB,IAAI;AACjE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAoB;AAAA,IACpD,cAAc;AAAA,IACd,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAED,QAAM,OAAO,QAAQ;AAGrB,YAAU,MAAM;AACd,mBAAe,cAAc;AAC3B,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,GAAG,kBAAkB,MAAM,MAAM,SAAS;AACvE,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,EAAE;AAAA,QAC3D;AACA,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,kBAAU,IAAI;AAAA,MAChB,SAAS,KAAK;AACZ,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,4BAA4B;AACjF,uBAAe,KAAK;AACpB,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF;AACA,gBAAY;AAAA,EACd,GAAG,CAAC,QAAQ,OAAO,CAAC;AAEpB,QAAM,WAAW,OAAO,SAAkC;AACxD,QAAI,CAAC,OAAQ;AAEb,iBAAa;AAAA,MACX,cAAc;AAAA,MACd,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AAED,QAAI;AACF,YAAM,WAAW,IAAI,SAAS;AAE9B,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,YAAI,iBAAiB,UAAU;AAC7B,qBAAW,QAAQ,MAAM,KAAK,KAAK,GAAG;AACpC,qBAAS,OAAO,KAAK,IAAI;AAAA,UAC3B;AAAA,QACF,WAAW,UAAU,UAAa,UAAU,MAAM;AAChD,mBAAS,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,QACpC;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,MAAM,OAAO,cAAc;AAAA,QAChD,QAAQ;AAAA,QACR,MAAM;AAAA,MACR,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,cAAM,IAAI,MAAM,aAAa,sBAAsB,SAAS,MAAM,EAAE;AAAA,MACtE;AAEA,mBAAa;AAAA,QACX,cAAc;AAAA,QACd,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAED,WAAK,MAAM;AAEX,UAAI,OAAO,WAAW,eAAgB,OAA4D,WAAW;AAC3G,QAAC,OAA2D,UAAU,MAAM;AAAA,MAC9E;AAEA,kBAAY,IAAI;AAAA,IAClB,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,wBAAwB;AAC7E,mBAAa;AAAA,QACX,cAAc;AAAA,QACd,WAAW;AAAA,QACX,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AACD,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF;AAGA,MAAI,CAAC,UAAU,CAAC,aAAa;AAC3B,WAAO,oBAAC,SAAI,WAAsB,6BAAe;AAAA,EACnD;AAGA,MAAI,aAAa;AACf,WACE,oBAAC,SAAI,WACH,8BAAC,OAAE,0DAA4C,GACjD;AAAA,EAEJ;AAEA,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAO,EAAE,WAAW,OAAO,GAC/C,8BAAC,gBAAc,GAAG,MAChB,+BAAC,UAAK,UAAU,KAAK,aAAa,QAAQ,GAAG,WAC1C;AAAA;AAAA,IAIA,QAAQ,qBACP;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACJ,GAAG,KAAK,SAAS,yBAAyB;AAAA,UACzC,UAAU;AAAA,QACZ,CAAC;AAAA;AAAA,IACH;AAAA,KAEJ,GACF,GACF;AAEJ;;;ACvJA,SAAS,sBAAsB;AA6Bb,SACV,OAAAA,MADU,QAAAC,aAAA;AA1BX,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,EAAE,UAAU,WAAW,EAAE,OAAO,EAAE,IAAI,eAAe;AAE3D,QAAM,QAAQ,OAAO,IAAI;AACzB,QAAM,eAAe,OAAO;AAE5B,QAAM,iBAAiB,aAAa;AAEpC,QAAM,kBAAkB;AAAA,IACtB,UAAU,WAAW,GAAG,SAAS,IAAI,iBAAiB;AAAA,EACxD;AAGA,MAAI,SAAS,YAAY;AACvB,WACE,gBAAAA,MAAC,SACE;AAAA,eAAS,gBAAAA,MAAC,WAAM,SAAS,MAAO;AAAA;AAAA,QAAO,YAAY;AAAA,SAAK;AAAA,MACzD,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ;AAAA,UACA,WAAW;AAAA,UACV,GAAG,SAAS,MAAM,eAAe;AAAA;AAAA,MACpC;AAAA,MACC,gBAAgB,gBAAAA,KAAC,UAAM,wBAAa;AAAA,OACvC;AAAA,EAEJ;AAGA,MAAI,SAAS,YAAY,SAAS;AAChC,WACE,gBAAAC,MAAC,SACE;AAAA,eAAS,gBAAAA,MAAC,WAAM,SAAS,MAAO;AAAA;AAAA,QAAO,YAAY;AAAA,SAAK;AAAA,MACzD,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAW;AAAA,UACV,GAAG,SAAS,MAAM,eAAe;AAAA,UAElC;AAAA,4BAAAD,KAAC,YAAO,OAAM,IAAI,yBAAe,oBAAmB;AAAA,YACnD,QAAQ,IAAI,CAAC,WACZ,gBAAAA,KAAC,YAA0B,OAAO,OAAO,OACtC,iBAAO,SADG,OAAO,KAEpB,CACD;AAAA;AAAA;AAAA,MACH;AAAA,MACC,gBAAgB,gBAAAA,KAAC,UAAM,wBAAa;AAAA,OACvC;AAAA,EAEJ;AAGA,MAAI,SAAS,QAAQ;AACnB,WACE,gBAAAC,MAAC,SACE;AAAA,eAAS,gBAAAA,MAAC,WAAM,SAAS,MAAO;AAAA;AAAA,QAAO,YAAY;AAAA,SAAK;AAAA,MACzD,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,MAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,WAAW;AAAA,UACV,GAAG,SAAS,MAAM,eAAe;AAAA;AAAA,MACpC;AAAA,MACC,gBAAgB,gBAAAA,KAAC,UAAM,wBAAa;AAAA,OACvC;AAAA,EAEJ;AAGA,SACE,gBAAAC,MAAC,SACE;AAAA,aAAS,gBAAAA,MAAC,WAAM,SAAS,MAAO;AAAA;AAAA,MAAO,YAAY;AAAA,OAAK;AAAA,IACzD,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACV,GAAG,SAAS,MAAM,eAAe;AAAA;AAAA,IACpC;AAAA,IACC,gBAAgB,gBAAAA,KAAC,UAAM,wBAAa;AAAA,KACvC;AAEJ;;;AC/FA,SAAS,mBAAmB;AAC5B,SAAS,kBAAAE,uBAAsB;AAC/B,SAAS,iBAAiB;AAiCtB,SAIM,OAAAC,MAJN,QAAAC,aAAA;AArBG,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,EAAE,OAAO,IAAI,kBAAkB;AACrC,QAAM,OAAOC,gBAAe;AAE5B,QAAM,yBAAyB,YAAY,CAAC,UAAkB;AAC5D,SAAK,SAAS,yBAAyB,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACxE,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,uBAAuB,YAAY,MAAM;AAC7C,SAAK,SAAS,yBAAyB,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACrE,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,wBAAwB,YAAY,MAAM;AAC9C,SAAK,SAAS,yBAAyB,IAAI,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACrE,GAAG,CAAC,IAAI,CAAC;AAET,SACE,gBAAAD,MAAC,SAAI,WAEF;AAAA,YAAQ,qBACP,gBAAAD,KAAC,SAAI,WAAW,oBACd,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,OAAO;AAAA,QAChB,WAAW;AAAA,QACX,SAAS;AAAA,QACT,UAAU;AAAA,QACV,SAAS;AAAA,UACP,OAAO;AAAA,QACT;AAAA;AAAA,IACF,GACF;AAAA,IAID;AAAA,KACH;AAEJ;;;AC5CI,gBAAAG,YAAA;AARG,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAAsB;AACpB,QAAM,EAAE,UAAU,IAAI,kBAAkB;AAExC,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU,UAAU;AAAA,MACpB;AAAA,MAEC,oBAAU,eAAe,cAAc;AAAA;AAAA,EAC1C;AAEJ;;;ACCM,gBAAAC,YAAA;AATC,SAAS,WAAW;AAAA,EACzB,iBAAiB;AAAA,EACjB;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,EAAE,UAAU,IAAI,kBAAkB;AAExC,MAAI,UAAU,WAAW;AACvB,WACE,gBAAAA,KAAC,SAAI,WAAW,kBACb,0BACH;AAAA,EAEJ;AAEA,MAAI,UAAU,WAAW,UAAU,OAAO;AACxC,WACE,gBAAAA,KAAC,SAAI,WAAW,gBACb,oBAAU,MAAM,SACnB;AAAA,EAEJ;AAEA,SAAO;AACT;;;AClBA,SAAS,kBAAAC,iBAAgB,kBAAkB;","names":["jsx","jsxs","useFormContext","jsx","jsxs","useFormContext","jsx","jsx","useFormContext"]}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@aifeatures/react",
3
+ "version": "0.0.1",
4
+ "description": "React components for aifeatures forms with built-in Turnstile CAPTCHA",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "peerDependencies": {
25
+ "react": ">=18",
26
+ "react-dom": ">=18"
27
+ },
28
+ "dependencies": {
29
+ "@marsidev/react-turnstile": "^1.1.0",
30
+ "react-hook-form": "^7.54.2"
31
+ },
32
+ "devDependencies": {
33
+ "@storybook/addon-essentials": "^8.4.2",
34
+ "@storybook/addon-interactions": "^8.4.2",
35
+ "@storybook/addon-links": "^8.4.2",
36
+ "@storybook/blocks": "^8.4.2",
37
+ "@storybook/react": "^8.4.2",
38
+ "@storybook/react-vite": "^8.4.2",
39
+ "@storybook/test": "^8.4.2",
40
+ "@types/react": "^18.3.12",
41
+ "@types/react-dom": "^18.3.1",
42
+ "msw": "^2.6.5",
43
+ "msw-storybook-addon": "^2.0.3",
44
+ "react": "^18.3.1",
45
+ "react-dom": "^18.3.1",
46
+ "storybook": "^8.4.2",
47
+ "tsup": "^8.3.5",
48
+ "typescript": "^5.3.3"
49
+ },
50
+ "msw": {
51
+ "workerDirectory": [
52
+ ".storybook/public"
53
+ ]
54
+ },
55
+ "scripts": {
56
+ "build": "tsup",
57
+ "dev": "tsup --watch",
58
+ "lint": "eslint src/",
59
+ "typecheck": "tsc --noEmit",
60
+ "storybook": "storybook dev -p 6007",
61
+ "build-storybook": "storybook build"
62
+ }
63
+ }