@openzeppelin/ui-components 1.1.0 → 1.2.0

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.
@@ -167,4 +167,4 @@ function ErrorMessage({ error, id, message, className = "" }) {
167
167
 
168
168
  //#endregion
169
169
  export { getValidationStateClasses as a, isDuplicateMapKey as c, INTEGER_HTML_PATTERN as d, INTEGER_INPUT_PATTERN as f, getErrorMessage as i, validateField as l, createValidationResult as n, handleValidationError as o, INTEGER_PATTERN as p, formatValidationError as r, hasFieldError as s, ErrorMessage as t, validateMapEntries as u };
170
- //# sourceMappingURL=ErrorMessage-DyO9ZWWz.js.map
170
+ //# sourceMappingURL=ErrorMessage-BqOEJm84.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorMessage-BqOEJm84.mjs","names":[],"sources":["../src/components/fields/utils/integerValidation.ts","../src/components/fields/utils/validation.ts","../src/components/fields/utils/ErrorMessage.tsx"],"sourcesContent":["/**\n * Shared integer validation patterns for BigInt fields and validation utilities.\n *\n * These patterns ensure consistent validation across the application.\n */\n\n/**\n * Integer validation pattern - requires at least one digit\n * Used for validation to ensure complete integers are entered\n * Matches: -123, 0, 456\n * Does not match: -, abc, 12.3\n */\nexport const INTEGER_PATTERN = /^-?\\d+$/;\n\n/**\n * Integer input pattern - allows partial input during typing\n * Used during input to allow users to type minus sign first\n * Matches: -, -1, 123, (empty string)\n * Does not match: abc, 12.3\n */\nexport const INTEGER_INPUT_PATTERN = /^-?\\d*$/;\n\n/**\n * HTML pattern attribute for integer inputs\n * Must use [0-9] instead of \\d for HTML5 pattern attribute\n */\nexport const INTEGER_HTML_PATTERN = '-?[0-9]*';\n","/**\n * Validation and error handling utilities for form field components\n */\nimport { FieldError } from 'react-hook-form';\n\nimport { FieldValidation, MapEntry } from '@openzeppelin/ui-types';\n\nimport { INTEGER_PATTERN } from './integerValidation';\n\n/**\n * Determines if a field has an error\n */\nexport function hasFieldError(error: FieldError | undefined): boolean {\n return !!error;\n}\n\n/**\n * Gets appropriate error message from field error\n */\nexport function getErrorMessage(error: FieldError | undefined): string | undefined {\n if (!error) return undefined;\n\n return error.message || 'This field is invalid';\n}\n\n/**\n * Formats validation error messages for display\n */\nexport function formatValidationError(\n error: FieldError | undefined,\n fieldName?: string\n): string | undefined {\n if (!error) return undefined;\n\n const defaultMessage = fieldName ? `${fieldName} is invalid` : 'This field is invalid';\n\n return error.message || defaultMessage;\n}\n\n/**\n * Generates common CSS classes for field validation states\n */\nexport function getValidationStateClasses(\n error: FieldError | undefined,\n touched?: boolean\n): string {\n if (error) {\n return 'border-destructive focus:border-destructive focus:ring-destructive/30';\n }\n\n if (touched) {\n return 'border-success focus:border-success focus:ring-success/30';\n }\n\n return '';\n}\n\n/**\n * Helper for handling form validation errors with React Hook Form\n */\nexport function handleValidationError(\n error: FieldError | undefined,\n id: string\n): {\n errorId: string;\n errorMessage: string | undefined;\n hasError: boolean;\n validationClasses: string;\n} {\n const hasError = hasFieldError(error);\n const errorMessage = getErrorMessage(error);\n const errorId = `${id}-error`;\n const validationClasses = getValidationStateClasses(error);\n\n return {\n errorId,\n errorMessage,\n hasError,\n validationClasses,\n };\n}\n\n/**\n * Creates a validation result object for field components\n */\nexport function createValidationResult(\n id: string,\n error: FieldError | undefined,\n touched?: boolean\n): {\n hasError: boolean;\n errorMessage: string | undefined;\n errorId: string;\n validationClasses: string;\n 'aria-invalid': boolean;\n 'aria-errormessage'?: string;\n} {\n const hasError = hasFieldError(error);\n const errorMessage = getErrorMessage(error);\n const errorId = `${id}-error`;\n const validationClasses = getValidationStateClasses(error, touched);\n\n return {\n hasError,\n errorMessage,\n errorId,\n validationClasses,\n 'aria-invalid': hasError,\n 'aria-errormessage': hasError ? errorId : undefined,\n };\n}\n\n/**\n * Generic field validation function that can be used to validate any field type\n * based on common validation criteria\n */\nexport function validateField(value: unknown, validation?: FieldValidation): string | boolean {\n // Return true if no validation rules are provided\n if (!validation) return true;\n\n // Check if required but empty\n if (validation.required && (value === undefined || value === null || value === '')) {\n return typeof validation.required === 'boolean'\n ? 'This field is required'\n : 'This field is required'; // Always return the standard message for required fields\n }\n\n // Skip other validations if value is empty and not required\n if (value === undefined || value === null || value === '') {\n return true;\n }\n\n // Validate string length\n if (typeof value === 'string') {\n if (validation.minLength && value.length < validation.minLength) {\n return `Minimum length is ${validation.minLength} characters`;\n }\n\n if (validation.maxLength && value.length > validation.maxLength) {\n return `Maximum length is ${validation.maxLength} characters`;\n }\n\n // Validate pattern\n if (validation.pattern) {\n const pattern =\n typeof validation.pattern === 'string'\n ? new RegExp(validation.pattern)\n : validation.pattern;\n\n if (!pattern.test(value)) {\n // Provide more specific error message for integer validation\n if (!INTEGER_PATTERN.test(value) && /\\d/.test(value)) {\n return 'Value must be a valid integer (no decimals)';\n }\n return 'Value does not match the required pattern';\n }\n }\n }\n\n // Validate number range\n if (typeof value === 'number' || (typeof value === 'string' && !isNaN(Number(value)))) {\n const numValue = typeof value === 'number' ? value : Number(value);\n\n if (validation.min !== undefined && numValue < validation.min) {\n return `Minimum value is ${validation.min}`;\n }\n\n if (validation.max !== undefined && numValue > validation.max) {\n return `Maximum value is ${validation.max}`;\n }\n }\n\n // Check field conditions if defined\n if (validation.conditions && validation.conditions.length > 0) {\n // Note: This would need the current form values to evaluate conditions\n // This is intended to be used with React Hook Form's validation context\n // Implementation would depend on how conditions are evaluated in the form context\n }\n\n // If all validations pass\n return true;\n}\n\n/**\n * Map validation utilities\n */\n\n/**\n * Checks if a map entry at the given index has a duplicate key\n * @param entries - Array of map entries\n * @param currentIndex - Index of the entry to check\n * @returns true if the key at currentIndex is duplicated elsewhere in the array\n */\nexport function isDuplicateMapKey(entries: MapEntry[], currentIndex: number): boolean {\n if (!Array.isArray(entries) || entries.length <= 1) {\n return false;\n }\n\n const currentKeyValue = entries[currentIndex]?.key;\n\n // Don't consider empty keys as duplicates\n if (!currentKeyValue || currentKeyValue === '') {\n return false;\n }\n\n // Prefer strict equality to avoid cross-type false positives (e.g., 123 vs \"123\")\n // Treat empty string as non-duplicate as above.\n return entries.some((entry: MapEntry, i: number) => {\n if (i === currentIndex) return false;\n const key = entry?.key;\n if (key === '') return false;\n // If both are strings, compare strings\n if (typeof key === 'string' && typeof currentKeyValue === 'string') {\n return key === currentKeyValue;\n }\n // If both are numbers, compare numbers\n if (typeof key === 'number' && typeof currentKeyValue === 'number') {\n return Number.isNaN(key) ? Number.isNaN(currentKeyValue) : key === currentKeyValue;\n }\n // If both are booleans\n if (typeof key === 'boolean' && typeof currentKeyValue === 'boolean') {\n return key === currentKeyValue;\n }\n // For objects (including dates) fall back to reference equality\n if (\n typeof key === 'object' &&\n key !== null &&\n typeof currentKeyValue === 'object' &&\n currentKeyValue !== null\n ) {\n return key === currentKeyValue;\n }\n // Otherwise, consider different types as different keys\n return false;\n });\n}\n\n/**\n * Validates an array of map entries for duplicate keys\n * @param entries - Array of map entries to validate\n * @returns Validation error message if duplicates found, otherwise undefined\n */\nexport function validateMapEntries(entries: MapEntry[]): string | undefined {\n if (!Array.isArray(entries) || entries.length <= 1) {\n return undefined;\n }\n\n const keys = entries\n .map((entry) => entry?.key)\n .filter((key) => key !== undefined && key !== null && key !== '');\n\n const keyStrings = keys.map((key) => String(key));\n const uniqueKeyStrings = new Set(keyStrings);\n\n if (keyStrings.length !== uniqueKeyStrings.size) {\n return 'Duplicate keys are not allowed';\n }\n\n return undefined;\n}\n","import React from 'react';\nimport { FieldError } from 'react-hook-form';\n\nimport { getErrorMessage } from './validation';\n\ninterface ErrorMessageProps {\n /**\n * The error object from React Hook Form\n */\n error?: FieldError;\n\n /**\n * The ID of the error message for aria-errormessage references\n */\n id: string;\n\n /**\n * Optional custom error message to display instead of the error from React Hook Form\n */\n message?: string;\n\n /**\n * Optional additional CSS classes\n */\n className?: string;\n}\n\n/**\n * Displays validation error messages for form fields\n */\nexport function ErrorMessage({\n error,\n id,\n message,\n className = '',\n}: ErrorMessageProps): React.ReactElement | null {\n const errorMessage = message || getErrorMessage(error);\n\n if (!errorMessage) return null;\n\n return (\n <div id={id} className={`text-destructive mt-1 text-sm ${className}`} role=\"alert\">\n {errorMessage}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  const require_ErrorMessage = require('./ErrorMessage-D4BAP8iw.cjs');
2
+ let react = require("react");
3
+ react = require_ErrorMessage.__toESM(react);
4
+ let react_jsx_runtime = require("react/jsx-runtime");
5
+ let react_hook_form = require("react-hook-form");
2
6
  let _uiw_react_textarea_code_editor = require("@uiw/react-textarea-code-editor");
3
7
  _uiw_react_textarea_code_editor = require_ErrorMessage.__toESM(_uiw_react_textarea_code_editor);
4
8
  let _uiw_react_textarea_code_editor_nohighlight = require("@uiw/react-textarea-code-editor/nohighlight");
5
9
  _uiw_react_textarea_code_editor_nohighlight = require_ErrorMessage.__toESM(_uiw_react_textarea_code_editor_nohighlight);
6
- let react = require("react");
7
- react = require_ErrorMessage.__toESM(react);
8
- let react_hook_form = require("react-hook-form");
9
- let react_jsx_runtime = require("react/jsx-runtime");
10
10
 
11
11
  //#region src/components/fields/CodeEditorField.tsx
12
12
  /**
@@ -116,7 +116,6 @@ declare function CodeEditorField<TFieldValues extends FieldValues = FieldValues>
116
116
  declare namespace CodeEditorField {
117
117
  var displayName: string;
118
118
  }
119
- //# sourceMappingURL=CodeEditorField.d.ts.map
120
119
  //#endregion
121
120
  export { CodeEditorField, CodeEditorFieldProps };
122
- //# sourceMappingURL=code-editor-BKEEXGxt.d.cts.map
121
+ //# sourceMappingURL=code-editor.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.d.cts","names":[],"sources":["../src/components/fields/CodeEditorField.tsx"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAA2D,UAA1C,oBAA0C,CAAA,qBAAA,WAAA,GAAc,WAAd,CAAA,CAAA;EAAc;;;EAmBtD,EAAA,EAAA,MAAA;EAAR;;AAsFX;EAAqD,SAAA,CAAA,EAAA,MAAA;EAAc;;;EAGjE,IAAA,EA9FM,IA8FN,CA9FW,YA8FX,CAAA;EACA;;;EAGA,OAAA,EA7FS,OA6FT,CA7FiB,YA6FjB,CAAA;EACA;;;EAGA,KAAA,CAAA,EAAA,MAAA;EACA;;;EAGA,UAAA,CAAA,EAAA,MAAA;EACA;;;EACsC,WAAM,CAAA,EAAA,MAAA;EAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAjB1C,qCAAqC,cAAc;;;;;;;;;;;;;;;;;GAiBhE,qBAAqB,gBAAgB,KAAA,CAAM;kBAjB9B,eAAA"}
@@ -116,7 +116,6 @@ declare function CodeEditorField<TFieldValues extends FieldValues = FieldValues>
116
116
  declare namespace CodeEditorField {
117
117
  var displayName: string;
118
118
  }
119
- //# sourceMappingURL=CodeEditorField.d.ts.map
120
119
  //#endregion
121
120
  export { CodeEditorField, CodeEditorFieldProps };
122
- //# sourceMappingURL=code-editor-CxxSe1o8.d.ts.map
121
+ //# sourceMappingURL=code-editor.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.d.mts","names":[],"sources":["../src/components/fields/CodeEditorField.tsx"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAA2D,UAA1C,oBAA0C,CAAA,qBAAA,WAAA,GAAc,WAAd,CAAA,CAAA;EAAc;;;EAmBtD,EAAA,EAAA,MAAA;EAAR;;AAsFX;EAAqD,SAAA,CAAA,EAAA,MAAA;EAAc;;;EAGjE,IAAA,EA9FM,IA8FN,CA9FW,YA8FX,CAAA;EACA;;;EAGA,OAAA,EA7FS,OA6FT,CA7FiB,YA6FjB,CAAA;EACA;;;EAGA,KAAA,CAAA,EAAA,MAAA;EACA;;;EAGA,UAAA,CAAA,EAAA,MAAA;EACA;;;EACsC,WAAM,CAAA,EAAA,MAAA;EAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAjB1C,qCAAqC,cAAc;;;;;;;;;;;;;;;;;GAiBhE,qBAAqB,gBAAgB,KAAA,CAAM;kBAjB9B,eAAA"}
@@ -1,9 +1,9 @@
1
- import { t as ErrorMessage } from "./ErrorMessage-DyO9ZWWz.js";
2
- import CodeEditor from "@uiw/react-textarea-code-editor";
3
- import CodeEditorNoHighlight from "@uiw/react-textarea-code-editor/nohighlight";
1
+ import { t as ErrorMessage } from "./ErrorMessage-BqOEJm84.mjs";
4
2
  import React from "react";
5
- import { Controller } from "react-hook-form";
6
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { Controller } from "react-hook-form";
5
+ import CodeEditor from "@uiw/react-textarea-code-editor";
6
+ import CodeEditorNoHighlight from "@uiw/react-textarea-code-editor/nohighlight";
7
7
 
8
8
  //#region src/components/fields/CodeEditorField.tsx
9
9
  /**
@@ -126,4 +126,4 @@ CodeEditorField.displayName = "CodeEditorField";
126
126
 
127
127
  //#endregion
128
128
  export { CodeEditorField };
129
- //# sourceMappingURL=code-editor.js.map
129
+ //# sourceMappingURL=code-editor.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor.mjs","names":[],"sources":["../src/components/fields/CodeEditorField.tsx"],"sourcesContent":["import CodeEditor from '@uiw/react-textarea-code-editor';\nimport CodeEditorNoHighlight from '@uiw/react-textarea-code-editor/nohighlight';\nimport React from 'react';\nimport { Controller, type Control, type FieldValues, type Path } from 'react-hook-form';\n\nimport { ErrorMessage } from './utils';\n\n/**\n * CodeEditorField component properties\n */\nexport interface CodeEditorFieldProps<TFieldValues extends FieldValues = FieldValues> {\n /**\n * Unique identifier for the field\n */\n id: string;\n\n /**\n * Optional CSS class name for the container\n */\n className?: string;\n\n /**\n * Field name for form control\n */\n name: Path<TFieldValues>;\n\n /**\n * React Hook Form control object\n */\n control: Control<TFieldValues>;\n\n /**\n * Field label\n */\n label?: string;\n\n /**\n * Helper text displayed below the field\n */\n helperText?: string;\n\n /**\n * Placeholder text when empty\n */\n placeholder?: string;\n\n /**\n * Programming language for syntax highlighting\n */\n language?: string;\n\n /**\n * Editor theme\n */\n theme?: string;\n\n /**\n * Editor height\n */\n height?: string;\n\n /**\n * Editor maximum height (with scrollbar for overflow)\n */\n maxHeight?: string;\n\n /**\n * Content size threshold for disabling syntax highlighting (default: 5000 chars)\n */\n performanceThreshold?: number;\n\n /**\n * Whether the field is required\n */\n required?: boolean;\n\n /**\n * Whether the field is disabled\n */\n disabled?: boolean;\n\n /**\n * Whether the field is read-only\n */\n readOnly?: boolean;\n\n /**\n * Custom validation function for code values\n */\n validateCode?: (value: string) => boolean | string;\n}\n\n/**\n * CodeEditorField provides syntax-highlighted code editing with form integration.\n *\n * Features:\n * - Syntax highlighting via @uiw/react-textarea-code-editor\n * - React Hook Form integration with Controller\n * - Configurable language support (JSON, TypeScript, etc.)\n * - Performance optimizations with smart highlighting\n * - Constrained height with automatic scrolling\n * - Design system styling integration\n *\n * @example\n * ```tsx\n * <CodeEditorField\n * id=\"contractAbi\"\n * name=\"contractSchema\"\n * control={control}\n * label=\"Contract ABI\"\n * language=\"json\"\n * placeholder=\"Paste your ABI JSON here...\"\n * />\n * ```\n */\nexport function CodeEditorField<TFieldValues extends FieldValues = FieldValues>({\n id,\n name,\n control,\n label,\n helperText,\n placeholder = '',\n language = 'json',\n theme = 'light',\n height = '200px',\n maxHeight = '400px',\n performanceThreshold = 5000,\n required = false,\n disabled = false,\n readOnly = false,\n validateCode,\n className,\n}: CodeEditorFieldProps<TFieldValues>): React.ReactElement {\n // Convert height strings to numbers for native props with robust parsing\n function extractPixelValue(val: string | number, fallback: number): number {\n if (typeof val === 'number') return val;\n const match = typeof val === 'string' ? val.match(/^(\\d+)\\s*px$/) : null;\n if (match) return parseInt(match[1], 10);\n return fallback;\n }\n const minHeightNum = extractPixelValue(height, 200);\n const maxHeightNum = extractPixelValue(maxHeight, 400);\n\n return (\n <div className={className}>\n {label && (\n <label htmlFor={id} className=\"block text-sm font-medium text-foreground mb-2\">\n {label}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n </label>\n )}\n\n <Controller\n name={name}\n control={control}\n rules={{\n required: required ? 'This field is required' : false,\n // Move validation to onBlur to avoid expensive operations on every keystroke\n validate: {\n validCode: (value: string) => {\n if (!validateCode || !value) return true;\n\n const validation = validateCode(value);\n if (typeof validation === 'string') {\n return validation;\n }\n if (validation === false) {\n return 'Invalid code format';\n }\n return true;\n },\n },\n }}\n render={({ field: { onChange, onBlur, value }, fieldState: { error } }) => {\n // Check if content is too large for syntax highlighting\n const contentSize = (value || '').length;\n const shouldDisableHighlighting = contentSize > performanceThreshold;\n const EditorComponent = shouldDisableHighlighting ? CodeEditorNoHighlight : CodeEditor;\n\n // Update form immediately to prevent controlled component conflicts\n const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>): void => {\n onChange(event.target.value); // Immediate update for controlled component\n };\n\n // Simple blur handler\n const handleBlur = (): void => {\n onBlur();\n };\n\n return (\n <div className=\"space-y-2\">\n <div\n className=\"w-full rounded-md border border-input bg-background ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 disabled:cursor-not-allowed resize-y\"\n style={{\n maxHeight: `${maxHeightNum}px`,\n overflow: 'auto',\n overflowX: 'hidden', // Prevent horizontal scrolling and expansion\n minHeight: `${minHeightNum}px`,\n resize: 'vertical',\n }}\n >\n <EditorComponent\n id={id}\n value={value || ''}\n language={language}\n placeholder={placeholder}\n onChange={handleChange}\n onBlur={handleBlur}\n padding={12}\n minHeight={minHeightNum}\n data-color-mode={theme as 'light' | 'dark'}\n disabled={disabled}\n readOnly={readOnly}\n data-testid={`${id}-code-editor${shouldDisableHighlighting ? '-no-highlight' : ''}`}\n className=\"text-sm placeholder:text-muted-foreground\"\n style={{\n fontFamily:\n 'ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, \"Liberation Mono\", Menlo, monospace',\n fontSize: '14px',\n border: 'none',\n backgroundColor: 'transparent',\n width: '100%',\n wordWrap: 'break-word', // Break long words to prevent horizontal overflow\n whiteSpace: 'pre-wrap', // Preserve formatting while allowing wrapping\n overflowWrap: 'break-word', // Modern CSS property for word breaking\n }}\n />\n </div>\n\n <ErrorMessage error={error} id={`${id}-error`} />\n\n {helperText && !error && (\n <p className=\"text-sm text-muted-foreground\" id={`${id}-helper`}>\n {helperText}\n </p>\n )}\n </div>\n );\n }}\n />\n </div>\n );\n}\n\nCodeEditorField.displayName = 'CodeEditorField';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,SAAgB,gBAAgE,EAC9E,IACA,MACA,SACA,OACA,YACA,cAAc,IACd,WAAW,QACX,QAAQ,SACR,SAAS,SACT,YAAY,SACZ,uBAAuB,KACvB,WAAW,OACX,WAAW,OACX,WAAW,OACX,cACA,aACyD;CAEzD,SAAS,kBAAkB,KAAsB,UAA0B;AACzE,MAAI,OAAO,QAAQ,SAAU,QAAO;EACpC,MAAM,QAAQ,OAAO,QAAQ,WAAW,IAAI,MAAM,eAAe,GAAG;AACpE,MAAI,MAAO,QAAO,SAAS,MAAM,IAAI,GAAG;AACxC,SAAO;;CAET,MAAM,eAAe,kBAAkB,QAAQ,IAAI;CACnD,MAAM,eAAe,kBAAkB,WAAW,IAAI;AAEtD,QACE,qBAAC;EAAe;aACb,SACC,qBAAC;GAAM,SAAS;GAAI,WAAU;cAC3B,OACA,YAAY,oBAAC;IAAK,WAAU;cAAwB;KAAQ;IACvD,EAGV,oBAAC;GACO;GACG;GACT,OAAO;IACL,UAAU,WAAW,2BAA2B;IAEhD,UAAU,EACR,YAAY,UAAkB;AAC5B,SAAI,CAAC,gBAAgB,CAAC,MAAO,QAAO;KAEpC,MAAM,aAAa,aAAa,MAAM;AACtC,SAAI,OAAO,eAAe,SACxB,QAAO;AAET,SAAI,eAAe,MACjB,QAAO;AAET,YAAO;OAEV;IACF;GACD,SAAS,EAAE,OAAO,EAAE,UAAU,QAAQ,SAAS,YAAY,EAAE,cAAc;IAGzE,MAAM,6BADe,SAAS,IAAI,SACc;IAChD,MAAM,kBAAkB,4BAA4B,wBAAwB;IAG5E,MAAM,gBAAgB,UAAwD;AAC5E,cAAS,MAAM,OAAO,MAAM;;IAI9B,MAAM,mBAAyB;AAC7B,aAAQ;;AAGV,WACE,qBAAC;KAAI,WAAU;;MACb,oBAAC;OACC,WAAU;OACV,OAAO;QACL,WAAW,GAAG,aAAa;QAC3B,UAAU;QACV,WAAW;QACX,WAAW,GAAG,aAAa;QAC3B,QAAQ;QACT;iBAED,oBAAC;QACK;QACJ,OAAO,SAAS;QACN;QACG;QACb,UAAU;QACV,QAAQ;QACR,SAAS;QACT,WAAW;QACX,mBAAiB;QACP;QACA;QACV,eAAa,GAAG,GAAG,cAAc,4BAA4B,kBAAkB;QAC/E,WAAU;QACV,OAAO;SACL,YACE;SACF,UAAU;SACV,QAAQ;SACR,iBAAiB;SACjB,OAAO;SACP,UAAU;SACV,YAAY;SACZ,cAAc;SACf;SACD;QACE;MAEN,oBAAC;OAAoB;OAAO,IAAI,GAAG,GAAG;QAAW;MAEhD,cAAc,CAAC,SACd,oBAAC;OAAE,WAAU;OAAgC,IAAI,GAAG,GAAG;iBACpD;QACC;;MAEF;;IAGV;GACE;;AAIV,gBAAgB,cAAc"}
package/dist/index.cjs CHANGED
@@ -1,13 +1,12 @@
1
1
  const require_ErrorMessage = require('./ErrorMessage-D4BAP8iw.cjs');
2
- let react = require("react");
3
- react = require_ErrorMessage.__toESM(react);
4
- let react_hook_form = require("react-hook-form");
5
- let react_jsx_runtime = require("react/jsx-runtime");
6
2
  let _radix_ui_react_accordion = require("@radix-ui/react-accordion");
7
3
  _radix_ui_react_accordion = require_ErrorMessage.__toESM(_radix_ui_react_accordion);
8
4
  let class_variance_authority = require("class-variance-authority");
9
5
  let lucide_react = require("lucide-react");
6
+ let react = require("react");
7
+ react = require_ErrorMessage.__toESM(react);
10
8
  let _openzeppelin_ui_utils = require("@openzeppelin/ui-utils");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
11
10
  let _radix_ui_react_slot = require("@radix-ui/react-slot");
12
11
  let react_day_picker = require("react-day-picker");
13
12
  let _radix_ui_react_checkbox = require("@radix-ui/react-checkbox");
@@ -19,6 +18,7 @@ let _radix_ui_react_dialog = require("@radix-ui/react-dialog");
19
18
  _radix_ui_react_dialog = require_ErrorMessage.__toESM(_radix_ui_react_dialog);
20
19
  let _radix_ui_react_dropdown_menu = require("@radix-ui/react-dropdown-menu");
21
20
  _radix_ui_react_dropdown_menu = require_ErrorMessage.__toESM(_radix_ui_react_dropdown_menu);
21
+ let react_hook_form = require("react-hook-form");
22
22
  let _radix_ui_react_label = require("@radix-ui/react-label");
23
23
  _radix_ui_react_label = require_ErrorMessage.__toESM(_radix_ui_react_label);
24
24
  let _radix_ui_react_progress = require("@radix-ui/react-progress");
@@ -37,195 +37,6 @@ let _openzeppelin_ui_types = require("@openzeppelin/ui-types");
37
37
  let sonner = require("sonner");
38
38
  let next_themes = require("next-themes");
39
39
 
40
- //#region src/components/fields/utils/accessibility.ts
41
- /**
42
- * Accessibility utilities for form field components
43
- */
44
- /**
45
- * Default aria-describedby ID generator based on field ID
46
- */
47
- function getDescribedById(id, type = "error") {
48
- return `${id}-${type}`;
49
- }
50
- /**
51
- * Generates accessibility attributes for form fields
52
- */
53
- function getAccessibilityProps({ id, hasError = false, isRequired = false, isDisabled = false, isReadOnly = false, hasHelperText = false, hasCharacterCounter = false }) {
54
- const describedByIds = [];
55
- if (hasError) describedByIds.push(getDescribedById(id, "error"));
56
- if (hasHelperText) describedByIds.push(getDescribedById(id, "description"));
57
- if (hasCharacterCounter) describedByIds.push(getDescribedById(id, "counter"));
58
- return {
59
- "aria-invalid": hasError,
60
- "aria-required": isRequired,
61
- "aria-describedby": describedByIds.length > 0 ? describedByIds.join(" ") : void 0,
62
- "aria-errormessage": hasError ? getDescribedById(id, "error") : void 0,
63
- required: isRequired,
64
- disabled: isDisabled,
65
- readOnly: isReadOnly,
66
- tabIndex: isDisabled ? -1 : 0
67
- };
68
- }
69
- /**
70
- * Utility function to handle keyboard events for interactive elements
71
- * Helps ensure keyboard users can interact with form controls
72
- */
73
- function handleKeyboardEvent(event, handlers) {
74
- switch (event.key) {
75
- case "Enter":
76
- if (handlers.onEnter) {
77
- event.preventDefault();
78
- handlers.onEnter();
79
- }
80
- break;
81
- case " ":
82
- if (handlers.onSpace) {
83
- event.preventDefault();
84
- handlers.onSpace();
85
- }
86
- break;
87
- case "Escape":
88
- if (handlers.onEscape) {
89
- event.preventDefault();
90
- handlers.onEscape();
91
- }
92
- break;
93
- case "ArrowUp":
94
- if (handlers.onArrowUp) {
95
- event.preventDefault();
96
- handlers.onArrowUp();
97
- }
98
- break;
99
- case "ArrowDown":
100
- if (handlers.onArrowDown) {
101
- event.preventDefault();
102
- handlers.onArrowDown();
103
- }
104
- break;
105
- case "ArrowLeft":
106
- if (handlers.onArrowLeft) {
107
- event.preventDefault();
108
- handlers.onArrowLeft();
109
- }
110
- break;
111
- case "ArrowRight":
112
- if (handlers.onArrowRight) {
113
- event.preventDefault();
114
- handlers.onArrowRight();
115
- }
116
- break;
117
- case "Tab":
118
- if (handlers.onTab) {
119
- event.preventDefault();
120
- handlers.onTab();
121
- }
122
- break;
123
- default: break;
124
- }
125
- }
126
- /**
127
- * Field focus management utility
128
- * For managing focus within a field group or complex form component
129
- */
130
- function createFocusManager() {
131
- return {
132
- focusFirstElement(container) {
133
- if (!container) return;
134
- const focusable = container.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])");
135
- if (focusable.length > 0) focusable[0].focus();
136
- },
137
- focusElementById(id) {
138
- const element = document.getElementById(id);
139
- if (element) element.focus();
140
- },
141
- trapFocus(event, container) {
142
- if (!container || event.key !== "Tab") return;
143
- const focusable = Array.from(container.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])"));
144
- if (focusable.length === 0) return;
145
- const firstElement = focusable[0];
146
- const lastElement = focusable[focusable.length - 1];
147
- if (event.shiftKey && document.activeElement === firstElement) {
148
- event.preventDefault();
149
- lastElement.focus();
150
- } else if (!event.shiftKey && document.activeElement === lastElement) {
151
- event.preventDefault();
152
- firstElement.focus();
153
- }
154
- }
155
- };
156
- }
157
- /**
158
- * Provides a handler for Escape key to clear input fields
159
- *
160
- * @param onChange - Function to call when value changes
161
- * @param value - Current value of the input
162
- * @returns A function to handle the Escape key press
163
- */
164
- function handleEscapeKey(onChange, value) {
165
- return (e) => {
166
- if (e.key === "Escape") {
167
- e.preventDefault();
168
- if (value) onChange("");
169
- else e.target.blur();
170
- }
171
- };
172
- }
173
- /**
174
- * Provides a handler for Space/Enter keys for toggle components (checkboxes, switches)
175
- *
176
- * @param onChange - Function to call when value changes
177
- * @param value - Current value of the input
178
- * @returns A function to handle the Space/Enter key press
179
- */
180
- function handleToggleKeys(onChange, value) {
181
- return (e) => {
182
- if (e.key === " " || e.key === "Enter") {
183
- e.preventDefault();
184
- onChange(!value);
185
- }
186
- };
187
- }
188
- /**
189
- * Provides a handler for arrow keys for numeric inputs
190
- *
191
- * @param onChange - Function to call when value changes
192
- * @param value - Current numeric value
193
- * @param step - Step amount for increments/decrements
194
- * @param min - Minimum allowed value (optional)
195
- * @param max - Maximum allowed value (optional)
196
- * @returns A function to handle arrow key presses
197
- */
198
- function handleNumericKeys(onChange, value, step = 1, min, max) {
199
- return (e) => {
200
- if (e.key === "ArrowUp" || e.key === "ArrowDown") {
201
- e.preventDefault();
202
- const direction = e.key === "ArrowUp" ? 1 : -1;
203
- let newValue = typeof value === "number" ? value + step * direction : 0;
204
- if (typeof min === "number") newValue = Math.max(min, newValue);
205
- if (typeof max === "number") newValue = Math.min(max, newValue);
206
- onChange(newValue);
207
- }
208
- };
209
- }
210
-
211
- //#endregion
212
- //#region src/components/fields/utils/layout.ts
213
- /**
214
- * Layout utility functions for form components
215
- */
216
- /**
217
- * Helper function to get width classes based on the field width
218
- */
219
- function getWidthClasses(width) {
220
- switch (width) {
221
- case "half": return "form-field-half";
222
- case "third": return "form-field-third";
223
- case "full":
224
- default: return "form-field-full";
225
- }
226
- }
227
-
228
- //#endregion
229
40
  //#region src/components/ui/accordion.tsx
