@openzeppelin/ui-components 3.0.1 → 3.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.
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import { a as getValidationStateClasses, c as isDuplicateMapKey, d as INTEGER_HTML_PATTERN, f as INTEGER_INPUT_PATTERN, i as getErrorMessage, l as validateField, n as createValidationResult, o as handleValidationError, p as INTEGER_PATTERN, r as formatValidationError, s as hasFieldError, t as ErrorMessage, u as validateMapEntries } from "./ErrorMessage-BqOEJm84.mjs";
2
2
  import * as AccordionPrimitive from "@radix-ui/react-accordion";
3
3
  import { cva } from "class-variance-authority";
4
- import { AlertCircle, Calendar as Calendar$1, Check, CheckCircle, CheckCircle2, CheckIcon, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Circle, CloudOff, Copy, DollarSign, ExternalLink as ExternalLink$1, ExternalLinkIcon, Eye, EyeOff, File, FileText, GripVertical, Hash, Info, Loader2, Menu, MoreHorizontal, Network, Pencil, Plus, Search, Settings, Timer, Upload, X } from "lucide-react";
4
+ import { AlertCircle, Calendar as Calendar$1, Check, CheckCircle, CheckCircle2, CheckIcon, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Circle, ClipboardPaste, CloudOff, Copy, DollarSign, ExternalLink as ExternalLink$1, ExternalLinkIcon, Eye, EyeOff, File, FileText, GripVertical, Hash, Info, Loader2, Menu, MoreHorizontal, Network, Pencil, PencilLine, Plus, Search, Settings, Timer, Upload, X } from "lucide-react";
5
5
  import * as React$1 from "react";
