@page-speed/forms 0.5.9 → 0.6.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/{FormContext-089h0j0Q.d.ts → FormContext-DaRyEYnF.d.ts} +1 -1
- package/dist/{FormContext-iHzBALZa.d.cts → FormContext-KsjKSot3.d.cts} +1 -1
- package/dist/{chunk-TOPOO33Z.js → chunk-7ZX2EUOG.js} +27 -18
- package/dist/chunk-7ZX2EUOG.js.map +1 -0
- package/dist/{chunk-OF6ZYT7A.cjs → chunk-F5ZJCJOD.cjs} +27 -18
- package/dist/chunk-F5ZJCJOD.cjs.map +1 -0
- package/dist/core.cjs +4 -4
- package/dist/core.d.cts +8 -4
- package/dist/core.d.ts +8 -4
- package/dist/core.js +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/inputs.d.cts +1 -1
- package/dist/inputs.d.ts +1 -1
- package/dist/integration.d.cts +1 -1
- package/dist/integration.d.ts +1 -1
- package/dist/{types-BemGfSPA.d.ts → types-RnnhRtD2.d.cts} +4 -0
- package/dist/{types-BemGfSPA.d.cts → types-RnnhRtD2.d.ts} +4 -0
- package/dist/validation-rules.d.cts +1 -1
- package/dist/validation-rules.d.ts +1 -1
- package/dist/validation-utils.d.cts +1 -1
- package/dist/validation-utils.d.ts +1 -1
- package/dist/validation-valibot.d.cts +1 -1
- package/dist/validation-valibot.d.ts +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/package.json +2 -1
- package/dist/chunk-OF6ZYT7A.cjs.map +0 -1
- package/dist/chunk-TOPOO33Z.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as FormValues, U as UseFormOptions, m as UseFormReturn, p as UseFieldOptions, q as UseFieldReturn, r as FormProps, s as FieldProps } from './types-
|
|
1
|
+
import { b as FormValues, U as UseFormOptions, m as UseFormReturn, p as UseFieldOptions, q as UseFieldReturn, r as FormProps, s as FieldProps } from './types-RnnhRtD2.js';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as FormValues, U as UseFormOptions, m as UseFormReturn, p as UseFieldOptions, q as UseFieldReturn, r as FormProps, s as FieldProps } from './types-
|
|
1
|
+
import { b as FormValues, U as UseFormOptions, m as UseFormReturn, p as UseFieldOptions, q as UseFieldReturn, r as FormProps, s as FieldProps } from './types-RnnhRtD2.cjs';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -3,6 +3,7 @@ import { cn, FieldLabel, TextInput, Button } from './chunk-455PI4LV.js';
|
|
|
3
3
|
import * as React4 from 'react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import 'radix-ui';
|
|
6
|
+
import { Icon } from '@page-speed/icon';
|
|
6
7
|
|
|
7
8
|
function renderMessage(message, fallbackClassName, className) {
|
|
8
9
|
if (typeof message === "string") {
|
|
@@ -91,8 +92,7 @@ function ButtonGroup({
|
|
|
91
92
|
}
|
|
92
93
|
);
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
-
// src/core/button-group-form.tsx
|
|
95
|
+
var DEFAULT_ICON_API_KEY = "au382bi7fsh96w9h9xlrnat2jglx";
|
|
96
96
|
function ButtonGroupForm({
|
|
97
97
|
name,
|
|
98
98
|
label,
|
|
@@ -101,21 +101,24 @@ function ButtonGroupForm({
|
|
|
101
101
|
submitVariant = "default",
|
|
102
102
|
size = "default",
|
|
103
103
|
isSubmitting = false,
|
|
104
|
+
submitIconName,
|
|
104
105
|
className,
|
|
105
106
|
labelClassName
|
|
106
107
|
}) {
|
|
107
108
|
const inputId = `button-group-input-${name}`;
|
|
109
|
+
const hasValue = String(inputProps.value ?? "").trim().length > 0;
|
|
110
|
+
const hasError = !!inputProps.error;
|
|
111
|
+
const buttonSize = React4.useMemo(() => {
|
|
112
|
+
if (submitIconName) {
|
|
113
|
+
return size === "default" ? "icon" : `icon-${size}`;
|
|
114
|
+
}
|
|
115
|
+
return size;
|
|
116
|
+
}, [submitIconName, size]);
|
|
108
117
|
const inputSizeClasses = {
|
|
109
|
-
xs: "
|
|
110
|
-
sm: "
|
|
111
|
-
default: "
|
|
112
|
-
lg: "
|
|
113
|
-
};
|
|
114
|
-
const buttonSizes = {
|
|
115
|
-
xs: "xs",
|
|
116
|
-
sm: "sm",
|
|
117
|
-
default: "default",
|
|
118
|
-
lg: "lg"
|
|
118
|
+
xs: "text-xs px-3",
|
|
119
|
+
sm: "text-sm px-3",
|
|
120
|
+
default: "text-base px-4",
|
|
121
|
+
lg: "text-md px-6"
|
|
119
122
|
};
|
|
120
123
|
return /* @__PURE__ */ React4.createElement("div", { className: cn("space-y-2", className) }, label && /* @__PURE__ */ React4.createElement(FieldLabel, { htmlFor: inputId, className: labelClassName }, label), /* @__PURE__ */ React4.createElement(ButtonGroup, null, /* @__PURE__ */ React4.createElement(
|
|
121
124
|
TextInput,
|
|
@@ -131,13 +134,16 @@ function ButtonGroupForm({
|
|
|
131
134
|
), /* @__PURE__ */ React4.createElement(
|
|
132
135
|
Button,
|
|
133
136
|
{
|
|
134
|
-
size:
|
|
137
|
+
size: buttonSize,
|
|
135
138
|
type: "submit",
|
|
136
139
|
variant: submitVariant,
|
|
137
140
|
disabled: isSubmitting,
|
|
138
|
-
className:
|
|
141
|
+
className: cn(
|
|
142
|
+
"rounded-l-none",
|
|
143
|
+
!hasError && hasValue && "ring-2 ring-ring"
|
|
144
|
+
)
|
|
139
145
|
},
|
|
140
|
-
submitLabel
|
|
146
|
+
submitIconName ? /* @__PURE__ */ React4.createElement(Icon, { name: submitIconName, apiKey: DEFAULT_ICON_API_KEY }) : submitLabel
|
|
141
147
|
)));
|
|
142
148
|
}
|
|
143
149
|
ButtonGroupForm.displayName = "ButtonGroupForm";
|
|
@@ -195,7 +201,9 @@ function Form({
|
|
|
195
201
|
}, [form, onNewSubmission]);
|
|
196
202
|
const formLayout = formConfig?.formLayout ?? "standard";
|
|
197
203
|
const isButtonGroupLayout = formLayout === "button-group";
|
|
198
|
-
const hasTextField = fields && fields.length === 1 && fields[0] && ["text", "email", "password", "url", "tel", "search"].includes(
|
|
204
|
+
const hasTextField = fields && fields.length === 1 && fields[0] && ["text", "email", "password", "url", "tel", "search"].includes(
|
|
205
|
+
fields[0].type
|
|
206
|
+
);
|
|
199
207
|
const shouldUseButtonGroup = isButtonGroupLayout && hasTextField;
|
|
200
208
|
const buttonGroupContent = React4.useMemo(() => {
|
|
201
209
|
if (!shouldUseButtonGroup || !fields || fields.length === 0) return null;
|
|
@@ -218,6 +226,7 @@ function Form({
|
|
|
218
226
|
},
|
|
219
227
|
submitLabel: formConfig?.submitLabel,
|
|
220
228
|
submitVariant: formConfig?.submitVariant,
|
|
229
|
+
submitIconName: formConfig?.submitIconName,
|
|
221
230
|
size: formConfig?.buttonGroupSize,
|
|
222
231
|
isSubmitting: form.isSubmitting
|
|
223
232
|
}
|
|
@@ -259,5 +268,5 @@ function Form({
|
|
|
259
268
|
Form.displayName = "Form";
|
|
260
269
|
|
|
261
270
|
export { ButtonGroupForm, Form, FormFeedback };
|
|
262
|
-
//# sourceMappingURL=chunk-
|
|
263
|
-
//# sourceMappingURL=chunk-
|
|
271
|
+
//# sourceMappingURL=chunk-7ZX2EUOG.js.map
|
|
272
|
+
//# sourceMappingURL=chunk-7ZX2EUOG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/form-feedback.tsx","../src/components/ui/button-group.tsx","../src/core/button-group-form.tsx","../src/core/Form.tsx"],"names":["React","React2","React3"],"mappings":"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","file":"chunk-7ZX2EUOG.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface FormFeedbackProps {\n successMessage?: React.ReactNode;\n submissionError?: React.ReactNode;\n successMessageClassName?: string;\n errorMessageClassName?: string;\n}\n\nfunction renderMessage(\n message: React.ReactNode,\n fallbackClassName: string,\n className?: string,\n) {\n if (typeof message === \"string\") {\n return (\n <p\n className={cn(\n \"text-sm font-medium text-center text-balance\",\n className,\n )}\n >\n {message}\n </p>\n );\n }\n\n return <div className={cn(fallbackClassName, className)}>{message}</div>;\n}\n\nexport function FormFeedback({\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n}: FormFeedbackProps) {\n if (!successMessage && !submissionError) {\n return null;\n }\n\n return (\n <>\n {successMessage ? (\n <div\n className={cn(\n \"rounded-md border border-primary bg-primary px-4 py-3 shadow-sm\",\n successMessageClassName,\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n {renderMessage(\n successMessage,\n \"text-primary-foreground\",\n \"text-primary-foreground\",\n )}\n </div>\n ) : null}\n\n {submissionError ? (\n <div\n className={cn(\n \"rounded-md border border-destructive bg-destructive px-4 py-3 shadow-sm\",\n errorMessageClassName,\n )}\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {renderMessage(\n submissionError,\n \"text-destructive-foreground\",\n \"text-destructive-foreground\",\n )}\n </div>\n ) : null}\n </>\n );\n}\n\nFormFeedback.displayName = \"FormFeedback\";\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"../../lib/utils\"\nimport { Separator } from \"./separator\"\n\nconst buttonGroupVariants = cva(\n \"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2\",\n {\n variants: {\n orientation: {\n horizontal:\n \"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none\",\n vertical:\n \"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none\",\n },\n },\n defaultVariants: {\n orientation: \"horizontal\",\n },\n }\n)\n\nfunction ButtonGroup({\n className,\n orientation,\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof buttonGroupVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"button-group\"\n data-orientation={orientation}\n className={cn(buttonGroupVariants({ orientation }), className)}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupText({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n asChild?: boolean\n}) {\n const Comp = asChild ? Slot.Root : \"div\"\n\n return (\n <Comp\n className={cn(\n \"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupSeparator({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof Separator>) {\n return (\n <Separator\n data-slot=\"button-group-separator\"\n orientation={orientation}\n className={cn(\n \"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n ButtonGroup,\n ButtonGroupSeparator,\n ButtonGroupText,\n buttonGroupVariants,\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroup } from \"../components/ui/button-group\";\nimport { FieldLabel } from \"../components/ui/field\";\nimport { TextInput } from \"../inputs/TextInput\";\nimport type { InputProps } from \"./types\";\nimport { Icon } from \"@page-speed/icon\";\n\nconst DEFAULT_ICON_API_KEY = \"au382bi7fsh96w9h9xlrnat2jglx\";\n\nexport type ButtonGroupFormSize = \"xs\" | \"sm\" | \"default\" | \"lg\";\n\nexport type ButtonGroupFormProps = {\n /**\n * Field name\n */\n name: string;\n /**\n * Optional label above the input\n */\n label?: React.ReactNode;\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n /**\n * Input props from form field\n */\n inputProps: InputProps<string> & {\n type?: \"text\" | \"email\" | \"password\" | \"url\" | \"tel\" | \"search\";\n };\n /**\n * Submit button label\n */\n submitLabel?: React.ReactNode;\n /**\n * Submit button size\n */\n size?: ButtonGroupFormSize;\n /**\n * Submit button variant\n */\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | null\n | undefined;\n /**\n * Whether form is submitting\n */\n isSubmitting?: boolean;\n /**\n * Additional className for the container\n */\n className?: string;\n /**\n * Icon name for icon based submit buttons\n */\n submitIconName?: string;\n /**\n * Additional className for the label\n */\n labelClassName?: string;\n};\n\n/**\n * ButtonGroupForm - Inline form layout with input and submit button grouped together\n *\n * Commonly used for newsletter signups and other simple single-field forms.\n * The input and button automatically adjust sizing together.\n *\n * Size mappings:\n * - xs: h-8 text-xs\n * - sm: h-9 text-sm\n * - default: h-10 text-sm\n * - lg: h-12 text-base\n *\n * @example\n * ```tsx\n * <ButtonGroupForm\n * name=\"email\"\n * placeholder=\"Enter your email\"\n * inputProps={form.getFieldProps('email')}\n * submitLabel=\"Subscribe\"\n * size=\"default\"\n * />\n * ```\n */\nexport function ButtonGroupForm({\n name,\n label,\n inputProps,\n submitLabel = \"Submit\",\n submitVariant = \"default\",\n size = \"default\",\n isSubmitting = false,\n submitIconName,\n className,\n labelClassName,\n}: ButtonGroupFormProps) {\n const inputId = `button-group-input-${name}`;\n\n const hasValue = String(inputProps.value ?? \"\").trim().length > 0;\n const hasError = !!inputProps.error;\n\n const buttonSize:\n | \"xs\"\n | \"sm\"\n | \"default\"\n | \"lg\"\n | \"icon\"\n | \"icon-xs\"\n | \"icon-sm\"\n | \"icon-lg\" = React.useMemo(() => {\n if (submitIconName) {\n return size === \"default\" ? \"icon\" : (`icon-${size}` as const);\n }\n return size;\n }, [submitIconName, size]);\n\n // Size-specific classes for input to match button heights\n const inputSizeClasses = {\n xs: \"text-xs px-3\",\n sm: \"text-sm px-3\",\n default: \"text-base px-4\",\n lg: \"text-md px-6\",\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {label && (\n <FieldLabel htmlFor={inputId} className={labelClassName}>\n {label}\n </FieldLabel>\n )}\n <ButtonGroup>\n <TextInput\n {...inputProps}\n id={inputId}\n className={cn(\n inputSizeClasses[size],\n \"border-r-0 rounded-r-none focus-visible:z-10\",\n inputProps.className,\n )}\n />\n <Button\n size={buttonSize}\n type=\"submit\"\n variant={submitVariant}\n disabled={isSubmitting}\n className={cn(\n \"rounded-l-none\",\n !hasError && hasValue && \"ring-2 ring-ring\",\n )}\n >\n {submitIconName ? (\n <Icon name={submitIconName} apiKey={DEFAULT_ICON_API_KEY} />\n ) : (\n submitLabel\n )}\n </Button>\n </ButtonGroup>\n </div>\n );\n}\n\nButtonGroupForm.displayName = \"ButtonGroupForm\";\n","\"use client\";\n\nimport * as React from \"react\";\nimport { FormContext } from \"./FormContext\";\nimport type { FormProps, FormValues } from \"./types\";\nimport { FormFeedback } from \"./form-feedback\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroupForm } from \"./button-group-form\";\nimport type { FormFieldConfig } from \"../integration/form-field-types\";\n\n/**\n * Form - Progressive enhancement form component\n *\n * Provides form context to child components and handles form submission.\n * Supports progressive enhancement with server-side fallback.\n *\n * Features:\n * - Provides FormContext for useField hook\n * - Handles form submission with validation\n * - Progressive enhancement support (works without JavaScript)\n * - Accessible form semantics\n *\n * @example\n * ```tsx\n * const form = useForm({\n * initialValues: { email: '' },\n * onSubmit: async (values) => {\n * await submitForm(values);\n * },\n * });\n *\n * return (\n * <Form form={form} action=\"/api/submit\" method=\"post\">\n * <input {...form.getFieldProps('email')} />\n * <button type=\"submit\">Submit</button>\n * </Form>\n * );\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/form\n */\nexport function Form<T extends FormValues = FormValues>({\n form,\n children,\n fields,\n className,\n action,\n method,\n noValidate = true,\n submissionConfig,\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n onNewSubmission,\n notificationConfig,\n styleConfig,\n formConfig,\n ...props\n}: FormProps<T> & React.FormHTMLAttributes<HTMLFormElement>) {\n // Wrap handleSubmit to catch any unhandled rejections\n const handleFormSubmit = React.useCallback(\n async (e: React.FormEvent) => {\n try {\n await form.handleSubmit(e);\n } catch {\n // Error is already handled by useForm, just prevent unhandled rejection\n // The form status and errors are already set by useForm's error handling\n }\n },\n [form],\n );\n\n const resolvedClassName = className ?? styleConfig?.formClassName;\n const resolvedAction = action ?? formConfig?.endpoint;\n const resolvedMethod = method ?? formConfig?.method ?? \"post\";\n const resolvedSubmissionConfig =\n submissionConfig ?? formConfig?.submissionConfig;\n const resolvedSuccessMessage =\n successMessage ?? notificationConfig?.successMessage;\n const resolvedSubmissionError =\n submissionError ?? notificationConfig?.submissionError;\n const resolvedSuccessMessageClassName =\n successMessageClassName ?? styleConfig?.successMessageClassName;\n const resolvedErrorMessageClassName =\n errorMessageClassName ?? styleConfig?.errorMessageClassName;\n\n const behavior = resolvedSubmissionConfig?.behavior || \"showConfirmation\";\n\n const shouldManageSubmissionUi =\n resolvedSubmissionConfig !== undefined ||\n resolvedSuccessMessage !== undefined ||\n resolvedSuccessMessageClassName !== undefined ||\n resolvedErrorMessageClassName !== undefined ||\n resolvedSubmissionError != null ||\n onNewSubmission !== undefined;\n\n const hasSubmissionError = Boolean(resolvedSubmissionError);\n\n const isSubmissionSuccessful =\n shouldManageSubmissionUi &&\n form.status === \"success\" &&\n !hasSubmissionError;\n\n const defaultSuccessMessage =\n behavior === \"redirect\"\n ? \"Form submitted successfully. Redirecting...\"\n : \"Thank you. Your form has been submitted successfully.\";\n\n const finalSuccessMessage = resolvedSuccessMessage ?? defaultSuccessMessage;\n\n const shouldRenderCustomComponent =\n isSubmissionSuccessful &&\n behavior === \"renderCustomComponent\" &&\n Boolean(resolvedSubmissionConfig?.customComponent);\n\n const newSubmissionAction = resolvedSubmissionConfig?.newFormSubmissionAction;\n\n const showNewSubmissionAction =\n isSubmissionSuccessful &&\n (typeof newSubmissionAction?.enable === \"boolean\"\n ? newSubmissionAction.enable\n : Boolean(newSubmissionAction?.label));\n\n const newSubmissionLabel =\n newSubmissionAction?.label ?? \"Submit another response\";\n\n const handleNewSubmission = React.useCallback(() => {\n form.resetForm();\n onNewSubmission?.();\n }, [form, onNewSubmission]);\n\n // Check if we should use button-group layout\n const formLayout = formConfig?.formLayout ?? \"standard\";\n const isButtonGroupLayout = formLayout === \"button-group\";\n const hasTextField =\n fields &&\n fields.length === 1 &&\n fields[0] &&\n [\"text\", \"email\", \"password\", \"url\", \"tel\", \"search\"].includes(\n fields[0].type,\n );\n const shouldUseButtonGroup = isButtonGroupLayout && hasTextField;\n\n // Render button-group layout if conditions are met\n const buttonGroupContent = React.useMemo(() => {\n if (!shouldUseButtonGroup || !fields || fields.length === 0) return null;\n\n const field = fields[0] as FormFieldConfig;\n const fieldProps = form.getFieldProps(field.name);\n\n return (\n <ButtonGroupForm\n name={field.name}\n label={field.label}\n inputProps={{\n name: fieldProps.name,\n value: fieldProps.value as string,\n onChange: fieldProps.onChange as (value: string) => void,\n onBlur: fieldProps.onBlur,\n type: field.type as\n | \"text\"\n | \"email\"\n | \"password\"\n | \"url\"\n | \"tel\"\n | \"search\",\n placeholder: field.placeholder,\n required: field.required,\n disabled: field.disabled,\n }}\n submitLabel={formConfig?.submitLabel}\n submitVariant={formConfig?.submitVariant}\n submitIconName={formConfig?.submitIconName}\n size={formConfig?.buttonGroupSize}\n isSubmitting={form.isSubmitting}\n />\n );\n }, [shouldUseButtonGroup, fields, form, formConfig]);\n\n return (\n <FormContext.Provider value={form}>\n <form\n onSubmit={handleFormSubmit}\n action={resolvedAction}\n method={resolvedMethod}\n noValidate={noValidate}\n className={resolvedClassName}\n {...props}\n >\n {isSubmissionSuccessful ? (\n <div className=\"space-y-4\">\n {shouldRenderCustomComponent ? (\n resolvedSubmissionConfig?.customComponent\n ) : (\n <FormFeedback\n successMessage={finalSuccessMessage}\n successMessageClassName={resolvedSuccessMessageClassName}\n />\n )}\n\n {showNewSubmissionAction ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleNewSubmission}\n >\n {newSubmissionLabel}\n </Button>\n ) : null}\n </div>\n ) : (\n <>\n {shouldUseButtonGroup ? buttonGroupContent : children}\n {resolvedSubmissionError ? (\n <div className=\"mt-4\">\n <FormFeedback\n submissionError={resolvedSubmissionError}\n errorMessageClassName={resolvedErrorMessageClassName}\n />\n </div>\n ) : null}\n </>\n )}\n </form>\n </FormContext.Provider>\n );\n}\n\nForm.displayName = \"Form\";\n"]}
|
|
@@ -5,6 +5,7 @@ var chunkQRI5TMES_cjs = require('./chunk-QRI5TMES.cjs');
|
|
|
5
5
|
var React4 = require('react');
|
|
6
6
|
var classVarianceAuthority = require('class-variance-authority');
|
|
7
7
|
require('radix-ui');
|
|
8
|
+
var icon = require('@page-speed/icon');
|
|
8
9
|
|
|
9
10
|
function _interopNamespace(e) {
|
|
10
11
|
if (e && e.__esModule) return e;
|
|
@@ -113,8 +114,7 @@ function ButtonGroup({
|
|
|
113
114
|
}
|
|
114
115
|
);
|
|
115
116
|
}
|
|
116
|
-
|
|
117
|
-
// src/core/button-group-form.tsx
|
|
117
|
+
var DEFAULT_ICON_API_KEY = "au382bi7fsh96w9h9xlrnat2jglx";
|
|
118
118
|
function ButtonGroupForm({
|
|
119
119
|
name,
|
|
120
120
|
label,
|
|
@@ -123,21 +123,24 @@ function ButtonGroupForm({
|
|
|
123
123
|
submitVariant = "default",
|
|
124
124
|
size = "default",
|
|
125
125
|
isSubmitting = false,
|
|
126
|
+
submitIconName,
|
|
126
127
|
className,
|
|
127
128
|
labelClassName
|
|
128
129
|
}) {
|
|
129
130
|
const inputId = `button-group-input-${name}`;
|
|
131
|
+
const hasValue = String(inputProps.value ?? "").trim().length > 0;
|
|
132
|
+
const hasError = !!inputProps.error;
|
|
133
|
+
const buttonSize = React4__namespace.useMemo(() => {
|
|
134
|
+
if (submitIconName) {
|
|
135
|
+
return size === "default" ? "icon" : `icon-${size}`;
|
|
136
|
+
}
|
|
137
|
+
return size;
|
|
138
|
+
}, [submitIconName, size]);
|
|
130
139
|
const inputSizeClasses = {
|
|
131
|
-
xs: "
|
|
132
|
-
sm: "
|
|
133
|
-
default: "
|
|
134
|
-
lg: "
|
|
135
|
-
};
|
|
136
|
-
const buttonSizes = {
|
|
137
|
-
xs: "xs",
|
|
138
|
-
sm: "sm",
|
|
139
|
-
default: "default",
|
|
140
|
-
lg: "lg"
|
|
140
|
+
xs: "text-xs px-3",
|
|
141
|
+
sm: "text-sm px-3",
|
|
142
|
+
default: "text-base px-4",
|
|
143
|
+
lg: "text-md px-6"
|
|
141
144
|
};
|
|
142
145
|
return /* @__PURE__ */ React4__namespace.createElement("div", { className: chunkQRI5TMES_cjs.cn("space-y-2", className) }, label && /* @__PURE__ */ React4__namespace.createElement(chunkQRI5TMES_cjs.FieldLabel, { htmlFor: inputId, className: labelClassName }, label), /* @__PURE__ */ React4__namespace.createElement(ButtonGroup, null, /* @__PURE__ */ React4__namespace.createElement(
|
|
143
146
|
chunkQRI5TMES_cjs.TextInput,
|
|
@@ -153,13 +156,16 @@ function ButtonGroupForm({
|
|
|
153
156
|
), /* @__PURE__ */ React4__namespace.createElement(
|
|
154
157
|
chunkQRI5TMES_cjs.Button,
|
|
155
158
|
{
|
|
156
|
-
size:
|
|
159
|
+
size: buttonSize,
|
|
157
160
|
type: "submit",
|
|
158
161
|
variant: submitVariant,
|
|
159
162
|
disabled: isSubmitting,
|
|
160
|
-
className:
|
|
163
|
+
className: chunkQRI5TMES_cjs.cn(
|
|
164
|
+
"rounded-l-none",
|
|
165
|
+
!hasError && hasValue && "ring-2 ring-ring"
|
|
166
|
+
)
|
|
161
167
|
},
|
|
162
|
-
submitLabel
|
|
168
|
+
submitIconName ? /* @__PURE__ */ React4__namespace.createElement(icon.Icon, { name: submitIconName, apiKey: DEFAULT_ICON_API_KEY }) : submitLabel
|
|
163
169
|
)));
|
|
164
170
|
}
|
|
165
171
|
ButtonGroupForm.displayName = "ButtonGroupForm";
|
|
@@ -217,7 +223,9 @@ function Form({
|
|
|
217
223
|
}, [form, onNewSubmission]);
|
|
218
224
|
const formLayout = formConfig?.formLayout ?? "standard";
|
|
219
225
|
const isButtonGroupLayout = formLayout === "button-group";
|
|
220
|
-
const hasTextField = fields && fields.length === 1 && fields[0] && ["text", "email", "password", "url", "tel", "search"].includes(
|
|
226
|
+
const hasTextField = fields && fields.length === 1 && fields[0] && ["text", "email", "password", "url", "tel", "search"].includes(
|
|
227
|
+
fields[0].type
|
|
228
|
+
);
|
|
221
229
|
const shouldUseButtonGroup = isButtonGroupLayout && hasTextField;
|
|
222
230
|
const buttonGroupContent = React4__namespace.useMemo(() => {
|
|
223
231
|
if (!shouldUseButtonGroup || !fields || fields.length === 0) return null;
|
|
@@ -240,6 +248,7 @@ function Form({
|
|
|
240
248
|
},
|
|
241
249
|
submitLabel: formConfig?.submitLabel,
|
|
242
250
|
submitVariant: formConfig?.submitVariant,
|
|
251
|
+
submitIconName: formConfig?.submitIconName,
|
|
243
252
|
size: formConfig?.buttonGroupSize,
|
|
244
253
|
isSubmitting: form.isSubmitting
|
|
245
254
|
}
|
|
@@ -283,5 +292,5 @@ Form.displayName = "Form";
|
|
|
283
292
|
exports.ButtonGroupForm = ButtonGroupForm;
|
|
284
293
|
exports.Form = Form;
|
|
285
294
|
exports.FormFeedback = FormFeedback;
|
|
286
|
-
//# sourceMappingURL=chunk-
|
|
287
|
-
//# sourceMappingURL=chunk-
|
|
295
|
+
//# sourceMappingURL=chunk-F5ZJCJOD.cjs.map
|
|
296
|
+
//# sourceMappingURL=chunk-F5ZJCJOD.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/form-feedback.tsx","../src/components/ui/button-group.tsx","../src/core/button-group-form.tsx","../src/core/Form.tsx"],"names":["React","cn","cva","React2","React3","FieldLabel","TextInput","Button","Icon","React4","FormContext"],"mappings":"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","file":"chunk-F5ZJCJOD.cjs","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface FormFeedbackProps {\n successMessage?: React.ReactNode;\n submissionError?: React.ReactNode;\n successMessageClassName?: string;\n errorMessageClassName?: string;\n}\n\nfunction renderMessage(\n message: React.ReactNode,\n fallbackClassName: string,\n className?: string,\n) {\n if (typeof message === \"string\") {\n return (\n <p\n className={cn(\n \"text-sm font-medium text-center text-balance\",\n className,\n )}\n >\n {message}\n </p>\n );\n }\n\n return <div className={cn(fallbackClassName, className)}>{message}</div>;\n}\n\nexport function FormFeedback({\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n}: FormFeedbackProps) {\n if (!successMessage && !submissionError) {\n return null;\n }\n\n return (\n <>\n {successMessage ? (\n <div\n className={cn(\n \"rounded-md border border-primary bg-primary px-4 py-3 shadow-sm\",\n successMessageClassName,\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n {renderMessage(\n successMessage,\n \"text-primary-foreground\",\n \"text-primary-foreground\",\n )}\n </div>\n ) : null}\n\n {submissionError ? (\n <div\n className={cn(\n \"rounded-md border border-destructive bg-destructive px-4 py-3 shadow-sm\",\n errorMessageClassName,\n )}\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {renderMessage(\n submissionError,\n \"text-destructive-foreground\",\n \"text-destructive-foreground\",\n )}\n </div>\n ) : null}\n </>\n );\n}\n\nFormFeedback.displayName = \"FormFeedback\";\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"../../lib/utils\"\nimport { Separator } from \"./separator\"\n\nconst buttonGroupVariants = cva(\n \"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2\",\n {\n variants: {\n orientation: {\n horizontal:\n \"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none\",\n vertical:\n \"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none\",\n },\n },\n defaultVariants: {\n orientation: \"horizontal\",\n },\n }\n)\n\nfunction ButtonGroup({\n className,\n orientation,\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof buttonGroupVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"button-group\"\n data-orientation={orientation}\n className={cn(buttonGroupVariants({ orientation }), className)}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupText({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n asChild?: boolean\n}) {\n const Comp = asChild ? Slot.Root : \"div\"\n\n return (\n <Comp\n className={cn(\n \"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupSeparator({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof Separator>) {\n return (\n <Separator\n data-slot=\"button-group-separator\"\n orientation={orientation}\n className={cn(\n \"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n ButtonGroup,\n ButtonGroupSeparator,\n ButtonGroupText,\n buttonGroupVariants,\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroup } from \"../components/ui/button-group\";\nimport { FieldLabel } from \"../components/ui/field\";\nimport { TextInput } from \"../inputs/TextInput\";\nimport type { InputProps } from \"./types\";\nimport { Icon } from \"@page-speed/icon\";\n\nconst DEFAULT_ICON_API_KEY = \"au382bi7fsh96w9h9xlrnat2jglx\";\n\nexport type ButtonGroupFormSize = \"xs\" | \"sm\" | \"default\" | \"lg\";\n\nexport type ButtonGroupFormProps = {\n /**\n * Field name\n */\n name: string;\n /**\n * Optional label above the input\n */\n label?: React.ReactNode;\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n /**\n * Input props from form field\n */\n inputProps: InputProps<string> & {\n type?: \"text\" | \"email\" | \"password\" | \"url\" | \"tel\" | \"search\";\n };\n /**\n * Submit button label\n */\n submitLabel?: React.ReactNode;\n /**\n * Submit button size\n */\n size?: ButtonGroupFormSize;\n /**\n * Submit button variant\n */\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | null\n | undefined;\n /**\n * Whether form is submitting\n */\n isSubmitting?: boolean;\n /**\n * Additional className for the container\n */\n className?: string;\n /**\n * Icon name for icon based submit buttons\n */\n submitIconName?: string;\n /**\n * Additional className for the label\n */\n labelClassName?: string;\n};\n\n/**\n * ButtonGroupForm - Inline form layout with input and submit button grouped together\n *\n * Commonly used for newsletter signups and other simple single-field forms.\n * The input and button automatically adjust sizing together.\n *\n * Size mappings:\n * - xs: h-8 text-xs\n * - sm: h-9 text-sm\n * - default: h-10 text-sm\n * - lg: h-12 text-base\n *\n * @example\n * ```tsx\n * <ButtonGroupForm\n * name=\"email\"\n * placeholder=\"Enter your email\"\n * inputProps={form.getFieldProps('email')}\n * submitLabel=\"Subscribe\"\n * size=\"default\"\n * />\n * ```\n */\nexport function ButtonGroupForm({\n name,\n label,\n inputProps,\n submitLabel = \"Submit\",\n submitVariant = \"default\",\n size = \"default\",\n isSubmitting = false,\n submitIconName,\n className,\n labelClassName,\n}: ButtonGroupFormProps) {\n const inputId = `button-group-input-${name}`;\n\n const hasValue = String(inputProps.value ?? \"\").trim().length > 0;\n const hasError = !!inputProps.error;\n\n const buttonSize:\n | \"xs\"\n | \"sm\"\n | \"default\"\n | \"lg\"\n | \"icon\"\n | \"icon-xs\"\n | \"icon-sm\"\n | \"icon-lg\" = React.useMemo(() => {\n if (submitIconName) {\n return size === \"default\" ? \"icon\" : (`icon-${size}` as const);\n }\n return size;\n }, [submitIconName, size]);\n\n // Size-specific classes for input to match button heights\n const inputSizeClasses = {\n xs: \"text-xs px-3\",\n sm: \"text-sm px-3\",\n default: \"text-base px-4\",\n lg: \"text-md px-6\",\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {label && (\n <FieldLabel htmlFor={inputId} className={labelClassName}>\n {label}\n </FieldLabel>\n )}\n <ButtonGroup>\n <TextInput\n {...inputProps}\n id={inputId}\n className={cn(\n inputSizeClasses[size],\n \"border-r-0 rounded-r-none focus-visible:z-10\",\n inputProps.className,\n )}\n />\n <Button\n size={buttonSize}\n type=\"submit\"\n variant={submitVariant}\n disabled={isSubmitting}\n className={cn(\n \"rounded-l-none\",\n !hasError && hasValue && \"ring-2 ring-ring\",\n )}\n >\n {submitIconName ? (\n <Icon name={submitIconName} apiKey={DEFAULT_ICON_API_KEY} />\n ) : (\n submitLabel\n )}\n </Button>\n </ButtonGroup>\n </div>\n );\n}\n\nButtonGroupForm.displayName = \"ButtonGroupForm\";\n","\"use client\";\n\nimport * as React from \"react\";\nimport { FormContext } from \"./FormContext\";\nimport type { FormProps, FormValues } from \"./types\";\nimport { FormFeedback } from \"./form-feedback\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroupForm } from \"./button-group-form\";\nimport type { FormFieldConfig } from \"../integration/form-field-types\";\n\n/**\n * Form - Progressive enhancement form component\n *\n * Provides form context to child components and handles form submission.\n * Supports progressive enhancement with server-side fallback.\n *\n * Features:\n * - Provides FormContext for useField hook\n * - Handles form submission with validation\n * - Progressive enhancement support (works without JavaScript)\n * - Accessible form semantics\n *\n * @example\n * ```tsx\n * const form = useForm({\n * initialValues: { email: '' },\n * onSubmit: async (values) => {\n * await submitForm(values);\n * },\n * });\n *\n * return (\n * <Form form={form} action=\"/api/submit\" method=\"post\">\n * <input {...form.getFieldProps('email')} />\n * <button type=\"submit\">Submit</button>\n * </Form>\n * );\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/form\n */\nexport function Form<T extends FormValues = FormValues>({\n form,\n children,\n fields,\n className,\n action,\n method,\n noValidate = true,\n submissionConfig,\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n onNewSubmission,\n notificationConfig,\n styleConfig,\n formConfig,\n ...props\n}: FormProps<T> & React.FormHTMLAttributes<HTMLFormElement>) {\n // Wrap handleSubmit to catch any unhandled rejections\n const handleFormSubmit = React.useCallback(\n async (e: React.FormEvent) => {\n try {\n await form.handleSubmit(e);\n } catch {\n // Error is already handled by useForm, just prevent unhandled rejection\n // The form status and errors are already set by useForm's error handling\n }\n },\n [form],\n );\n\n const resolvedClassName = className ?? styleConfig?.formClassName;\n const resolvedAction = action ?? formConfig?.endpoint;\n const resolvedMethod = method ?? formConfig?.method ?? \"post\";\n const resolvedSubmissionConfig =\n submissionConfig ?? formConfig?.submissionConfig;\n const resolvedSuccessMessage =\n successMessage ?? notificationConfig?.successMessage;\n const resolvedSubmissionError =\n submissionError ?? notificationConfig?.submissionError;\n const resolvedSuccessMessageClassName =\n successMessageClassName ?? styleConfig?.successMessageClassName;\n const resolvedErrorMessageClassName =\n errorMessageClassName ?? styleConfig?.errorMessageClassName;\n\n const behavior = resolvedSubmissionConfig?.behavior || \"showConfirmation\";\n\n const shouldManageSubmissionUi =\n resolvedSubmissionConfig !== undefined ||\n resolvedSuccessMessage !== undefined ||\n resolvedSuccessMessageClassName !== undefined ||\n resolvedErrorMessageClassName !== undefined ||\n resolvedSubmissionError != null ||\n onNewSubmission !== undefined;\n\n const hasSubmissionError = Boolean(resolvedSubmissionError);\n\n const isSubmissionSuccessful =\n shouldManageSubmissionUi &&\n form.status === \"success\" &&\n !hasSubmissionError;\n\n const defaultSuccessMessage =\n behavior === \"redirect\"\n ? \"Form submitted successfully. Redirecting...\"\n : \"Thank you. Your form has been submitted successfully.\";\n\n const finalSuccessMessage = resolvedSuccessMessage ?? defaultSuccessMessage;\n\n const shouldRenderCustomComponent =\n isSubmissionSuccessful &&\n behavior === \"renderCustomComponent\" &&\n Boolean(resolvedSubmissionConfig?.customComponent);\n\n const newSubmissionAction = resolvedSubmissionConfig?.newFormSubmissionAction;\n\n const showNewSubmissionAction =\n isSubmissionSuccessful &&\n (typeof newSubmissionAction?.enable === \"boolean\"\n ? newSubmissionAction.enable\n : Boolean(newSubmissionAction?.label));\n\n const newSubmissionLabel =\n newSubmissionAction?.label ?? \"Submit another response\";\n\n const handleNewSubmission = React.useCallback(() => {\n form.resetForm();\n onNewSubmission?.();\n }, [form, onNewSubmission]);\n\n // Check if we should use button-group layout\n const formLayout = formConfig?.formLayout ?? \"standard\";\n const isButtonGroupLayout = formLayout === \"button-group\";\n const hasTextField =\n fields &&\n fields.length === 1 &&\n fields[0] &&\n [\"text\", \"email\", \"password\", \"url\", \"tel\", \"search\"].includes(\n fields[0].type,\n );\n const shouldUseButtonGroup = isButtonGroupLayout && hasTextField;\n\n // Render button-group layout if conditions are met\n const buttonGroupContent = React.useMemo(() => {\n if (!shouldUseButtonGroup || !fields || fields.length === 0) return null;\n\n const field = fields[0] as FormFieldConfig;\n const fieldProps = form.getFieldProps(field.name);\n\n return (\n <ButtonGroupForm\n name={field.name}\n label={field.label}\n inputProps={{\n name: fieldProps.name,\n value: fieldProps.value as string,\n onChange: fieldProps.onChange as (value: string) => void,\n onBlur: fieldProps.onBlur,\n type: field.type as\n | \"text\"\n | \"email\"\n | \"password\"\n | \"url\"\n | \"tel\"\n | \"search\",\n placeholder: field.placeholder,\n required: field.required,\n disabled: field.disabled,\n }}\n submitLabel={formConfig?.submitLabel}\n submitVariant={formConfig?.submitVariant}\n submitIconName={formConfig?.submitIconName}\n size={formConfig?.buttonGroupSize}\n isSubmitting={form.isSubmitting}\n />\n );\n }, [shouldUseButtonGroup, fields, form, formConfig]);\n\n return (\n <FormContext.Provider value={form}>\n <form\n onSubmit={handleFormSubmit}\n action={resolvedAction}\n method={resolvedMethod}\n noValidate={noValidate}\n className={resolvedClassName}\n {...props}\n >\n {isSubmissionSuccessful ? (\n <div className=\"space-y-4\">\n {shouldRenderCustomComponent ? (\n resolvedSubmissionConfig?.customComponent\n ) : (\n <FormFeedback\n successMessage={finalSuccessMessage}\n successMessageClassName={resolvedSuccessMessageClassName}\n />\n )}\n\n {showNewSubmissionAction ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleNewSubmission}\n >\n {newSubmissionLabel}\n </Button>\n ) : null}\n </div>\n ) : (\n <>\n {shouldUseButtonGroup ? buttonGroupContent : children}\n {resolvedSubmissionError ? (\n <div className=\"mt-4\">\n <FormFeedback\n submissionError={resolvedSubmissionError}\n errorMessageClassName={resolvedErrorMessageClassName}\n />\n </div>\n ) : null}\n </>\n )}\n </form>\n </FormContext.Provider>\n );\n}\n\nForm.displayName = \"Form\";\n"]}
|
package/dist/core.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkF5ZJCJOD_cjs = require('./chunk-F5ZJCJOD.cjs');
|
|
4
4
|
var chunkMUBEMXI7_cjs = require('./chunk-MUBEMXI7.cjs');
|
|
5
5
|
require('./chunk-QRI5TMES.cjs');
|
|
6
6
|
|
|
@@ -8,15 +8,15 @@ require('./chunk-QRI5TMES.cjs');
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "ButtonGroupForm", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunkF5ZJCJOD_cjs.ButtonGroupForm; }
|
|
12
12
|
});
|
|
13
13
|
Object.defineProperty(exports, "Form", {
|
|
14
14
|
enumerable: true,
|
|
15
|
-
get: function () { return
|
|
15
|
+
get: function () { return chunkF5ZJCJOD_cjs.Form; }
|
|
16
16
|
});
|
|
17
17
|
Object.defineProperty(exports, "FormFeedback", {
|
|
18
18
|
enumerable: true,
|
|
19
|
-
get: function () { return
|
|
19
|
+
get: function () { return chunkF5ZJCJOD_cjs.FormFeedback; }
|
|
20
20
|
});
|
|
21
21
|
Object.defineProperty(exports, "Field", {
|
|
22
22
|
enumerable: true,
|
package/dist/core.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-
|
|
1
|
+
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-KsjKSot3.cjs';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { I as InputProps } from './types-
|
|
4
|
-
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-
|
|
3
|
+
import { I as InputProps } from './types-RnnhRtD2.cjs';
|
|
4
|
+
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-RnnhRtD2.cjs';
|
|
5
5
|
|
|
6
6
|
type ButtonGroupFormSize = "xs" | "sm" | "default" | "lg";
|
|
7
7
|
type ButtonGroupFormProps = {
|
|
@@ -43,6 +43,10 @@ type ButtonGroupFormProps = {
|
|
|
43
43
|
* Additional className for the container
|
|
44
44
|
*/
|
|
45
45
|
className?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Icon name for icon based submit buttons
|
|
48
|
+
*/
|
|
49
|
+
submitIconName?: string;
|
|
46
50
|
/**
|
|
47
51
|
* Additional className for the label
|
|
48
52
|
*/
|
|
@@ -71,7 +75,7 @@ type ButtonGroupFormProps = {
|
|
|
71
75
|
* />
|
|
72
76
|
* ```
|
|
73
77
|
*/
|
|
74
|
-
declare function ButtonGroupForm({ name, label, inputProps, submitLabel, submitVariant, size, isSubmitting, className, labelClassName, }: ButtonGroupFormProps): React.JSX.Element;
|
|
78
|
+
declare function ButtonGroupForm({ name, label, inputProps, submitLabel, submitVariant, size, isSubmitting, submitIconName, className, labelClassName, }: ButtonGroupFormProps): React.JSX.Element;
|
|
75
79
|
declare namespace ButtonGroupForm {
|
|
76
80
|
var displayName: string;
|
|
77
81
|
}
|
package/dist/core.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-
|
|
1
|
+
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-DaRyEYnF.js';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { I as InputProps } from './types-
|
|
4
|
-
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-
|
|
3
|
+
import { I as InputProps } from './types-RnnhRtD2.js';
|
|
4
|
+
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-RnnhRtD2.js';
|
|
5
5
|
|
|
6
6
|
type ButtonGroupFormSize = "xs" | "sm" | "default" | "lg";
|
|
7
7
|
type ButtonGroupFormProps = {
|
|
@@ -43,6 +43,10 @@ type ButtonGroupFormProps = {
|
|
|
43
43
|
* Additional className for the container
|
|
44
44
|
*/
|
|
45
45
|
className?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Icon name for icon based submit buttons
|
|
48
|
+
*/
|
|
49
|
+
submitIconName?: string;
|
|
46
50
|
/**
|
|
47
51
|
* Additional className for the label
|
|
48
52
|
*/
|
|
@@ -71,7 +75,7 @@ type ButtonGroupFormProps = {
|
|
|
71
75
|
* />
|
|
72
76
|
* ```
|
|
73
77
|
*/
|
|
74
|
-
declare function ButtonGroupForm({ name, label, inputProps, submitLabel, submitVariant, size, isSubmitting, className, labelClassName, }: ButtonGroupFormProps): React.JSX.Element;
|
|
78
|
+
declare function ButtonGroupForm({ name, label, inputProps, submitLabel, submitVariant, size, isSubmitting, submitIconName, className, labelClassName, }: ButtonGroupFormProps): React.JSX.Element;
|
|
75
79
|
declare namespace ButtonGroupForm {
|
|
76
80
|
var displayName: string;
|
|
77
81
|
}
|
package/dist/core.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ButtonGroupForm, Form, FormFeedback } from './chunk-
|
|
1
|
+
export { ButtonGroupForm, Form, FormFeedback } from './chunk-7ZX2EUOG.js';
|
|
2
2
|
export { Field, FormContext, useField, useForm } from './chunk-MJYEXJ3U.js';
|
|
3
3
|
import './chunk-455PI4LV.js';
|
|
4
4
|
//# sourceMappingURL=core.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkF5ZJCJOD_cjs = require('./chunk-F5ZJCJOD.cjs');
|
|
4
4
|
var chunkMUBEMXI7_cjs = require('./chunk-MUBEMXI7.cjs');
|
|
5
5
|
var chunkQRI5TMES_cjs = require('./chunk-QRI5TMES.cjs');
|
|
6
6
|
|
|
@@ -8,11 +8,11 @@ var chunkQRI5TMES_cjs = require('./chunk-QRI5TMES.cjs');
|
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(exports, "Form", {
|
|
10
10
|
enumerable: true,
|
|
11
|
-
get: function () { return
|
|
11
|
+
get: function () { return chunkF5ZJCJOD_cjs.Form; }
|
|
12
12
|
});
|
|
13
13
|
Object.defineProperty(exports, "FormFeedback", {
|
|
14
14
|
enumerable: true,
|
|
15
|
-
get: function () { return
|
|
15
|
+
get: function () { return chunkF5ZJCJOD_cjs.FormFeedback; }
|
|
16
16
|
});
|
|
17
17
|
Object.defineProperty(exports, "Field", {
|
|
18
18
|
enumerable: true,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-
|
|
2
|
-
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, I as InputProps, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-
|
|
1
|
+
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-KsjKSot3.cjs';
|
|
2
|
+
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, I as InputProps, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-RnnhRtD2.cjs';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
|
|
5
5
|
type FieldOrientation = "vertical" | "horizontal";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-
|
|
2
|
-
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, I as InputProps, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-
|
|
1
|
+
export { b as Field, F as Form, d as FormContext, c as FormFeedback, e as FormFeedbackProps, a as useField, u as useForm } from './FormContext-DaRyEYnF.js';
|
|
2
|
+
export { E as ErrorHandler, n as FieldInputProps, o as FieldMeta, s as FieldProps, F as FieldValidator, l as FormActions, c as FormErrors, i as FormHelpers, f as FormNotificationConfig, r as FormProps, h as FormRenderConfig, k as FormState, g as FormStyleConfig, d as FormSubmissionBehavior, e as FormSubmissionConfig, b as FormValues, I as InputProps, N as NewFormSubmissionActionConfig, S as SubmissionStatus, j as SubmitHandler, T as TouchedFields, p as UseFieldOptions, q as UseFieldReturn, U as UseFormOptions, m as UseFormReturn, a as ValidationMode, V as ValidationSchema } from './types-RnnhRtD2.js';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
|
|
5
5
|
type FieldOrientation = "vertical" | "horizontal";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { Form, FormFeedback } from './chunk-
|
|
1
|
+
export { Form, FormFeedback } from './chunk-7ZX2EUOG.js';
|
|
2
2
|
export { Field, FormContext, useField, useForm } from './chunk-MJYEXJ3U.js';
|
|
3
3
|
export { FieldDescription, FieldError, FieldGroup, Field as FieldWrapper, FieldLabel as FormFieldLabel } from './chunk-455PI4LV.js';
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
package/dist/inputs.d.cts
CHANGED
package/dist/inputs.d.ts
CHANGED
package/dist/integration.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { d as FormSubmissionBehavior, N as NewFormSubmissionActionConfig, m as UseFormReturn } from './types-
|
|
3
|
+
import { d as FormSubmissionBehavior, N as NewFormSubmissionActionConfig, m as UseFormReturn } from './types-RnnhRtD2.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @page-speed/forms - Rails API Serializer
|
package/dist/integration.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { d as FormSubmissionBehavior, N as NewFormSubmissionActionConfig, m as UseFormReturn } from './types-
|
|
3
|
+
import { d as FormSubmissionBehavior, N as NewFormSubmissionActionConfig, m as UseFormReturn } from './types-RnnhRtD2.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @page-speed/forms - Rails API Serializer
|
|
@@ -361,6 +361,10 @@ interface FormRenderConfig {
|
|
|
361
361
|
* @default "default"
|
|
362
362
|
*/
|
|
363
363
|
submitVariant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost";
|
|
364
|
+
/**
|
|
365
|
+
* Icon name for icon based submit buttons
|
|
366
|
+
*/
|
|
367
|
+
submitIconName?: string;
|
|
364
368
|
}
|
|
365
369
|
/**
|
|
366
370
|
* Form component props
|
|
@@ -361,6 +361,10 @@ interface FormRenderConfig {
|
|
|
361
361
|
* @default "default"
|
|
362
362
|
*/
|
|
363
363
|
submitVariant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost";
|
|
364
|
+
/**
|
|
365
|
+
* Icon name for icon based submit buttons
|
|
366
|
+
*/
|
|
367
|
+
submitIconName?: string;
|
|
364
368
|
}
|
|
365
369
|
/**
|
|
366
370
|
* Form component props
|
package/dist/validation.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { F as FieldValidator, a as ValidationMode, V as ValidationSchema } from './types-
|
|
1
|
+
export { F as FieldValidator, a as ValidationMode, V as ValidationSchema } from './types-RnnhRtD2.cjs';
|
|
2
2
|
export { ErrorMessageFn, ValidationRuleOptions, alpha, alphanumeric, compose, creditCard, email, integer, matches, max, maxLength, min, minLength, numeric, oneOf, pattern, phone, postalCode, required, url } from './validation-rules.cjs';
|
|
3
3
|
export { DebounceOptions, ErrorMessages, MessageTemplate, asyncValidator, crossFieldValidator, debounce, defaultMessages, getErrorMessage, messageRegistry, resetErrorMessages, setErrorMessages, when, withRaceConditionPrevention } from './validation-utils.cjs';
|
|
4
4
|
import 'react';
|
package/dist/validation.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { F as FieldValidator, a as ValidationMode, V as ValidationSchema } from './types-
|
|
1
|
+
export { F as FieldValidator, a as ValidationMode, V as ValidationSchema } from './types-RnnhRtD2.js';
|
|
2
2
|
export { ErrorMessageFn, ValidationRuleOptions, alpha, alphanumeric, compose, creditCard, email, integer, matches, max, maxLength, min, minLength, numeric, oneOf, pattern, phone, postalCode, required, url } from './validation-rules.js';
|
|
3
3
|
export { DebounceOptions, ErrorMessages, MessageTemplate, asyncValidator, crossFieldValidator, debounce, defaultMessages, getErrorMessage, messageRegistry, resetErrorMessages, setErrorMessages, when, withRaceConditionPrevention } from './validation-utils.js';
|
|
4
4
|
import 'react';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@page-speed/forms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Ultra-high-performance React form library with field-level reactivity and tree-shakable architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -118,6 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
"dependencies": {
|
|
120
120
|
"@opensite/hooks": "2.0.8",
|
|
121
|
+
"@page-speed/icon": "^0.1.0",
|
|
121
122
|
"@radix-ui/react-direction": "^1.1.1",
|
|
122
123
|
"@radix-ui/react-slot": "^1.2.4",
|
|
123
124
|
"class-variance-authority": "^0.7.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/form-feedback.tsx","../src/components/ui/button-group.tsx","../src/core/button-group-form.tsx","../src/core/Form.tsx"],"names":["React","cn","cva","React2","React3","FieldLabel","TextInput","Button","React4","FormContext"],"mappings":"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","file":"chunk-OF6ZYT7A.cjs","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface FormFeedbackProps {\n successMessage?: React.ReactNode;\n submissionError?: React.ReactNode;\n successMessageClassName?: string;\n errorMessageClassName?: string;\n}\n\nfunction renderMessage(\n message: React.ReactNode,\n fallbackClassName: string,\n className?: string,\n) {\n if (typeof message === \"string\") {\n return (\n <p\n className={cn(\n \"text-sm font-medium text-center text-balance\",\n className,\n )}\n >\n {message}\n </p>\n );\n }\n\n return <div className={cn(fallbackClassName, className)}>{message}</div>;\n}\n\nexport function FormFeedback({\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n}: FormFeedbackProps) {\n if (!successMessage && !submissionError) {\n return null;\n }\n\n return (\n <>\n {successMessage ? (\n <div\n className={cn(\n \"rounded-md border border-primary bg-primary px-4 py-3 shadow-sm\",\n successMessageClassName,\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n {renderMessage(\n successMessage,\n \"text-primary-foreground\",\n \"text-primary-foreground\",\n )}\n </div>\n ) : null}\n\n {submissionError ? (\n <div\n className={cn(\n \"rounded-md border border-destructive bg-destructive px-4 py-3 shadow-sm\",\n errorMessageClassName,\n )}\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {renderMessage(\n submissionError,\n \"text-destructive-foreground\",\n \"text-destructive-foreground\",\n )}\n </div>\n ) : null}\n </>\n );\n}\n\nFormFeedback.displayName = \"FormFeedback\";\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"../../lib/utils\"\nimport { Separator } from \"./separator\"\n\nconst buttonGroupVariants = cva(\n \"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2\",\n {\n variants: {\n orientation: {\n horizontal:\n \"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none\",\n vertical:\n \"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none\",\n },\n },\n defaultVariants: {\n orientation: \"horizontal\",\n },\n }\n)\n\nfunction ButtonGroup({\n className,\n orientation,\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof buttonGroupVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"button-group\"\n data-orientation={orientation}\n className={cn(buttonGroupVariants({ orientation }), className)}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupText({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n asChild?: boolean\n}) {\n const Comp = asChild ? Slot.Root : \"div\"\n\n return (\n <Comp\n className={cn(\n \"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupSeparator({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof Separator>) {\n return (\n <Separator\n data-slot=\"button-group-separator\"\n orientation={orientation}\n className={cn(\n \"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n ButtonGroup,\n ButtonGroupSeparator,\n ButtonGroupText,\n buttonGroupVariants,\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroup } from \"../components/ui/button-group\";\nimport { FieldLabel } from \"../components/ui/field\";\nimport { TextInput } from \"../inputs/TextInput\";\nimport type { InputProps } from \"./types\";\n\nexport type ButtonGroupFormSize = \"xs\" | \"sm\" | \"default\" | \"lg\";\n\nexport type ButtonGroupFormProps = {\n /**\n * Field name\n */\n name: string;\n /**\n * Optional label above the input\n */\n label?: React.ReactNode;\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n /**\n * Input props from form field\n */\n inputProps: InputProps<string> & {\n type?: \"text\" | \"email\" | \"password\" | \"url\" | \"tel\" | \"search\";\n };\n /**\n * Submit button label\n */\n submitLabel?: React.ReactNode;\n /**\n * Submit button size\n */\n size?: ButtonGroupFormSize;\n /**\n * Submit button variant\n */\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | null\n | undefined;\n /**\n * Whether form is submitting\n */\n isSubmitting?: boolean;\n /**\n * Additional className for the container\n */\n className?: string;\n /**\n * Additional className for the label\n */\n labelClassName?: string;\n};\n\n/**\n * ButtonGroupForm - Inline form layout with input and submit button grouped together\n *\n * Commonly used for newsletter signups and other simple single-field forms.\n * The input and button automatically adjust sizing together.\n *\n * Size mappings:\n * - xs: h-8 text-xs\n * - sm: h-9 text-sm\n * - default: h-10 text-sm\n * - lg: h-12 text-base\n *\n * @example\n * ```tsx\n * <ButtonGroupForm\n * name=\"email\"\n * placeholder=\"Enter your email\"\n * inputProps={form.getFieldProps('email')}\n * submitLabel=\"Subscribe\"\n * size=\"default\"\n * />\n * ```\n */\nexport function ButtonGroupForm({\n name,\n label,\n inputProps,\n submitLabel = \"Submit\",\n submitVariant = \"default\",\n size = \"default\",\n isSubmitting = false,\n className,\n labelClassName,\n}: ButtonGroupFormProps) {\n const inputId = `button-group-input-${name}`;\n\n // Size-specific classes for input to match button heights\n const inputSizeClasses = {\n xs: \"h-8 text-xs px-3\",\n sm: \"h-9 text-sm px-3\",\n default: \"h-10 text-sm px-4\",\n lg: \"h-12 text-base px-6\",\n };\n\n // Map button group sizes to button sizes\n const buttonSizes: Record<ButtonGroupFormSize, \"xs\" | \"sm\" | \"default\" | \"lg\"> = {\n xs: \"xs\",\n sm: \"sm\",\n default: \"default\",\n lg: \"lg\",\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {label && (\n <FieldLabel htmlFor={inputId} className={labelClassName}>\n {label}\n </FieldLabel>\n )}\n <ButtonGroup>\n <TextInput\n {...inputProps}\n id={inputId}\n className={cn(\n inputSizeClasses[size],\n \"border-r-0 rounded-r-none focus-visible:z-10\",\n inputProps.className,\n )}\n />\n <Button\n size={buttonSizes[size]}\n type=\"submit\"\n variant={submitVariant}\n disabled={isSubmitting}\n className=\"rounded-l-none\"\n >\n {submitLabel}\n </Button>\n </ButtonGroup>\n </div>\n );\n}\n\nButtonGroupForm.displayName = \"ButtonGroupForm\";\n","\"use client\";\n\nimport * as React from \"react\";\nimport { FormContext } from \"./FormContext\";\nimport type { FormProps, FormValues } from \"./types\";\nimport { FormFeedback } from \"./form-feedback\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroupForm } from \"./button-group-form\";\nimport type { FormFieldConfig } from \"../integration/form-field-types\";\n\n/**\n * Form - Progressive enhancement form component\n *\n * Provides form context to child components and handles form submission.\n * Supports progressive enhancement with server-side fallback.\n *\n * Features:\n * - Provides FormContext for useField hook\n * - Handles form submission with validation\n * - Progressive enhancement support (works without JavaScript)\n * - Accessible form semantics\n *\n * @example\n * ```tsx\n * const form = useForm({\n * initialValues: { email: '' },\n * onSubmit: async (values) => {\n * await submitForm(values);\n * },\n * });\n *\n * return (\n * <Form form={form} action=\"/api/submit\" method=\"post\">\n * <input {...form.getFieldProps('email')} />\n * <button type=\"submit\">Submit</button>\n * </Form>\n * );\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/form\n */\nexport function Form<T extends FormValues = FormValues>({\n form,\n children,\n fields,\n className,\n action,\n method,\n noValidate = true,\n submissionConfig,\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n onNewSubmission,\n notificationConfig,\n styleConfig,\n formConfig,\n ...props\n}: FormProps<T> & React.FormHTMLAttributes<HTMLFormElement>) {\n // Wrap handleSubmit to catch any unhandled rejections\n const handleFormSubmit = React.useCallback(\n async (e: React.FormEvent) => {\n try {\n await form.handleSubmit(e);\n } catch {\n // Error is already handled by useForm, just prevent unhandled rejection\n // The form status and errors are already set by useForm's error handling\n }\n },\n [form],\n );\n\n const resolvedClassName = className ?? styleConfig?.formClassName;\n const resolvedAction = action ?? formConfig?.endpoint;\n const resolvedMethod = method ?? formConfig?.method ?? \"post\";\n const resolvedSubmissionConfig = submissionConfig ?? formConfig?.submissionConfig;\n const resolvedSuccessMessage =\n successMessage ?? notificationConfig?.successMessage;\n const resolvedSubmissionError =\n submissionError ?? notificationConfig?.submissionError;\n const resolvedSuccessMessageClassName =\n successMessageClassName ?? styleConfig?.successMessageClassName;\n const resolvedErrorMessageClassName =\n errorMessageClassName ?? styleConfig?.errorMessageClassName;\n\n const behavior = resolvedSubmissionConfig?.behavior || \"showConfirmation\";\n\n const shouldManageSubmissionUi =\n resolvedSubmissionConfig !== undefined ||\n resolvedSuccessMessage !== undefined ||\n resolvedSuccessMessageClassName !== undefined ||\n resolvedErrorMessageClassName !== undefined ||\n resolvedSubmissionError != null ||\n onNewSubmission !== undefined;\n\n const hasSubmissionError = Boolean(resolvedSubmissionError);\n\n const isSubmissionSuccessful =\n shouldManageSubmissionUi &&\n form.status === \"success\" &&\n !hasSubmissionError;\n\n const defaultSuccessMessage =\n behavior === \"redirect\"\n ? \"Form submitted successfully. Redirecting...\"\n : \"Thank you. Your form has been submitted successfully.\";\n\n const finalSuccessMessage = resolvedSuccessMessage ?? defaultSuccessMessage;\n\n const shouldRenderCustomComponent =\n isSubmissionSuccessful &&\n behavior === \"renderCustomComponent\" &&\n Boolean(resolvedSubmissionConfig?.customComponent);\n\n const newSubmissionAction = resolvedSubmissionConfig?.newFormSubmissionAction;\n\n const showNewSubmissionAction =\n isSubmissionSuccessful &&\n (typeof newSubmissionAction?.enable === \"boolean\"\n ? newSubmissionAction.enable\n : Boolean(newSubmissionAction?.label));\n\n const newSubmissionLabel =\n newSubmissionAction?.label ?? \"Submit another response\";\n\n const handleNewSubmission = React.useCallback(() => {\n form.resetForm();\n onNewSubmission?.();\n }, [form, onNewSubmission]);\n\n // Check if we should use button-group layout\n const formLayout = formConfig?.formLayout ?? \"standard\";\n const isButtonGroupLayout = formLayout === \"button-group\";\n const hasTextField = fields && fields.length === 1 && fields[0] &&\n [\"text\", \"email\", \"password\", \"url\", \"tel\", \"search\"].includes(fields[0].type);\n const shouldUseButtonGroup = isButtonGroupLayout && hasTextField;\n\n // Render button-group layout if conditions are met\n const buttonGroupContent = React.useMemo(() => {\n if (!shouldUseButtonGroup || !fields || fields.length === 0) return null;\n\n const field = fields[0] as FormFieldConfig;\n const fieldProps = form.getFieldProps(field.name);\n\n return (\n <ButtonGroupForm\n name={field.name}\n label={field.label}\n inputProps={{\n name: fieldProps.name,\n value: fieldProps.value as string,\n onChange: fieldProps.onChange as (value: string) => void,\n onBlur: fieldProps.onBlur,\n type: field.type as \"text\" | \"email\" | \"password\" | \"url\" | \"tel\" | \"search\",\n placeholder: field.placeholder,\n required: field.required,\n disabled: field.disabled,\n }}\n submitLabel={formConfig?.submitLabel}\n submitVariant={formConfig?.submitVariant}\n size={formConfig?.buttonGroupSize}\n isSubmitting={form.isSubmitting}\n />\n );\n }, [shouldUseButtonGroup, fields, form, formConfig]);\n\n return (\n <FormContext.Provider value={form}>\n <form\n onSubmit={handleFormSubmit}\n action={resolvedAction}\n method={resolvedMethod}\n noValidate={noValidate}\n className={resolvedClassName}\n {...props}\n >\n {isSubmissionSuccessful ? (\n <div className=\"space-y-4\">\n {shouldRenderCustomComponent ? (\n resolvedSubmissionConfig?.customComponent\n ) : (\n <FormFeedback\n successMessage={finalSuccessMessage}\n successMessageClassName={resolvedSuccessMessageClassName}\n />\n )}\n\n {showNewSubmissionAction ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleNewSubmission}\n >\n {newSubmissionLabel}\n </Button>\n ) : null}\n </div>\n ) : (\n <>\n {shouldUseButtonGroup ? buttonGroupContent : children}\n {resolvedSubmissionError ? (\n <div className=\"mt-4\">\n <FormFeedback\n submissionError={resolvedSubmissionError}\n errorMessageClassName={resolvedErrorMessageClassName}\n />\n </div>\n ) : null}\n </>\n )}\n </form>\n </FormContext.Provider>\n );\n}\n\nForm.displayName = \"Form\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/form-feedback.tsx","../src/components/ui/button-group.tsx","../src/core/button-group-form.tsx","../src/core/Form.tsx"],"names":["React","React2","React3"],"mappings":"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","file":"chunk-TOPOO33Z.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface FormFeedbackProps {\n successMessage?: React.ReactNode;\n submissionError?: React.ReactNode;\n successMessageClassName?: string;\n errorMessageClassName?: string;\n}\n\nfunction renderMessage(\n message: React.ReactNode,\n fallbackClassName: string,\n className?: string,\n) {\n if (typeof message === \"string\") {\n return (\n <p\n className={cn(\n \"text-sm font-medium text-center text-balance\",\n className,\n )}\n >\n {message}\n </p>\n );\n }\n\n return <div className={cn(fallbackClassName, className)}>{message}</div>;\n}\n\nexport function FormFeedback({\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n}: FormFeedbackProps) {\n if (!successMessage && !submissionError) {\n return null;\n }\n\n return (\n <>\n {successMessage ? (\n <div\n className={cn(\n \"rounded-md border border-primary bg-primary px-4 py-3 shadow-sm\",\n successMessageClassName,\n )}\n role=\"status\"\n aria-live=\"polite\"\n >\n {renderMessage(\n successMessage,\n \"text-primary-foreground\",\n \"text-primary-foreground\",\n )}\n </div>\n ) : null}\n\n {submissionError ? (\n <div\n className={cn(\n \"rounded-md border border-destructive bg-destructive px-4 py-3 shadow-sm\",\n errorMessageClassName,\n )}\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {renderMessage(\n submissionError,\n \"text-destructive-foreground\",\n \"text-destructive-foreground\",\n )}\n </div>\n ) : null}\n </>\n );\n}\n\nFormFeedback.displayName = \"FormFeedback\";\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"../../lib/utils\"\nimport { Separator } from \"./separator\"\n\nconst buttonGroupVariants = cva(\n \"flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2\",\n {\n variants: {\n orientation: {\n horizontal:\n \"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none\",\n vertical:\n \"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none\",\n },\n },\n defaultVariants: {\n orientation: \"horizontal\",\n },\n }\n)\n\nfunction ButtonGroup({\n className,\n orientation,\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof buttonGroupVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"button-group\"\n data-orientation={orientation}\n className={cn(buttonGroupVariants({ orientation }), className)}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupText({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n asChild?: boolean\n}) {\n const Comp = asChild ? Slot.Root : \"div\"\n\n return (\n <Comp\n className={cn(\n \"bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ButtonGroupSeparator({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof Separator>) {\n return (\n <Separator\n data-slot=\"button-group-separator\"\n orientation={orientation}\n className={cn(\n \"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n ButtonGroup,\n ButtonGroupSeparator,\n ButtonGroupText,\n buttonGroupVariants,\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroup } from \"../components/ui/button-group\";\nimport { FieldLabel } from \"../components/ui/field\";\nimport { TextInput } from \"../inputs/TextInput\";\nimport type { InputProps } from \"./types\";\n\nexport type ButtonGroupFormSize = \"xs\" | \"sm\" | \"default\" | \"lg\";\n\nexport type ButtonGroupFormProps = {\n /**\n * Field name\n */\n name: string;\n /**\n * Optional label above the input\n */\n label?: React.ReactNode;\n /**\n * Placeholder text for the input\n */\n placeholder?: string;\n /**\n * Input props from form field\n */\n inputProps: InputProps<string> & {\n type?: \"text\" | \"email\" | \"password\" | \"url\" | \"tel\" | \"search\";\n };\n /**\n * Submit button label\n */\n submitLabel?: React.ReactNode;\n /**\n * Submit button size\n */\n size?: ButtonGroupFormSize;\n /**\n * Submit button variant\n */\n submitVariant?:\n | \"link\"\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | null\n | undefined;\n /**\n * Whether form is submitting\n */\n isSubmitting?: boolean;\n /**\n * Additional className for the container\n */\n className?: string;\n /**\n * Additional className for the label\n */\n labelClassName?: string;\n};\n\n/**\n * ButtonGroupForm - Inline form layout with input and submit button grouped together\n *\n * Commonly used for newsletter signups and other simple single-field forms.\n * The input and button automatically adjust sizing together.\n *\n * Size mappings:\n * - xs: h-8 text-xs\n * - sm: h-9 text-sm\n * - default: h-10 text-sm\n * - lg: h-12 text-base\n *\n * @example\n * ```tsx\n * <ButtonGroupForm\n * name=\"email\"\n * placeholder=\"Enter your email\"\n * inputProps={form.getFieldProps('email')}\n * submitLabel=\"Subscribe\"\n * size=\"default\"\n * />\n * ```\n */\nexport function ButtonGroupForm({\n name,\n label,\n inputProps,\n submitLabel = \"Submit\",\n submitVariant = \"default\",\n size = \"default\",\n isSubmitting = false,\n className,\n labelClassName,\n}: ButtonGroupFormProps) {\n const inputId = `button-group-input-${name}`;\n\n // Size-specific classes for input to match button heights\n const inputSizeClasses = {\n xs: \"h-8 text-xs px-3\",\n sm: \"h-9 text-sm px-3\",\n default: \"h-10 text-sm px-4\",\n lg: \"h-12 text-base px-6\",\n };\n\n // Map button group sizes to button sizes\n const buttonSizes: Record<ButtonGroupFormSize, \"xs\" | \"sm\" | \"default\" | \"lg\"> = {\n xs: \"xs\",\n sm: \"sm\",\n default: \"default\",\n lg: \"lg\",\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {label && (\n <FieldLabel htmlFor={inputId} className={labelClassName}>\n {label}\n </FieldLabel>\n )}\n <ButtonGroup>\n <TextInput\n {...inputProps}\n id={inputId}\n className={cn(\n inputSizeClasses[size],\n \"border-r-0 rounded-r-none focus-visible:z-10\",\n inputProps.className,\n )}\n />\n <Button\n size={buttonSizes[size]}\n type=\"submit\"\n variant={submitVariant}\n disabled={isSubmitting}\n className=\"rounded-l-none\"\n >\n {submitLabel}\n </Button>\n </ButtonGroup>\n </div>\n );\n}\n\nButtonGroupForm.displayName = \"ButtonGroupForm\";\n","\"use client\";\n\nimport * as React from \"react\";\nimport { FormContext } from \"./FormContext\";\nimport type { FormProps, FormValues } from \"./types\";\nimport { FormFeedback } from \"./form-feedback\";\nimport { Button } from \"../components/ui/button\";\nimport { ButtonGroupForm } from \"./button-group-form\";\nimport type { FormFieldConfig } from \"../integration/form-field-types\";\n\n/**\n * Form - Progressive enhancement form component\n *\n * Provides form context to child components and handles form submission.\n * Supports progressive enhancement with server-side fallback.\n *\n * Features:\n * - Provides FormContext for useField hook\n * - Handles form submission with validation\n * - Progressive enhancement support (works without JavaScript)\n * - Accessible form semantics\n *\n * @example\n * ```tsx\n * const form = useForm({\n * initialValues: { email: '' },\n * onSubmit: async (values) => {\n * await submitForm(values);\n * },\n * });\n *\n * return (\n * <Form form={form} action=\"/api/submit\" method=\"post\">\n * <input {...form.getFieldProps('email')} />\n * <button type=\"submit\">Submit</button>\n * </Form>\n * );\n * ```\n *\n * @see https://opensite.ai/developers/page-speed/forms/form\n */\nexport function Form<T extends FormValues = FormValues>({\n form,\n children,\n fields,\n className,\n action,\n method,\n noValidate = true,\n submissionConfig,\n successMessage,\n submissionError,\n successMessageClassName,\n errorMessageClassName,\n onNewSubmission,\n notificationConfig,\n styleConfig,\n formConfig,\n ...props\n}: FormProps<T> & React.FormHTMLAttributes<HTMLFormElement>) {\n // Wrap handleSubmit to catch any unhandled rejections\n const handleFormSubmit = React.useCallback(\n async (e: React.FormEvent) => {\n try {\n await form.handleSubmit(e);\n } catch {\n // Error is already handled by useForm, just prevent unhandled rejection\n // The form status and errors are already set by useForm's error handling\n }\n },\n [form],\n );\n\n const resolvedClassName = className ?? styleConfig?.formClassName;\n const resolvedAction = action ?? formConfig?.endpoint;\n const resolvedMethod = method ?? formConfig?.method ?? \"post\";\n const resolvedSubmissionConfig = submissionConfig ?? formConfig?.submissionConfig;\n const resolvedSuccessMessage =\n successMessage ?? notificationConfig?.successMessage;\n const resolvedSubmissionError =\n submissionError ?? notificationConfig?.submissionError;\n const resolvedSuccessMessageClassName =\n successMessageClassName ?? styleConfig?.successMessageClassName;\n const resolvedErrorMessageClassName =\n errorMessageClassName ?? styleConfig?.errorMessageClassName;\n\n const behavior = resolvedSubmissionConfig?.behavior || \"showConfirmation\";\n\n const shouldManageSubmissionUi =\n resolvedSubmissionConfig !== undefined ||\n resolvedSuccessMessage !== undefined ||\n resolvedSuccessMessageClassName !== undefined ||\n resolvedErrorMessageClassName !== undefined ||\n resolvedSubmissionError != null ||\n onNewSubmission !== undefined;\n\n const hasSubmissionError = Boolean(resolvedSubmissionError);\n\n const isSubmissionSuccessful =\n shouldManageSubmissionUi &&\n form.status === \"success\" &&\n !hasSubmissionError;\n\n const defaultSuccessMessage =\n behavior === \"redirect\"\n ? \"Form submitted successfully. Redirecting...\"\n : \"Thank you. Your form has been submitted successfully.\";\n\n const finalSuccessMessage = resolvedSuccessMessage ?? defaultSuccessMessage;\n\n const shouldRenderCustomComponent =\n isSubmissionSuccessful &&\n behavior === \"renderCustomComponent\" &&\n Boolean(resolvedSubmissionConfig?.customComponent);\n\n const newSubmissionAction = resolvedSubmissionConfig?.newFormSubmissionAction;\n\n const showNewSubmissionAction =\n isSubmissionSuccessful &&\n (typeof newSubmissionAction?.enable === \"boolean\"\n ? newSubmissionAction.enable\n : Boolean(newSubmissionAction?.label));\n\n const newSubmissionLabel =\n newSubmissionAction?.label ?? \"Submit another response\";\n\n const handleNewSubmission = React.useCallback(() => {\n form.resetForm();\n onNewSubmission?.();\n }, [form, onNewSubmission]);\n\n // Check if we should use button-group layout\n const formLayout = formConfig?.formLayout ?? \"standard\";\n const isButtonGroupLayout = formLayout === \"button-group\";\n const hasTextField = fields && fields.length === 1 && fields[0] &&\n [\"text\", \"email\", \"password\", \"url\", \"tel\", \"search\"].includes(fields[0].type);\n const shouldUseButtonGroup = isButtonGroupLayout && hasTextField;\n\n // Render button-group layout if conditions are met\n const buttonGroupContent = React.useMemo(() => {\n if (!shouldUseButtonGroup || !fields || fields.length === 0) return null;\n\n const field = fields[0] as FormFieldConfig;\n const fieldProps = form.getFieldProps(field.name);\n\n return (\n <ButtonGroupForm\n name={field.name}\n label={field.label}\n inputProps={{\n name: fieldProps.name,\n value: fieldProps.value as string,\n onChange: fieldProps.onChange as (value: string) => void,\n onBlur: fieldProps.onBlur,\n type: field.type as \"text\" | \"email\" | \"password\" | \"url\" | \"tel\" | \"search\",\n placeholder: field.placeholder,\n required: field.required,\n disabled: field.disabled,\n }}\n submitLabel={formConfig?.submitLabel}\n submitVariant={formConfig?.submitVariant}\n size={formConfig?.buttonGroupSize}\n isSubmitting={form.isSubmitting}\n />\n );\n }, [shouldUseButtonGroup, fields, form, formConfig]);\n\n return (\n <FormContext.Provider value={form}>\n <form\n onSubmit={handleFormSubmit}\n action={resolvedAction}\n method={resolvedMethod}\n noValidate={noValidate}\n className={resolvedClassName}\n {...props}\n >\n {isSubmissionSuccessful ? (\n <div className=\"space-y-4\">\n {shouldRenderCustomComponent ? (\n resolvedSubmissionConfig?.customComponent\n ) : (\n <FormFeedback\n successMessage={finalSuccessMessage}\n successMessageClassName={resolvedSuccessMessageClassName}\n />\n )}\n\n {showNewSubmissionAction ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleNewSubmission}\n >\n {newSubmissionLabel}\n </Button>\n ) : null}\n </div>\n ) : (\n <>\n {shouldUseButtonGroup ? buttonGroupContent : children}\n {resolvedSubmissionError ? (\n <div className=\"mt-4\">\n <FormFeedback\n submissionError={resolvedSubmissionError}\n errorMessageClassName={resolvedErrorMessageClassName}\n />\n </div>\n ) : null}\n </>\n )}\n </form>\n </FormContext.Provider>\n );\n}\n\nForm.displayName = \"Form\";\n"]}
|