230
41
  const accordionItemVariants = (0, class_variance_authority.cva)("", {
231
42
  variants: { variant: {
@@ -1706,6 +1517,195 @@ function ViewContractStateButton({ contractAddress, onToggle }) {
1706
1517
  });
1707
1518
  }
1708
1519
 
1520
+ //#endregion
1521
+ //#region src/components/fields/utils/accessibility.ts
1522
+ /**
1523
+ * Accessibility utilities for form field components
1524
+ */
1525
+ /**
1526
+ * Default aria-describedby ID generator based on field ID
1527
+ */
1528
+ function getDescribedById(id, type = "error") {
1529
+ return `${id}-${type}`;
1530
+ }
1531
+ /**
1532
+ * Generates accessibility attributes for form fields
1533
+ */
1534
+ function getAccessibilityProps({ id, hasError = false, isRequired = false, isDisabled = false, isReadOnly = false, hasHelperText = false, hasCharacterCounter = false }) {
1535
+ const describedByIds = [];
1536
+ if (hasError) describedByIds.push(getDescribedById(id, "error"));
1537
+ if (hasHelperText) describedByIds.push(getDescribedById(id, "description"));
1538
+ if (hasCharacterCounter) describedByIds.push(getDescribedById(id, "counter"));
1539
+ return {
1540
+ "aria-invalid": hasError,
1541
+ "aria-required": isRequired,
1542
+ "aria-describedby": describedByIds.length > 0 ? describedByIds.join(" ") : void 0,
1543
+ "aria-errormessage": hasError ? getDescribedById(id, "error") : void 0,
1544
+ required: isRequired,
1545
+ disabled: isDisabled,
1546
+ readOnly: isReadOnly,
1547
+ tabIndex: isDisabled ? -1 : 0
1548
+ };
1549
+ }
1550
+ /**
1551
+ * Utility function to handle keyboard events for interactive elements
1552
+ * Helps ensure keyboard users can interact with form controls
1553
+ */
1554
+ function handleKeyboardEvent(event, handlers) {
1555
+ switch (event.key) {
1556
+ case "Enter":
1557
+ if (handlers.onEnter) {
1558
+ event.preventDefault();
1559
+ handlers.onEnter();
1560
+ }
1561
+ break;
1562
+ case " ":
1563
+ if (handlers.onSpace) {
1564
+ event.preventDefault();
1565
+ handlers.onSpace();
1566
+ }
1567
+ break;
1568
+ case "Escape":
1569
+ if (handlers.onEscape) {
1570
+ event.preventDefault();
1571
+ handlers.onEscape();
1572
+ }
1573
+ break;
1574
+ case "ArrowUp":
1575
+ if (handlers.onArrowUp) {
1576
+ event.preventDefault();
1577
+ handlers.onArrowUp();
1578
+ }
1579
+ break;
1580
+ case "ArrowDown":
1581
+ if (handlers.onArrowDown) {
1582
+ event.preventDefault();
1583
+ handlers.onArrowDown();
1584
+ }
1585
+ break;
1586
+ case "ArrowLeft":
1587
+ if (handlers.onArrowLeft) {
1588
+ event.preventDefault();
1589
+ handlers.onArrowLeft();
1590
+ }
1591
+ break;
1592
+ case "ArrowRight":
1593
+ if (handlers.onArrowRight) {
1594
+ event.preventDefault();
1595
+ handlers.onArrowRight();
1596
+ }
1597
+ break;
1598
+ case "Tab":
1599
+ if (handlers.onTab) {
1600
+ event.preventDefault();
1601
+ handlers.onTab();
1602
+ }
1603
+ break;
1604
+ default: break;
1605
+ }
1606
+ }
1607
+ /**
1608
+ * Field focus management utility
1609
+ * For managing focus within a field group or complex form component
1610
+ */
1611
+ function createFocusManager() {
1612
+ return {
1613
+ focusFirstElement(container) {
1614
+ if (!container) return;
1615
+ const focusable = container.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])");
1616
+ if (focusable.length > 0) focusable[0].focus();
1617
+ },
1618
+ focusElementById(id) {
1619
+ const element = document.getElementById(id);
1620
+ if (element) element.focus();
1621
+ },
1622
+ trapFocus(event, container) {
1623
+ if (!container || event.key !== "Tab") return;
1624
+ const focusable = Array.from(container.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])"));
1625
+ if (focusable.length === 0) return;
1626
+ const firstElement = focusable[0];
1627
+ const lastElement = focusable[focusable.length - 1];
1628
+ if (event.shiftKey && document.activeElement === firstElement) {
1629
+ event.preventDefault();
1630
+ lastElement.focus();
1631
+ } else if (!event.shiftKey && document.activeElement === lastElement) {
1632
+ event.preventDefault();
1633
+ firstElement.focus();
1634
+ }
1635
+ }
1636
+ };
1637
+ }
1638
+ /**
1639
+ * Provides a handler for Escape key to clear input fields
1640
+ *
1641
+ * @param onChange - Function to call when value changes
1642
+ * @param value - Current value of the input
1643
+ * @returns A function to handle the Escape key press
1644
+ */
1645
+ function handleEscapeKey(onChange, value) {
1646
+ return (e) => {
1647
+ if (e.key === "Escape") {
1648
+ e.preventDefault();
1649
+ if (value) onChange("");
1650
+ else e.target.blur();
1651
+ }
1652
+ };
1653
+ }
1654
+ /**
1655
+ * Provides a handler for Space/Enter keys for toggle components (checkboxes, switches)
1656
+ *
1657
+ * @param onChange - Function to call when value changes
1658
+ * @param value - Current value of the input
1659
+ * @returns A function to handle the Space/Enter key press
1660
+ */
1661
+ function handleToggleKeys(onChange, value) {
1662
+ return (e) => {
1663
+ if (e.key === " " || e.key === "Enter") {
1664
+ e.preventDefault();
1665
+ onChange(!value);
1666
+ }
1667
+ };
1668
+ }
1669
+ /**
1670
+ * Provides a handler for arrow keys for numeric inputs
1671
+ *
1672
+ * @param onChange - Function to call when value changes
1673
+ * @param value - Current numeric value
1674
+ * @param step - Step amount for increments/decrements
1675
+ * @param min - Minimum allowed value (optional)
1676
+ * @param max - Maximum allowed value (optional)
1677
+ * @returns A function to handle arrow key presses
1678
+ */
1679
+ function handleNumericKeys(onChange, value, step = 1, min, max) {
1680
+ return (e) => {
1681
+ if (e.key === "ArrowUp" || e.key === "ArrowDown") {
1682
+ e.preventDefault();
1683
+ const direction = e.key === "ArrowUp" ? 1 : -1;
1684
+ let newValue = typeof value === "number" ? value + step * direction : 0;
1685
+ if (typeof min === "number") newValue = Math.max(min, newValue);
1686
+ if (typeof max === "number") newValue = Math.min(max, newValue);
1687
+ onChange(newValue);
1688
+ }
1689
+ };
1690
+ }
1691
+
1692
+ //#endregion
1693
+ //#region src/components/fields/utils/layout.ts
1694
+ /**
1695
+ * Layout utility functions for form components
1696
+ */
1697
+ /**
1698
+ * Helper function to get width classes based on the field width
1699
+ */
1700
+ function getWidthClasses(width) {
1701
+ switch (width) {
1702
+ case "half": return "form-field-half";
1703
+ case "third": return "form-field-third";
1704
+ case "full":
1705
+ default: return "form-field-full";
1706
+ }
1707
+ }
1708
+
1709
1709
  //#endregion