6
6
  import React, { createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
7
- import { cn, getDefaultValueForType, getHostedNetworkAvailabilityNoticeCopy, getInvalidUrlMessage, getServiceDisplayName, isNetworkAvailabilityPolicyActive, isValidUrl, truncateMiddle, validateBytesSimple } from "@openzeppelin/ui-utils";
7
+ import { classifyAddressCandidates, cn, getDefaultValueForType, getHostedNetworkAvailabilityNoticeCopy, getInvalidUrlMessage, getServiceDisplayName, isNetworkAvailabilityPolicyActive, isValidUrl, parseDelimitedAddressInput, truncateMiddle, validateBytesSimple } from "@openzeppelin/ui-utils";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
10
10
  import { Slot, Slottable } from "@radix-ui/react-slot";
@@ -14,7 +14,7 @@ import { format } from "date-fns";
14
14
  import * as PopoverPrimitive from "@radix-ui/react-popover";
15
15
  import * as DialogPrimitive from "@radix-ui/react-dialog";
16
16
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
17
- import { Controller, FormProvider, useFieldArray, useFormContext, useWatch } from "react-hook-form";
17
+ import { Controller, FormProvider, useFieldArray, useForm, useFormContext, useWatch } from "react-hook-form";
18
18
  import * as LabelPrimitive from "@radix-ui/react-label";
19
19
  import * as ProgressPrimitive from "@radix-ui/react-progress";
20
20
  import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
@@ -26,7 +26,7 @@ import { Toaster as Toaster$1, toast } from "sonner";
26
26
  import { useTheme } from "next-themes";
27
27
 
28
28
  //#region src/version.ts
29
- const VERSION = "3.0.1";
29
+ const VERSION = "3.2.0";
30
30
 
31
31
  //#endregion
32
32
  //#region src/components/ui/accordion.tsx
@@ -2940,6 +2940,457 @@ function AddressField({ id, label, placeholder, helperText, control, name, width
2940
2940
  }
2941
2941
  AddressField.displayName = "AddressField";
2942
2942
 
2943
+ //#endregion
2944
+ //#region src/components/fields/TextAreaField.tsx
2945
+ /**
2946
+ * Multi-line text input field component specifically designed for React Hook Form integration.
2947
+ *
2948
+ * Architecture flow:
2949
+ * 1. Form schemas are generated from contract functions using adapters
2950
+ * 2. TransactionForm renders the overall form structure with React Hook Form
2951
+ * 3. DynamicFormField selects the appropriate field component based on field type
2952
+ * 4. BaseField provides consistent layout and hook form integration
2953
+ * 5. This component handles textarea-specific rendering and validation
2954
+ *
2955
+ * The component includes:
2956
+ * - Integration with React Hook Form
2957
+ * - Resizable multi-line text input
2958
+ * - Character limit support
2959
+ * - Customizable validation through adapter integration
2960
+ * - Automatic error handling and reporting
2961
+ * - Full accessibility support with ARIA attributes
2962
+ * - Keyboard navigation with Escape to clear
2963
+ */
2964
+ function TextAreaField({ id, label, placeholder, helperText, control, name, width = "full", validation, rows = 4, maxLength, validateTextArea, readOnly }) {
2965
+ const isRequired = !!validation?.required;
2966
+ const errorId = `${id}-error`;
2967
+ const descriptionId = `${id}-description`;
2968
+ return /* @__PURE__ */ jsxs("div", {
2969
+ className: `flex flex-col gap-2 ${width === "full" ? "w-full" : width === "half" ? "w-1/2" : "w-1/3"}`,
2970
+ children: [label && /* @__PURE__ */ jsxs(Label, {
2971
+ htmlFor: id,
2972
+ children: [
2973
+ label,
2974
+ " ",
2975
+ isRequired && /* @__PURE__ */ jsx("span", {
2976
+ className: "text-destructive",
2977
+ children: "*"
2978
+ })
2979
+ ]
2980
+ }), /* @__PURE__ */ jsx(Controller, {
2981
+ control,
2982
+ name,
2983
+ rules: { validate: (value) => {
2984
+ if (value === void 0 || value === null || value === "") return validation?.required ? "This field is required" : true;
2985
+ if (validateTextArea && value) {
2986
+ const validation$1 = validateTextArea(value);
2987
+ if (validation$1 !== true && typeof validation$1 === "string") return validation$1;
2988
+ }
2989
+ if (maxLength && typeof value === "string" && value.length > maxLength) return `Maximum length is ${maxLength} characters`;
2990
+ return true;
2991
+ } },
2992
+ disabled: readOnly,
2993
+ render: ({ field, fieldState: { error, isTouched } }) => {
2994
+ const hasError = !!error;
2995
+ const shouldShowError = hasError && isTouched;
2996
+ const validationClasses = getValidationStateClasses(error, isTouched);
2997
+ const accessibilityProps = getAccessibilityProps({
2998
+ id,
2999
+ hasError,
3000
+ isRequired,
3001
+ hasHelperText: !!helperText
3002
+ });
3003
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3004
+ /* @__PURE__ */ jsx(Textarea, {
3005
+ ...field,
3006
+ id,
3007
+ placeholder,
3008
+ rows,
3009
+ maxLength,
3010
+ className: validationClasses,
3011
+ value: field.value ?? "",
3012
+ disabled: readOnly,
3013
+ ...accessibilityProps,
3014
+ "aria-describedby": `${helperText ? descriptionId : ""} ${hasError ? errorId : ""}`,
3015
+ onKeyDown: handleEscapeKey((value) => {
3016
+ if (typeof field.onChange === "function") field.onChange(value);
3017
+ }, field.value)
3018
+ }),
3019
+ maxLength && typeof field.value === "string" && /* @__PURE__ */ jsxs("div", {
3020
+ className: "text-muted-foreground text-right text-xs",
3021
+ children: [
3022
+ field.value.length,
3023
+ "/",
3024
+ maxLength
3025
+ ]
3026
+ }),
3027
+ helperText && /* @__PURE__ */ jsx("div", {
3028
+ id: descriptionId,
3029
+ className: "text-muted-foreground text-sm",
3030
+ children: helperText
3031
+ }),
3032
+ /* @__PURE__ */ jsx(ErrorMessage, {
3033
+ error,
3034
+ id: errorId,
3035
+ message: shouldShowError ? error?.message : void 0
3036
+ })
3037
+ ] });
3038
+ }
3039
+ })]
3040
+ });
3041
+ }
3042
+ TextAreaField.displayName = "TextAreaField";
3043
+
3044
+ //#endregion
3045
+ //#region src/components/fields/AddressListField/labels.ts
3046
+ /** Default English labels for {@link AddressListField}. */
3047
+ const DEFAULT_ADDRESS_LIST_FIELD_LABELS = {
3048
+ addSingleAddress: "Add",
3049
+ invalidSingleAddress: "Enter a valid address",
3050
+ enableBulkEntry: "Bulk paste",
3051
+ enableSingleEntry: "Single entry",
3052
+ addAddresses: "Add addresses",
3053
+ addAddressCount: (count) => `Add ${count} address${count === 1 ? "" : "es"}`,
3054
+ invalidPrefix: "Invalid:",
3055
+ invalidMore: (extraCount) => `(+${extraCount} more)`,
3056
+ maxItemsReached: (maxItems) => `Maximum of ${maxItems} addresses reached.`,
3057
+ addressesAdded: (count) => `${count} address${count === 1 ? "" : "es"} added`,
3058
+ removeAddress: (index) => `Remove address ${index + 1}`,
3059
+ previewReadyToAdd: (count) => `${count} ready to add`,
3060
+ previewInvalid: (count) => `${count} invalid`,
3061
+ previewAlreadyListed: (count) => `${count} already listed`,
3062
+ previewDuplicateInPaste: (count) => `${count} duplicate in paste`,
3063
+ bulkNothingAdded: "No addresses were added. Check formatting and try again.",
3064
+ bulkAdded: (count) => `Added ${count} address${count === 1 ? "" : "es"}`,
3065
+ bulkInvalidFormats: (count) => `${count} invalid format${count === 1 ? "" : "s"}`,
3066
+ bulkAlreadyListed: (count) => `${count} already listed`,
3067
+ bulkDuplicatesInPaste: (count) => `${count} duplicate${count === 1 ? "" : "s"} in paste`
3068
+ };
3069
+ /** Merges {@link DEFAULT_ADDRESS_LIST_FIELD_LABELS} with optional overrides. */
3070
+ function resolveAddressListFieldLabels(overrides) {
3071
+ return {
3072
+ ...DEFAULT_ADDRESS_LIST_FIELD_LABELS,
3073
+ ...overrides
3074
+ };
3075
+ }
3076
+ /** Builds the inline helper preview shown while the user types in the draft textarea. */
3077
+ function buildAddressListPreviewSummary(classification, labels) {
3078
+ if (!classification) return null;
3079
+ const skipped = classification.invalid.length + classification.alreadyListed.length + classification.duplicatesInInput.length;
3080
+ if (classification.accepted.length === 0 && skipped === 0) return null;
3081
+ const parts = [];
3082
+ if (classification.accepted.length > 0) parts.push(labels.previewReadyToAdd(classification.accepted.length));
3083
+ if (classification.invalid.length > 0) parts.push(labels.previewInvalid(classification.invalid.length));
3084
+ if (classification.alreadyListed.length > 0) parts.push(labels.previewAlreadyListed(classification.alreadyListed.length));
3085
+ if (classification.duplicatesInInput.length > 0) parts.push(labels.previewDuplicateInPaste(classification.duplicatesInInput.length));
3086
+ return parts.join(" · ");
3087
+ }
3088
+ /** Formats post-add feedback after the user commits parsed addresses to the list. */
3089
+ function formatAddressBulkSummary(classification, addedCount, labels) {
3090
+ if (addedCount === 0 && classification.accepted.length === 0) {
3091
+ if (classification.invalid.length + classification.alreadyListed.length + classification.duplicatesInInput.length === 0) return null;
3092
+ return labels.bulkNothingAdded;
3093
+ }
3094
+ const parts = [];
3095
+ if (addedCount > 0) parts.push(labels.bulkAdded(addedCount));
3096
+ if (classification.invalid.length > 0) parts.push(labels.bulkInvalidFormats(classification.invalid.length));
3097
+ if (classification.alreadyListed.length > 0) parts.push(labels.bulkAlreadyListed(classification.alreadyListed.length));
3098
+ if (classification.duplicatesInInput.length > 0) parts.push(labels.bulkDuplicatesInPaste(classification.duplicatesInInput.length));
3099
+ if (parts.length === 0) return null;
3100
+ return parts.join(". ") + ".";
3101
+ }
3102
+
3103
+ //#endregion
3104
+ //#region src/components/fields/AddressListField/AddressListBulkEntry.tsx
3105
+ /** Bulk paste entry — info callout, textarea, and add action in a compact stack. */
3106
+ function AddressListBulkEntry({ fieldId, value, onAdd, placeholder, formatHint, addressing, maxItems, disabled = false, maxInvalidPreview = 3, labels, modeToggle }) {
3107
+ const atLimit = maxItems != null && value.length >= maxItems;
3108
+ const inputDisabled = disabled || atLimit;
3109
+ const [feedback, setFeedback] = useState(null);
3110
+ const { control, reset, watch } = useForm({
3111
+ defaultValues: { input: "" },
3112
+ mode: "onChange"
3113
+ });
3114
+ const rawInput = watch("input");
3115
+ const classification = useMemo(() => {
3116
+ if (!rawInput.trim()) return null;
3117
+ return classifyAddressCandidates(parseDelimitedAddressInput(rawInput), value, addressing, maxItems);
3118
+ }, [
3119
+ rawInput,
3120
+ value,
3121
+ addressing,
3122
+ maxItems
3123
+ ]);
3124
+ const previewSummary = useMemo(() => buildAddressListPreviewSummary(classification, labels), [classification, labels]);
3125
+ const statusMessage = rawInput.trim() ? previewSummary ?? void 0 : feedback ?? void 0;
3126
+ const handleAdd = useCallback(() => {
3127
+ if (!classification || classification.accepted.length === 0 || atLimit) return;
3128
+ onAdd(classification.accepted);
3129
+ reset({ input: "" });
3130
+ setFeedback(formatAddressBulkSummary(classification, classification.accepted.length, labels));
3131
+ }, [
3132
+ classification,
3133
+ atLimit,
3134
+ onAdd,
3135
+ reset,
3136
+ labels
3137
+ ]);
3138
+ const acceptedCount = classification?.accepted.length ?? 0;
3139
+ const addButtonLabel = acceptedCount > 0 ? labels.addAddressCount(acceptedCount) : labels.addAddresses;
3140
+ return /* @__PURE__ */ jsxs("div", {
3141
+ className: "space-y-3",
3142
+ children: [
3143
+ /* @__PURE__ */ jsxs("div", {
3144
+ className: "flex items-start gap-2 rounded-lg border border-border/70 bg-muted/30 px-3 py-2.5",
3145
+ children: [/* @__PURE__ */ jsx(Info, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }), /* @__PURE__ */ jsx("p", {
3146
+ className: "text-xs leading-relaxed text-muted-foreground",
3147
+ children: formatHint
3148
+ })]
3149
+ }),
3150
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
3151
+ className: "relative",
3152
+ children: /* @__PURE__ */ jsx(TextAreaField, {
3153
+ id: `address-list-bulk-${fieldId}`,
3154
+ name: "input",
3155
+ label: "",
3156
+ placeholder,
3157
+ helperText: statusMessage,
3158
+ control,
3159
+ rows: 4,
3160
+ validation: { required: false },
3161
+ readOnly: inputDisabled
3162
+ })
3163
+ }), modeToggle ? /* @__PURE__ */ jsx("div", {
3164
+ className: "flex justify-end pr-2",
3165
+ children: modeToggle
3166
+ }) : null] }),
3167
+ /* @__PURE__ */ jsxs("div", {
3168
+ className: "flex flex-wrap items-center justify-between gap-2",
3169
+ children: [/* @__PURE__ */ jsxs("div", {
3170
+ className: "min-w-0 flex-1",
3171
+ children: [classification && classification.invalid.length > 0 ? /* @__PURE__ */ jsxs("p", {
3172
+ className: "text-xs text-destructive",
3173
+ children: [
3174
+ labels.invalidPrefix,
3175
+ " ",
3176
+ classification.invalid.slice(0, maxInvalidPreview).join(", "),
3177
+ classification.invalid.length > maxInvalidPreview ? ` ${labels.invalidMore(classification.invalid.length - maxInvalidPreview)}` : ""
3178
+ ]
3179
+ }) : null, atLimit && maxItems != null ? /* @__PURE__ */ jsx("p", {
3180
+ className: "text-xs text-muted-foreground",
3181
+ children: labels.maxItemsReached(maxItems)
3182
+ }) : null]
3183
+ }), /* @__PURE__ */ jsxs(Button, {
3184
+ type: "button",
3185
+ size: "sm",
3186
+ className: "shrink-0",
3187
+ disabled: inputDisabled || acceptedCount === 0,
3188
+ onClick: () => handleAdd(),
3189
+ children: [/* @__PURE__ */ jsx(Plus, { className: "mr-1 size-4" }), addButtonLabel]
3190
+ })]
3191
+ })
3192
+ ]
3193
+ });
3194
+ }
3195
+
3196
+ //#endregion
3197
+ //#region src/components/fields/AddressListField/AddressListEntries.tsx
3198
+ /** Renders committed addresses with remove actions. Shared by single and bulk entry modes. */
3199
+ function AddressListEntries({ value, getExplorerUrl, disabled = false, labels, onRemove }) {
3200
+ if (value.length === 0) return null;
3201
+ return /* @__PURE__ */ jsxs("div", {
3202
+ className: "space-y-1",
3203
+ children: [/* @__PURE__ */ jsx("p", {
3204
+ className: "text-xs text-muted-foreground",
3205
+ children: labels.addressesAdded(value.length)
3206
+ }), /* @__PURE__ */ jsx("div", {
3207
+ className: "max-h-44 space-y-1 overflow-y-auto rounded-md border border-border/70 p-1",
3208
+ children: value.map((address, index) => /* @__PURE__ */ jsxs("div", {
3209
+ className: "flex items-center justify-between rounded bg-muted p-2",
3210
+ children: [/* @__PURE__ */ jsx(AddressDisplay, {
3211
+ address,
3212
+ variant: "inline",
3213
+ truncateWhenLabeled: true,
3214
+ showCopyButton: true,
3215
+ explorerUrl: getExplorerUrl?.(address) ?? void 0
3216
+ }), /* @__PURE__ */ jsx(Button, {
3217
+ type: "button",
3218
+ variant: "ghost",
3219
+ size: "sm",
3220
+ className: "size-7 shrink-0 p-0 text-muted-foreground hover:text-destructive",
3221
+ onClick: () => onRemove(index),
3222
+ disabled,
3223
+ "aria-label": labels.removeAddress(index),
3224
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
3225
+ })]
3226
+ }, `${address}-${index}`))
3227
+ })]
3228
+ });
3229
+ }
3230
+
3231
+ //#endregion
3232
+ //#region src/components/fields/AddressListField/AddressListSingleEntry.tsx
3233
+ /** Single-address entry — inline {@link AddressField} with an Add button (address-book aware). */
3234
+ function AddressListSingleEntry({ fieldId, value, onAdd, placeholder, addressing, maxItems, disabled = false, labels, modeToggle }) {
3235
+ const atLimit = maxItems != null && value.length >= maxItems;
3236
+ const inputDisabled = disabled || atLimit;
3237
+ const { control, reset, getValues, handleSubmit } = useForm({
3238
+ defaultValues: { draft: "" },
3239
+ mode: "onChange"
3240
+ });
3241
+ const trimmedDraft = (useWatch({
3242
+ control,
3243
+ name: "draft"
3244
+ }) ?? "").trim();
3245
+ const validationState = useMemo(() => {
3246
+ if (!trimmedDraft) return {
3247
+ canAdd: false,
3248
+ showInvalid: false
3249
+ };
3250
+ if (value.includes(trimmedDraft)) return {
3251
+ canAdd: false,
3252
+ showInvalid: false
3253
+ };
3254
+ if (atLimit) return {
3255
+ canAdd: false,
3256
+ showInvalid: false
3257
+ };
3258
+ if (addressing && !addressing.isValidAddress(trimmedDraft)) return {
3259
+ canAdd: false,
3260
+ showInvalid: true
3261
+ };
3262
+ return {
3263
+ canAdd: true,
3264
+ showInvalid: false
3265
+ };
3266
+ }, [
3267
+ trimmedDraft,
3268
+ value,
3269
+ atLimit,
3270
+ addressing
3271
+ ]);
3272
+ const submitAdd = useCallback(() => {
3273
+ if (!validationState.canAdd) return;
3274
+ onAdd(getValues("draft").trim());
3275
+ reset({ draft: "" });
3276
+ }, [
3277
+ validationState.canAdd,
3278
+ onAdd,
3279
+ getValues,
3280
+ reset
3281
+ ]);
3282
+ return /* @__PURE__ */ jsxs("div", {
3283
+ className: "space-y-1",
3284
+ children: [/* @__PURE__ */ jsxs("div", {
3285
+ className: "flex items-start gap-2",
3286
+ children: [/* @__PURE__ */ jsxs("div", {
3287
+ className: "min-w-0 flex-1",
3288
+ children: [/* @__PURE__ */ jsx(AddressField, {
3289
+ id: `address-list-single-${fieldId}`,
3290
+ name: "draft",
3291
+ label: "",
3292
+ placeholder,
3293
+ helperText: validationState.showInvalid ? labels.invalidSingleAddress : void 0,
3294
+ control,
3295
+ addressing,
3296
+ validation: { required: false },
3297
+ readOnly: inputDisabled
3298
+ }), modeToggle ? /* @__PURE__ */ jsx("div", {
3299
+ className: "-mt-px flex justify-end pr-2",
3300
+ children: modeToggle
3301
+ }) : null]
3302
+ }), /* @__PURE__ */ jsxs(Button, {
3303
+ type: "button",
3304
+ className: "h-10 shrink-0",
3305
+ disabled: inputDisabled || !validationState.canAdd,
3306
+ onClick: handleSubmit(submitAdd),
3307
+ children: [/* @__PURE__ */ jsx(Plus, { className: "mr-1 size-4" }), labels.addSingleAddress]
3308
+ })]
3309
+ }), atLimit && maxItems != null && /* @__PURE__ */ jsx("p", {
3310
+ className: "text-xs text-muted-foreground",
3311
+ children: labels.maxItemsReached(maxItems)
3312
+ })]
3313
+ });
3314
+ }
3315
+
3316
+ //#endregion
3317
+ //#region src/components/fields/AddressListField/AddressListField.tsx
3318
+ /**
3319
+ * Multi-address list field with single-entry default and optional bulk paste.
3320
+ */
3321
+ function AddressListField({ value, onChange, placeholder, bulkPlaceholder, formatHint, addressing, getExplorerUrl, label, helperText, maxItems, disabled = false, maxInvalidPreview = 3, className, defaultEntryMode = "single", allowModeToggle = true, labels: labelOverrides }) {
3322
+ const fieldId = useId();
3323
+ const labels = useMemo(() => resolveAddressListFieldLabels(labelOverrides), [labelOverrides]);
3324
+ const [entryMode, setEntryMode] = useState(defaultEntryMode);
3325
+ const resolvedBulkPlaceholder = bulkPlaceholder ?? placeholder;
3326
+ const handleRemove = useCallback((index) => {
3327
+ onChange(value.filter((_, currentIndex) => currentIndex !== index));
3328
+ }, [onChange, value]);
3329
+ const handleAddSingle = useCallback((address) => {
3330
+ onChange([...value, address]);
3331
+ }, [onChange, value]);
3332
+ const handleAddBulk = useCallback((addresses) => {
3333
+ onChange([...value, ...addresses]);
3334
+ }, [onChange, value]);
3335
+ const switchToBulk = useCallback(() => setEntryMode("bulk"), []);
3336
+ const switchToSingle = useCallback(() => setEntryMode("single"), []);
3337
+ const toggleClassName = "inline-flex items-center gap-1 rounded-b-md rounded-t-none border border-t-0 border-input bg-muted/40 px-2 py-1 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
3338
+ const modeToggle = !allowModeToggle ? void 0 : entryMode === "single" ? /* @__PURE__ */ jsxs("button", {
3339
+ type: "button",
3340
+ onClick: switchToBulk,
3341
+ className: toggleClassName,
3342
+ children: [/* @__PURE__ */ jsx(ClipboardPaste, { className: "size-3" }), labels.enableBulkEntry]
3343
+ }) : /* @__PURE__ */ jsxs("button", {
3344
+ type: "button",
3345
+ onClick: switchToSingle,
3346
+ className: toggleClassName,
3347
+ children: [/* @__PURE__ */ jsx(PencilLine, { className: "size-3" }), labels.enableSingleEntry]
3348
+ });
3349
+ return /* @__PURE__ */ jsxs("div", {
3350
+ className: cn("space-y-3", className),
3351
+ children: [
3352
+ label ? /* @__PURE__ */ jsx(Label, {
3353
+ className: "text-sm font-medium leading-none",
3354
+ children: label
3355
+ }) : null,
3356
+ helperText && entryMode === "single" ? /* @__PURE__ */ jsx("p", {
3357
+ className: "text-sm leading-relaxed text-muted-foreground",
3358
+ children: helperText
3359
+ }) : null,
3360
+ entryMode === "single" ? /* @__PURE__ */ jsx(AddressListSingleEntry, {
3361
+ fieldId,
3362
+ value,
3363
+ onAdd: handleAddSingle,
3364
+ placeholder,
3365
+ addressing,
3366
+ maxItems,
3367
+ disabled,
3368
+ labels,
3369
+ modeToggle
3370
+ }) : /* @__PURE__ */ jsx(AddressListBulkEntry, {
3371
+ fieldId,
3372
+ value,
3373
+ onAdd: handleAddBulk,
3374
+ placeholder: resolvedBulkPlaceholder,
3375
+ formatHint,
3376
+ addressing,
3377
+ maxItems,
3378
+ disabled,
3379
+ maxInvalidPreview,
3380
+ labels,
3381
+ modeToggle
3382
+ }),
3383
+ /* @__PURE__ */ jsx(AddressListEntries, {
3384
+ value,
3385
+ getExplorerUrl,
3386
+ disabled,
3387
+ labels,
3388
+ onRemove: handleRemove
3389
+ })
3390
+ ]
3391
+ });
3392
+ }
3393
+
2943
3394
  //#endregion
