@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
1 lines • 11 kB
Source Map (JSON)
{"version":3,"file":"field-error.cjs","sources":["../../../../src/components/forms/field-error.tsx"],"sourcesContent":["/**\n * @frank-auth/react - Field Error Component\n *\n * Displays field validation errors with consistent styling and animation.\n * Integrates with form validation system and supports organization theming.\n */\n\n\"use client\";\n\nimport { HelperText } from \"@/components/ui/input/input\";\nimport { useTheme } from \"@/theme/context\";\nimport type { StyledProps } from \"@/theme/styled\";\nimport type { SizeT } from \"@/types\";\nimport styled from \"@emotion/styled\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport React from \"react\";\n\n// ============================================================================\n// Field Error Interface\n// ============================================================================\n\nexport interface FieldErrorProps {\n\t/**\n\t * The error message to display\n\t */\n\terror?: string | string[] | null;\n\n\t/**\n\t * Field name for accessibility\n\t */\n\tfieldName?: string;\n\n\t/**\n\t * Whether to show error immediately or animate in\n\t */\n\timmediate?: boolean;\n\n\t/**\n\t * Custom className for styling\n\t */\n\tclassName?: string;\n\n\t/**\n\t * Custom error icon\n\t */\n\ticon?: React.ReactNode;\n\n\t/**\n\t * Whether to show multiple errors or just the first one\n\t */\n\tshowMultiple?: boolean;\n\n\t/**\n\t * Custom styling variant\n\t */\n\tvariant?: \"default\" | \"inline\" | \"tooltip\";\n\n\t/**\n\t * Size variant\n\t */\n\tsize?: SizeT;\n}\n\n// ============================================================================\n// Styled Components\n// ============================================================================\n\nconst ErrorContainer = styled(motion.div)<\n\tStyledProps & {\n\t\tsize: SizeT;\n\t\tvariant: \"default\" | \"inline\" | \"tooltip\";\n\t}\n>`\n\tdisplay: flex;\n\talign-items: flex-start;\n\tgap: ${(props) => props.theme.spacing[1]};\n\tcolor: ${(props) => props.theme.colors.danger[600]};\n\n\tfont-size: ${(props) => {\n\t\tswitch (props.size) {\n\t\t\tcase \"sm\":\n\t\t\t\treturn props.theme.fontSizes.xs;\n\t\t\tcase \"lg\":\n\t\t\t\treturn props.theme.fontSizes.base;\n\t\t\tdefault:\n\t\t\t\treturn props.theme.fontSizes.sm;\n\t\t}\n\t}};\n\n\t${(props) => {\n\t\tswitch (props.variant) {\n\t\t\tcase \"inline\":\n\t\t\t\treturn `\n\t\t\t\t\tmargin-left: ${props.theme.spacing[2]};\n\t\t\t\t\tdisplay: inline-flex;\n\t\t\t\t`;\n\t\t\tcase \"tooltip\":\n\t\t\t\treturn `\n\t\t\t\t\tposition: absolute;\n\t\t\t\t\tz-index: ${props.theme.zIndex.tooltip};\n\t\t\t\t\tmargin-top: ${props.theme.spacing[1]};\n\t\t\t\t`;\n\t\t\tdefault:\n\t\t\t\t// Fixed: Removed margin-top since parent container handles spacing with gap\n\t\t\t\treturn \"\";\n\t\t}\n\t}}\n`;\n\nconst ErrorIcon = styled.span<StyledProps>`\n\tcolor: ${(props) => props.theme.colors.danger[500]};\n\tmargin-top: ${(props) => props.theme.spacing[0.5] || \"0.125rem\"};\n\tflex-shrink: 0;\n\n\tsvg {\n\t\twidth: ${(props) => props.theme.spacing[4]};\n\t\theight: ${(props) => props.theme.spacing[4]};\n\t}\n`;\n\nconst ErrorContent = styled.div<StyledProps>`\n\tflex: 1;\n\tmin-width: 0;\n`;\n\nconst ErrorMessage = styled.div<StyledProps & { hasMargin?: boolean }>`\n\t${(props) => props.hasMargin && `margin-top: ${props.theme.spacing[1]};`}\n`;\n\nconst ErrorText = styled.span<StyledProps>`\n\tdisplay: block;\n\tcolor: inherit;\n\tword-break: break-words;\n`;\n\n// ============================================================================\n// Field Error Component\n// ============================================================================\n\nexport function FieldError({\n\terror,\n\tfieldName,\n\timmediate = false,\n\tclassName = \"\",\n\ticon,\n\tshowMultiple = false,\n\tvariant = \"default\",\n\tsize = \"md\",\n}: FieldErrorProps) {\n\tconst { theme } = useTheme();\n\n\t// Normalize error to array\n\tconst errors = React.useMemo(() => {\n\t\tif (!error) return [];\n\t\tif (Array.isArray(error)) return error.filter(Boolean);\n\t\treturn [error];\n\t}, [error]);\n\n\t// Don't render if no errors\n\tif (errors.length === 0) return null;\n\n\t// Display errors (show all if showMultiple is true, otherwise just first)\n\tconst displayErrors = showMultiple ? errors : errors.slice(0, 1);\n\n\t// Animation variants\n\tconst animationVariants = {\n\t\tinitial: { opacity: 0, y: -10, height: 0 },\n\t\tanimate: {\n\t\t\topacity: 1,\n\t\t\ty: 0,\n\t\t\theight: \"auto\",\n\t\t\ttransition: { duration: 0.2, ease: \"easeOut\" },\n\t\t},\n\t\texit: {\n\t\t\topacity: 0,\n\t\t\ty: -10,\n\t\t\theight: 0,\n\t\t\ttransition: { duration: 0.15, ease: \"easeIn\" },\n\t\t},\n\t};\n\n\t// Default error icon\n\tconst defaultIcon = (\n\t\t<svg\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path\n\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\tstrokeWidth={2}\n\t\t\t\td=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n\t\t\t/>\n\t\t</svg>\n\t);\n\n\tconst errorIcon = icon || defaultIcon;\n\n\treturn (\n\t\t<AnimatePresence mode=\"wait\">\n\t\t\t{displayErrors.length > 0 && (\n\t\t\t\t<ErrorContainer\n\t\t\t\t\ttheme={theme}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tvariant={variant}\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tinitial={immediate ? \"animate\" : \"initial\"}\n\t\t\t\t\tanimate=\"animate\"\n\t\t\t\t\texit=\"exit\"\n\t\t\t\t\tvariants={animationVariants}\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\taria-live=\"polite\"\n\t\t\t\t\taria-relevant=\"all\"\n\t\t\t\t>\n\t\t\t\t\t{errorIcon && <ErrorIcon theme={theme}>{errorIcon}</ErrorIcon>}\n\n\t\t\t\t\t<ErrorContent theme={theme}>\n\t\t\t\t\t\t{displayErrors.map((errorMessage, index) => (\n\t\t\t\t\t\t\t<ErrorMessage\n\t\t\t\t\t\t\t\tkey={`${fieldName}-error-${index}`}\n\t\t\t\t\t\t\t\ttheme={theme}\n\t\t\t\t\t\t\t\thasMargin={index > 0}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/*<ErrorText theme={theme}>{errorMessage}</ErrorText>*/}\n\t\t\t\t\t\t\t\t<HelperText theme={theme} isError={!!errorMessage}>\n\t\t\t\t\t\t\t\t\t{errorMessage}\n\t\t\t\t\t\t\t\t</HelperText>\n\t\t\t\t\t\t\t</ErrorMessage>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ErrorContent>\n\t\t\t\t</ErrorContainer>\n\t\t\t)}\n\t\t</AnimatePresence>\n\t);\n}\n\n// ============================================================================\n// Field Error Hook\n// ============================================================================\n\n/**\n * Hook for managing field error state\n */\nexport function useFieldError(fieldName?: string) {\n\tconst [error, setError] = React.useState<string | string[] | null>(null);\n\tconst [touched, setTouched] = React.useState(false);\n\n\tconst showError = React.useMemo(() => {\n\t\treturn touched && !!error;\n\t}, [touched, error]);\n\n\tconst clearError = React.useCallback(() => {\n\t\tsetError(null);\n\t}, []);\n\n\tconst setFieldError = React.useCallback(\n\t\t(newError: string | string[] | null) => {\n\t\t\tsetError(newError);\n\t\t},\n\t\t[],\n\t);\n\n\tconst touch = React.useCallback(() => {\n\t\tsetTouched(true);\n\t}, []);\n\n\tconst reset = React.useCallback(() => {\n\t\tsetError(null);\n\t\tsetTouched(false);\n\t}, []);\n\n\treturn {\n\t\terror,\n\t\tshowError,\n\t\ttouched,\n\t\tsetError: setFieldError,\n\t\tclearError,\n\t\ttouch,\n\t\treset,\n\t\tfieldName,\n\t};\n}\n\n// ============================================================================\n// Export\n// ============================================================================\n\nexport default FieldError;\n"],"names":["ErrorContainer","styled","motion","props","ErrorIcon","ErrorContent","ErrorMessage","FieldError","error","fieldName","immediate","className","icon","showMultiple","variant","size","theme","useTheme","errors","React","displayErrors","animationVariants","errorIcon","jsx","AnimatePresence","jsxs","errorMessage","index","HelperText","useFieldError","setError","touched","setTouched","showError","clearError","setFieldError","newError","touch","reset","field_error_default"],"mappings":"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"}