1710
1710
  //#region src/components/fields/AddressField.tsx
1711
1711
  /**
@@ -5093,6 +5093,65 @@ function useNetworkErrorAwareAdapter(adapter) {
5093
5093
  return wrappedAdapterRef.current;
5094
5094
  }
5095
5095
 
5096
+ //#endregion
5097
+ //#region src/components/network-errors/NetworkServiceErrorBanner.tsx
5098
+ /**
5099
+ * User-friendly banner displayed when a network service connection fails.
5100
+ * Works with any service type (RPC, Explorer, Indexer, etc.) and provides
5101
+ * a clear explanation of the issue with a call-to-action to open the
5102
+ * network settings dialog where users can configure an alternative endpoint.
5103
+ *
5104
+ * This component can be used with or without the NetworkErrorNotificationProvider:
5105
+ * - With provider: The settings handler is obtained from context
5106
+ * - Without provider: Pass onOpenNetworkSettings prop directly, or the button won't render
5107
+ */
5108
+ function NetworkServiceErrorBanner({ networkConfig, serviceType, errorMessage, title, description, onOpenNetworkSettings: onOpenNetworkSettingsProp }) {
5109
+ const context = (0, react.useContext)(NetworkErrorContext);
5110
+ const onOpenNetworkSettings = onOpenNetworkSettingsProp ?? context?.onOpenNetworkSettings;
5111
+ const handleOpenSettings = () => {
5112
+ onOpenNetworkSettings?.(networkConfig.id);
5113
+ };
5114
+ const serviceName = (0, _openzeppelin_ui_utils.getServiceDisplayName)(serviceType);
5115
+ const defaultTitle = `Unable to Connect to ${networkConfig.name}`;
5116
+ const defaultDescription = onOpenNetworkSettings ? `This could be due to network congestion, rate limiting, or the service being temporarily down. You can configure a custom ${serviceName.toLowerCase()} endpoint in the network settings.` : `This could be due to network congestion, rate limiting, or the service being temporarily down.`;
5117
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Alert, {
5118
+ variant: "destructive",
5119
+ className: "border-red-500/50 bg-red-50 dark:bg-red-950/20",
5120
+ children: [
5121
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CloudOff, { className: "h-4 w-4 text-red-600 dark:text-red-400" }),
5122
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlertTitle, {
5123
+ className: "text-red-900 dark:text-red-100",
5124
+ children: title || defaultTitle
5125
+ }),
5126
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(AlertDescription, {
5127
+ className: "text-red-800 dark:text-red-200",
5128
+ children: [
5129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5130
+ className: "mb-2",
5131
+ children: errorMessage || `The ${serviceName.toLowerCase()} for ${networkConfig.name} is currently unavailable or not responding.`
5132
+ }),
5133
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5134
+ className: "text-sm mb-3",
5135
+ children: description || defaultDescription
5136
+ }),
5137
+ onOpenNetworkSettings && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Button, {
5138
+ onClick: handleOpenSettings,
5139
+ variant: "default",
5140
+ size: "sm",
5141
+ className: "bg-red-600 hover:bg-red-700 text-white dark:bg-red-500 dark:hover:bg-red-600",
5142
+ children: [
5143
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Settings, { className: "mr-2 h-4 w-4" }),
5144
+ "Configure ",
5145
+ serviceName,
5146
+ " Settings"
5147
+ ]
5148
+ })
5149
+ ]
5150
+ })
5151
+ ]
5152
+ });
5153
+ }
5154
+
5096
5155
  //#endregion
5097
5156
  //#region src/components/ui/sonner.tsx
5098
5157
  const Toaster = ({ ...props }) => {
@@ -5191,6 +5250,7 @@ exports.MidnightIcon = MidnightIcon;
5191
5250
  exports.NetworkErrorNotificationProvider = NetworkErrorNotificationProvider;
5192
5251
  exports.NetworkIcon = NetworkIcon;
5193
5252
  exports.NetworkSelector = NetworkSelector;
5253
+ exports.NetworkServiceErrorBanner = NetworkServiceErrorBanner;
5194
5254
  exports.NetworkStatusBadge = NetworkStatusBadge;
5195
5255
  exports.NumberField = NumberField;
5196
5256
  exports.ObjectField = ObjectField;