2944
3395
  //#region src/components/fields/address-suggestion/address-suggestion-context.tsx
2945
3396
  /**
@@ -5732,105 +6183,6 @@ function SelectGroupedField({ id, label, placeholder = "Select an option", helpe
5732
6183
  }
5733
6184
  SelectGroupedField.displayName = "SelectGroupedField";
5734
6185
 
5735
- //#endregion
5736
- //#region src/components/fields/TextAreaField.tsx
5737
- /**
5738
- * Multi-line text input field component specifically designed for React Hook Form integration.
5739
- *
5740
- * Architecture flow:
5741
- * 1. Form schemas are generated from contract functions using adapters
5742
- * 2. TransactionForm renders the overall form structure with React Hook Form
5743
- * 3. DynamicFormField selects the appropriate field component based on field type
5744
- * 4. BaseField provides consistent layout and hook form integration
5745
- * 5. This component handles textarea-specific rendering and validation
5746
- *
5747
- * The component includes:
5748
- * - Integration with React Hook Form
5749
- * - Resizable multi-line text input
5750
- * - Character limit support
5751
- * - Customizable validation through adapter integration
5752
- * - Automatic error handling and reporting
5753
- * - Full accessibility support with ARIA attributes
5754
- * - Keyboard navigation with Escape to clear
5755
- */
5756
- function TextAreaField({ id, label, placeholder, helperText, control, name, width = "full", validation, rows = 4, maxLength, validateTextArea }) {
5757
- const isRequired = !!validation?.required;
5758
- const errorId = `${id}-error`;
5759
- const descriptionId = `${id}-description`;
5760
- return /* @__PURE__ */ jsxs("div", {
5761
- className: `flex flex-col gap-2 ${width === "full" ? "w-full" : width === "half" ? "w-1/2" : "w-1/3"}`,
5762
- children: [label && /* @__PURE__ */ jsxs(Label, {
5763
- htmlFor: id,
5764
- children: [
5765
- label,
5766
- " ",
5767
- isRequired && /* @__PURE__ */ jsx("span", {
5768
- className: "text-destructive",
5769
- children: "*"
5770
- })
5771
- ]
5772
- }), /* @__PURE__ */ jsx(Controller, {
5773
- control,
5774
- name,
5775
- rules: { validate: (value) => {
5776
- if (value === void 0 || value === null || value === "") return validation?.required ? "This field is required" : true;
5777
- if (validateTextArea && value) {
5778
- const validation$1 = validateTextArea(value);
5779
- if (validation$1 !== true && typeof validation$1 === "string") return validation$1;
5780
- }
5781
- if (maxLength && typeof value === "string" && value.length > maxLength) return `Maximum length is ${maxLength} characters`;
5782
- return true;
5783
- } },
5784
- render: ({ field, fieldState: { error, isTouched } }) => {
5785
- const hasError = !!error;
5786
- const shouldShowError = hasError && isTouched;
5787
- const validationClasses = getValidationStateClasses(error, isTouched);
5788
- const accessibilityProps = getAccessibilityProps({
5789
- id,
5790
- hasError,
5791
- isRequired,
5792
- hasHelperText: !!helperText
5793
- });
5794
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5795
- /* @__PURE__ */ jsx(Textarea, {
5796
- ...field,
5797
- id,
5798
- placeholder,
5799
- rows,
5800
- maxLength,
5801
- className: validationClasses,
5802
- value: field.value ?? "",
5803
- ...accessibilityProps,
5804
- "aria-describedby": `${helperText ? descriptionId : ""} ${hasError ? errorId : ""}`,
5805
- onKeyDown: handleEscapeKey((value) => {
5806
- if (typeof field.onChange === "function") field.onChange(value);
5807
- }, field.value)
5808
- }),
5809
- maxLength && typeof field.value === "string" && /* @__PURE__ */ jsxs("div", {
5810
- className: "text-muted-foreground text-right text-xs",
5811
- children: [
5812
- field.value.length,
5813
- "/",
5814
- maxLength
5815
- ]
5816
- }),
5817
- helperText && /* @__PURE__ */ jsx("div", {
5818
- id: descriptionId,
5819
- className: "text-muted-foreground text-sm",
5820
- children: helperText
5821
- }),
5822
- /* @__PURE__ */ jsx(ErrorMessage, {
5823
- error,
5824
- id: errorId,
5825
- message: shouldShowError ? error?.message : void 0
5826
- })
5827
- ] });
5828
- }
5829
- })]
5830
- });
5831
- }
5832
- TextAreaField.displayName = "TextAreaField";
5833
-
5834
6186
  //#endregion
5835
6187
  //#region src/components/fields/TextField.tsx
5836
6188
  /**
@@ -6422,5 +6774,5 @@ const Toaster = ({ ...props }) => {
6422
6774
  };
6423
6775
 
6424
6776
  //#endregion
6425
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AddressDisplay, AddressField, AddressLabelProvider, AddressSuggestionProvider, Alert, AlertDescription, AlertTitle, AmountField, ArrayField, ArrayObjectField, Banner, BaseField, BigIntField, BooleanField, Button, BytesField, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, DateRangePicker, DateTimeField, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EcosystemDropdown, EcosystemIcon, EmptyState, EnumField, ErrorMessage, ExternalLink, FileUploadField, Footer, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Header, INTEGER_HTML_PATTERN, INTEGER_INPUT_PATTERN, INTEGER_PATTERN, Input, Label, LoadingButton, MapEntryRow, MapField, MidnightIcon, NetworkAvailabilityNotice, NetworkErrorNotificationProvider, NetworkIcon, NetworkSelector, NetworkServiceErrorBanner, NetworkStatusBadge, NumberField, ObjectField, OverflowMenu, PasswordField, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioField, RadioGroup, RadioGroupItem, RelayerDetailsCard, Select, SelectContent, SelectField, SelectGroup, SelectGroupedField, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarButton, SidebarGroup, SidebarLayout, SidebarSection, Tabs, TabsContent, TabsList, TabsTrigger, TextAreaField, TextField, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UrlField, VERSION, ViewContractStateButton, WizardLayout, WizardNavigation, WizardStepper, buttonVariants, computeChildTouched, createFocusManager, createValidationResult, formatValidationError, getAccessibilityProps, getDescribedById, getErrorMessage, getValidationStateClasses, getWidthClasses, handleEscapeKey, handleKeyboardEvent, handleNumericKeys, handleToggleKeys, handleValidationError, hasFieldError, isDuplicateMapKey, useAddressLabel, useAddressSuggestions, useDuplicateKeyIndexes, useMapFieldSync, useNetworkErrorAwareAdapter, useNetworkErrorReporter, useNetworkErrors, validateField, validateMapEntries, validateMapStructure };
6777
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AddressDisplay, AddressField, AddressLabelProvider, AddressListField, AddressSuggestionProvider, Alert, AlertDescription, AlertTitle, AmountField, ArrayField, ArrayObjectField, Banner, BaseField, BigIntField, BooleanField, Button, BytesField, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, DEFAULT_ADDRESS_LIST_FIELD_LABELS, DateRangePicker, DateTimeField, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EcosystemDropdown, EcosystemIcon, EmptyState, EnumField, ErrorMessage, ExternalLink, FileUploadField, Footer, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Header, INTEGER_HTML_PATTERN, INTEGER_INPUT_PATTERN, INTEGER_PATTERN, Input, Label, LoadingButton, MapEntryRow, MapField, MidnightIcon, NetworkAvailabilityNotice, NetworkErrorNotificationProvider, NetworkIcon, NetworkSelector, NetworkServiceErrorBanner, NetworkStatusBadge, NumberField, ObjectField, OverflowMenu, PasswordField, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioField, RadioGroup, RadioGroupItem, RelayerDetailsCard, Select, SelectContent, SelectField, SelectGroup, SelectGroupedField, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SidebarButton, SidebarGroup, SidebarLayout, SidebarSection, Tabs, TabsContent, TabsList, TabsTrigger, TextAreaField, TextField, Textarea, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UrlField, VERSION, ViewContractStateButton, WizardLayout, WizardNavigation, WizardStepper, buildAddressListPreviewSummary, buttonVariants, computeChildTouched, createFocusManager, createValidationResult, formatAddressBulkSummary, formatValidationError, getAccessibilityProps, getDescribedById, getErrorMessage, getValidationStateClasses, getWidthClasses, handleEscapeKey, handleKeyboardEvent, handleNumericKeys, handleToggleKeys, handleValidationError, hasFieldError, isDuplicateMapKey, resolveAddressListFieldLabels, useAddressLabel, useAddressSuggestions, useDuplicateKeyIndexes, useMapFieldSync, useNetworkErrorAwareAdapter, useNetworkErrorReporter, useNetworkErrors, validateField, validateMapEntries, validateMapStructure };
6426
6778
  //# sourceMappingURL=index.mjs.map