@openzeppelin/ui-components 3.0.0 → 3.1.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.cjs +387 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -8
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +144 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +384 -130
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -10
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { cva } from "class-variance-authority";
|
|
|
4
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";
|
|
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, getInvalidUrlMessage, getServiceDisplayName, 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.
|
|
29
|
+
const VERSION = "3.1.0";
|
|
30
30
|
|
|
31
31
|
//#endregion
|
|
32
32
|
//#region src/components/ui/accordion.tsx
|
|
@@ -1336,7 +1336,7 @@ function NetworkIcon({ network, className, size = 16, variant = "branded" }) {
|
|
|
1336
1336
|
//#endregion
|
|
1337
1337
|
//#region src/components/ui/network-selector.tsx
|
|
1338
1338
|
/** Searchable dropdown selector for blockchain networks with optional grouping and multi-select. */
|
|
1339
|
-
function NetworkSelector({ networks, getNetworkLabel, getNetworkIcon, getNetworkType, getNetworkId, groupByEcosystem = false, getEcosystem, filterNetwork, className, placeholder = "Select Network", ...modeProps }) {
|
|
1339
|
+
function NetworkSelector({ networks, getNetworkLabel, getNetworkIcon, getNetworkType, getNetworkId, groupByEcosystem = false, getEcosystem, filterNetwork, isNetworkDisabled, getNetworkDisabledLabel, className, placeholder = "Select Network", ...modeProps }) {
|
|
1340
1340
|
const [open, setOpen] = React$1.useState(false);
|
|
1341
1341
|
const [searchQuery, setSearchQuery] = React$1.useState("");
|
|
1342
1342
|
const isMultiple = modeProps.multiple === true;
|
|
@@ -1377,6 +1377,7 @@ function NetworkSelector({ networks, getNetworkLabel, getNetworkIcon, getNetwork
|
|
|
1377
1377
|
getNetworkId
|
|
1378
1378
|
]);
|
|
1379
1379
|
const handleSelect = React$1.useCallback((network) => {
|
|
1380
|
+
if (isNetworkDisabled?.(network)) return;
|
|
1380
1381
|
if (isMultiple && selectedNetworkIds && onSelectionChange) {
|
|
1381
1382
|
const id = getNetworkId(network);
|
|
1382
1383
|
onSelectionChange(selectedNetworkIds.includes(id) ? selectedNetworkIds.filter((x) => x !== id) : [...selectedNetworkIds, id]);
|
|
@@ -1389,7 +1390,8 @@ function NetworkSelector({ networks, getNetworkLabel, getNetworkIcon, getNetwork
|
|
|
1389
1390
|
selectedNetworkIds,
|
|
1390
1391
|
onSelectionChange,
|
|
1391
1392
|
onSelectNetwork,
|
|
1392
|
-
getNetworkId
|
|
1393
|
+
getNetworkId,
|
|
1394
|
+
isNetworkDisabled
|
|
1393
1395
|
]);
|
|
1394
1396
|
const handleClearAll = React$1.useCallback(() => {
|
|
1395
1397
|
if (isMultiple && onSelectionChange) onSelectionChange([]);
|
|
@@ -1475,31 +1477,47 @@ function NetworkSelector({ networks, getNetworkLabel, getNetworkIcon, getNetwork
|
|
|
1475
1477
|
className: "text-xs font-medium text-muted-foreground",
|
|
1476
1478
|
children: group
|
|
1477
1479
|
}),
|
|
1478
|
-
/* @__PURE__ */ jsx(DropdownMenuGroup, { children: groupNetworks.map((network) =>
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1480
|
+
/* @__PURE__ */ jsx(DropdownMenuGroup, { children: groupNetworks.map((network) => {
|
|
1481
|
+
const disabled = isNetworkDisabled?.(network) ?? false;
|
|
1482
|
+
const disabledLabel = disabled ? getNetworkDisabledLabel?.(network) : void 0;
|
|
1483
|
+
return /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
1484
|
+
disabled,
|
|
1485
|
+
onSelect: (e) => {
|
|
1486
|
+
if (disabled) {
|
|
1487
|
+
e.preventDefault();
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (isMultiple) e.preventDefault();
|
|
1491
|
+
handleSelect(network);
|
|
1492
|
+
},
|
|
1493
|
+
className: cn("gap-2", disabled && "cursor-not-allowed opacity-60"),
|
|
1494
|
+
children: [
|
|
1495
|
+
isMultiple ? /* @__PURE__ */ jsx("div", {
|
|
1496
|
+
className: cn("flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border transition-colors", isSelected(network) ? "border-selected bg-selected text-selected-foreground" : "border-input bg-background", disabled && "opacity-50"),
|
|
1497
|
+
children: isSelected(network) && /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" })
|
|
1498
|
+
}) : null,
|
|
1499
|
+
getNetworkIcon?.(network),
|
|
1500
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1501
|
+
className: "flex flex-1 items-center gap-2 min-w-0",
|
|
1502
|
+
children: [
|
|
1503
|
+
/* @__PURE__ */ jsx("span", {
|
|
1504
|
+
className: "truncate",
|
|
1505
|
+
children: getNetworkLabel(network)
|
|
1506
|
+
}),
|
|
1507
|
+
getNetworkType && /* @__PURE__ */ jsx("span", {
|
|
1508
|
+
className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 text-[10px] font-medium uppercase text-muted-foreground",
|
|
1509
|
+
children: getNetworkType(network)
|
|
1510
|
+
}),
|
|
1511
|
+
disabledLabel ? /* @__PURE__ */ jsx("span", {
|
|
1512
|
+
className: "shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
|
|
1513
|
+
children: disabledLabel
|
|
1514
|
+
}) : null
|
|
1515
|
+
]
|
|
1516
|
+
}),
|
|
1517
|
+
!isMultiple && isSelected(network) && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 text-selected opacity-100" })
|
|
1518
|
+
]
|
|
1519
|
+
}, getNetworkId(network));
|
|
1520
|
+
}) }),
|
|
1503
1521
|
index < Object.keys(groupedNetworks).length - 1 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
|
|
1504
1522
|
] }, group))]
|
|
1505
1523
|
})]
|
|
@@ -2922,6 +2940,312 @@ function AddressField({ id, label, placeholder, helperText, control, name, width
|
|
|
2922
2940
|
}
|
|
2923
2941
|
AddressField.displayName = "AddressField";
|
|
2924
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
|
+
addAddresses: "Add addresses",
|
|
3049
|
+
addAddressCount: (count) => `Add ${count} address${count === 1 ? "" : "es"}`,
|
|
3050
|
+
invalidPrefix: "Invalid:",
|
|
3051
|
+
invalidMore: (extraCount) => `(+${extraCount} more)`,
|
|
3052
|
+
maxItemsReached: (maxItems) => `Maximum of ${maxItems} addresses reached.`,
|
|
3053
|
+
addressesAdded: (count) => `${count} address${count === 1 ? "" : "es"} added`,
|
|
3054
|
+
removeAddress: (index) => `Remove address ${index + 1}`,
|
|
3055
|
+
previewReadyToAdd: (count) => `${count} ready to add`,
|
|
3056
|
+
previewInvalid: (count) => `${count} invalid`,
|
|
3057
|
+
previewAlreadyListed: (count) => `${count} already listed`,
|
|
3058
|
+
previewDuplicateInPaste: (count) => `${count} duplicate in paste`,
|
|
3059
|
+
bulkNothingAdded: "No addresses were added. Check formatting and try again.",
|
|
3060
|
+
bulkAdded: (count) => `Added ${count} address${count === 1 ? "" : "es"}`,
|
|
3061
|
+
bulkInvalidFormats: (count) => `${count} invalid format${count === 1 ? "" : "s"}`,
|
|
3062
|
+
bulkAlreadyListed: (count) => `${count} already listed`,
|
|
3063
|
+
bulkDuplicatesInPaste: (count) => `${count} duplicate${count === 1 ? "" : "s"} in paste`
|
|
3064
|
+
};
|
|
3065
|
+
/** Merges {@link DEFAULT_ADDRESS_LIST_FIELD_LABELS} with optional overrides. */
|
|
3066
|
+
function resolveAddressListFieldLabels(overrides) {
|
|
3067
|
+
return {
|
|
3068
|
+
...DEFAULT_ADDRESS_LIST_FIELD_LABELS,
|
|
3069
|
+
...overrides
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
/** Builds the inline helper preview shown while the user types in the draft textarea. */
|
|
3073
|
+
function buildAddressListPreviewSummary(classification, labels) {
|
|
3074
|
+
if (!classification) return null;
|
|
3075
|
+
const skipped = classification.invalid.length + classification.alreadyListed.length + classification.duplicatesInInput.length;
|
|
3076
|
+
if (classification.accepted.length === 0 && skipped === 0) return null;
|
|
3077
|
+
const parts = [];
|
|
3078
|
+
if (classification.accepted.length > 0) parts.push(labels.previewReadyToAdd(classification.accepted.length));
|
|
3079
|
+
if (classification.invalid.length > 0) parts.push(labels.previewInvalid(classification.invalid.length));
|
|
3080
|
+
if (classification.alreadyListed.length > 0) parts.push(labels.previewAlreadyListed(classification.alreadyListed.length));
|
|
3081
|
+
if (classification.duplicatesInInput.length > 0) parts.push(labels.previewDuplicateInPaste(classification.duplicatesInInput.length));
|
|
3082
|
+
return parts.join(" · ");
|
|
3083
|
+
}
|
|
3084
|
+
/** Formats post-add feedback after the user commits parsed addresses to the list. */
|
|
3085
|
+
function formatAddressBulkSummary(classification, addedCount, labels) {
|
|
3086
|
+
if (addedCount === 0 && classification.accepted.length === 0) {
|
|
3087
|
+
if (classification.invalid.length + classification.alreadyListed.length + classification.duplicatesInInput.length === 0) return null;
|
|
3088
|
+
return labels.bulkNothingAdded;
|
|
3089
|
+
}
|
|
3090
|
+
const parts = [];
|
|
3091
|
+
if (addedCount > 0) parts.push(labels.bulkAdded(addedCount));
|
|
3092
|
+
if (classification.invalid.length > 0) parts.push(labels.bulkInvalidFormats(classification.invalid.length));
|
|
3093
|
+
if (classification.alreadyListed.length > 0) parts.push(labels.bulkAlreadyListed(classification.alreadyListed.length));
|
|
3094
|
+
if (classification.duplicatesInInput.length > 0) parts.push(labels.bulkDuplicatesInPaste(classification.duplicatesInInput.length));
|
|
3095
|
+
if (parts.length === 0) return null;
|
|
3096
|
+
return parts.join(". ") + ".";
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
//#endregion
|
|
3100
|
+
//#region src/components/fields/AddressListField/AddressListField.tsx
|
|
3101
|
+
/**
|
|
3102
|
+
* Multi-address list field for bulk paste workflows.
|
|
3103
|
+
*
|
|
3104
|
+
* Architecture flow:
|
|
3105
|
+
* 1. App or wizard steps hold the committed address list in local or form state
|
|
3106
|
+
* 2. AddressListField renders a draft textarea plus the committed list rows
|
|
3107
|
+
* 3. Parsing and classification run via `@openzeppelin/ui-utils` helpers
|
|
3108
|
+
* 4. Optional {@link AddressingCapability} validates candidates before add
|
|
3109
|
+
* 5. {@link AddressDisplay} renders each committed row with copy and explorer links
|
|
3110
|
+
*
|
|
3111
|
+
* The component includes:
|
|
3112
|
+
* - Delimiter-aware bulk paste (newlines, commas, semicolons)
|
|
3113
|
+
* - Live preview of accepted, invalid, duplicate, and already-listed candidates
|
|
3114
|
+
* - Optional `maxItems` cap with disabled input at the limit
|
|
3115
|
+
* - Removable list rows with accessible remove buttons
|
|
3116
|
+
* - Caller-controlled copy for placeholders and format hints
|
|
3117
|
+
* - Optional `labels` overrides for built-in button and status strings
|
|
3118
|
+
*
|
|
3119
|
+
* @example
|
|
3120
|
+
* ```tsx
|
|
3121
|
+
* const [addresses, setAddresses] = useState<string[]>([]);
|
|
3122
|
+
*
|
|
3123
|
+
* <AddressListField
|
|
3124
|
+
* value={addresses}
|
|
3125
|
+
* onChange={setAddresses}
|
|
3126
|
+
* placeholder="Paste addresses (one per line or comma-separated)"
|
|
3127
|
+
* formatHint="Each entry is validated before it is added."
|
|
3128
|
+
* addressing={capabilities}
|
|
3129
|
+
* />
|
|
3130
|
+
* ```
|
|
3131
|
+
*/
|
|
3132
|
+
function AddressListField({ value, onChange, placeholder, formatHint, addressing, getExplorerUrl, label, helperText, maxItems, disabled = false, maxInvalidPreview = 3, className, labels: labelOverrides }) {
|
|
3133
|
+
const fieldId = useId();
|
|
3134
|
+
const labels = useMemo(() => resolveAddressListFieldLabels(labelOverrides), [labelOverrides]);
|
|
3135
|
+
const atLimit = maxItems != null && value.length >= maxItems;
|
|
3136
|
+
const inputDisabled = disabled || atLimit;
|
|
3137
|
+
const [feedback, setFeedback] = useState(null);
|
|
3138
|
+
const { control, reset, watch } = useForm({
|
|
3139
|
+
defaultValues: { input: "" },
|
|
3140
|
+
mode: "onChange"
|
|
3141
|
+
});
|
|
3142
|
+
const rawInput = watch("input");
|
|
3143
|
+
const classification = useMemo(() => {
|
|
3144
|
+
if (!rawInput.trim()) return null;
|
|
3145
|
+
return classifyAddressCandidates(parseDelimitedAddressInput(rawInput), value, addressing, maxItems);
|
|
3146
|
+
}, [
|
|
3147
|
+
rawInput,
|
|
3148
|
+
value,
|
|
3149
|
+
addressing,
|
|
3150
|
+
maxItems
|
|
3151
|
+
]);
|
|
3152
|
+
const previewSummary = useMemo(() => buildAddressListPreviewSummary(classification, labels), [classification, labels]);
|
|
3153
|
+
const inlineHelperText = rawInput.trim() ? previewSummary ?? void 0 : feedback ?? void 0;
|
|
3154
|
+
const handleAdd = useCallback(() => {
|
|
3155
|
+
if (!classification || classification.accepted.length === 0 || atLimit) return;
|
|
3156
|
+
onChange([...value, ...classification.accepted]);
|
|
3157
|
+
reset({ input: "" });
|
|
3158
|
+
setFeedback(formatAddressBulkSummary(classification, classification.accepted.length, labels));
|
|
3159
|
+
}, [
|
|
3160
|
+
classification,
|
|
3161
|
+
atLimit,
|
|
3162
|
+
onChange,
|
|
3163
|
+
value,
|
|
3164
|
+
reset,
|
|
3165
|
+
labels
|
|
3166
|
+
]);
|
|
3167
|
+
const handleRemove = useCallback((index) => {
|
|
3168
|
+
onChange(value.filter((_, currentIndex) => currentIndex !== index));
|
|
3169
|
+
setFeedback(null);
|
|
3170
|
+
}, [onChange, value]);
|
|
3171
|
+
const acceptedCount = classification?.accepted.length ?? 0;
|
|
3172
|
+
const addButtonLabel = acceptedCount > 0 ? labels.addAddressCount(acceptedCount) : labels.addAddresses;
|
|
3173
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3174
|
+
className: cn("space-y-3", className),
|
|
3175
|
+
children: [
|
|
3176
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3177
|
+
className: "space-y-1",
|
|
3178
|
+
children: [helperText ? /* @__PURE__ */ jsx("p", {
|
|
3179
|
+
className: "text-xs text-muted-foreground",
|
|
3180
|
+
children: helperText
|
|
3181
|
+
}) : null, /* @__PURE__ */ jsx("p", {
|
|
3182
|
+
className: "text-xs text-muted-foreground",
|
|
3183
|
+
children: formatHint
|
|
3184
|
+
})]
|
|
3185
|
+
}),
|
|
3186
|
+
/* @__PURE__ */ jsx(TextAreaField, {
|
|
3187
|
+
id: `address-list-field-${fieldId}`,
|
|
3188
|
+
name: "input",
|
|
3189
|
+
label: label ?? "",
|
|
3190
|
+
placeholder,
|
|
3191
|
+
helperText: inlineHelperText,
|
|
3192
|
+
control,
|
|
3193
|
+
rows: 4,
|
|
3194
|
+
validation: { required: false },
|
|
3195
|
+
readOnly: inputDisabled
|
|
3196
|
+
}),
|
|
3197
|
+
classification && classification.invalid.length > 0 && /* @__PURE__ */ jsxs("p", {
|
|
3198
|
+
className: "text-xs text-destructive",
|
|
3199
|
+
children: [
|
|
3200
|
+
labels.invalidPrefix,
|
|
3201
|
+
" ",
|
|
3202
|
+
classification.invalid.slice(0, maxInvalidPreview).join(", "),
|
|
3203
|
+
classification.invalid.length > maxInvalidPreview ? ` ${labels.invalidMore(classification.invalid.length - maxInvalidPreview)}` : ""
|
|
3204
|
+
]
|
|
3205
|
+
}),
|
|
3206
|
+
/* @__PURE__ */ jsx(Button, {
|
|
3207
|
+
type: "button",
|
|
3208
|
+
size: "sm",
|
|
3209
|
+
disabled: inputDisabled || acceptedCount === 0,
|
|
3210
|
+
onClick: () => handleAdd(),
|
|
3211
|
+
children: addButtonLabel
|
|
3212
|
+
}),
|
|
3213
|
+
atLimit && maxItems != null && /* @__PURE__ */ jsx("p", {
|
|
3214
|
+
className: "text-xs text-muted-foreground",
|
|
3215
|
+
children: labels.maxItemsReached(maxItems)
|
|
3216
|
+
}),
|
|
3217
|
+
value.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
3218
|
+
className: "space-y-1",
|
|
3219
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
3220
|
+
className: "text-xs text-muted-foreground",
|
|
3221
|
+
children: labels.addressesAdded(value.length)
|
|
3222
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
3223
|
+
className: "max-h-44 space-y-1 overflow-y-auto rounded-md border border-border/70 p-1",
|
|
3224
|
+
children: value.map((address, index) => /* @__PURE__ */ jsxs("div", {
|
|
3225
|
+
className: "flex items-center justify-between rounded bg-muted p-2",
|
|
3226
|
+
children: [/* @__PURE__ */ jsx(AddressDisplay, {
|
|
3227
|
+
address,
|
|
3228
|
+
variant: "inline",
|
|
3229
|
+
truncateWhenLabeled: true,
|
|
3230
|
+
showCopyButton: true,
|
|
3231
|
+
explorerUrl: getExplorerUrl?.(address) ?? void 0
|
|
3232
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
3233
|
+
type: "button",
|
|
3234
|
+
variant: "ghost",
|
|
3235
|
+
size: "sm",
|
|
3236
|
+
className: "size-7 shrink-0 p-0 text-muted-foreground hover:text-destructive",
|
|
3237
|
+
onClick: () => handleRemove(index),
|
|
3238
|
+
disabled,
|
|
3239
|
+
"aria-label": labels.removeAddress(index),
|
|
3240
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3.5" })
|
|
3241
|
+
})]
|
|
3242
|
+
}, `${address}-${index}`))
|
|
3243
|
+
})]
|
|
3244
|
+
})
|
|
3245
|
+
]
|
|
3246
|
+
});
|
|
3247
|
+
}
|
|
3248
|
+
|
|
2925
3249
|
//#endregion
|
|
2926
3250
|
//#region src/components/fields/address-suggestion/address-suggestion-context.tsx
|
|
2927
3251
|
/**
|
|
@@ -5714,105 +6038,6 @@ function SelectGroupedField({ id, label, placeholder = "Select an option", helpe
|
|
|
5714
6038
|
}
|
|
5715
6039
|
SelectGroupedField.displayName = "SelectGroupedField";
|
|
5716
6040
|
|
|
5717
|
-
//#endregion
|
|
5718
|
-
//#region src/components/fields/TextAreaField.tsx
|
|
5719
|
-
/**
|
|
5720
|
-
* Multi-line text input field component specifically designed for React Hook Form integration.
|
|
5721
|
-
*
|
|
5722
|
-
* Architecture flow:
|
|
5723
|
-
* 1. Form schemas are generated from contract functions using adapters
|
|
5724
|
-
* 2. TransactionForm renders the overall form structure with React Hook Form
|
|
5725
|
-
* 3. DynamicFormField selects the appropriate field component based on field type
|
|
5726
|
-
* 4. BaseField provides consistent layout and hook form integration
|
|
5727
|
-
* 5. This component handles textarea-specific rendering and validation
|
|
5728
|
-
*
|
|
5729
|
-
* The component includes:
|
|
5730
|
-
* - Integration with React Hook Form
|
|
5731
|
-
* - Resizable multi-line text input
|
|
5732
|
-
* - Character limit support
|
|
5733
|
-
* - Customizable validation through adapter integration
|
|
5734
|
-
* - Automatic error handling and reporting
|
|
5735
|
-
* - Full accessibility support with ARIA attributes
|
|
5736
|
-
* - Keyboard navigation with Escape to clear
|
|
5737
|
-
*/
|
|
5738
|
-
function TextAreaField({ id, label, placeholder, helperText, control, name, width = "full", validation, rows = 4, maxLength, validateTextArea }) {
|
|
5739
|
-
const isRequired = !!validation?.required;
|
|
5740
|
-
const errorId = `${id}-error`;
|
|
5741
|
-
const descriptionId = `${id}-description`;
|
|
5742
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
5743
|
-
className: `flex flex-col gap-2 ${width === "full" ? "w-full" : width === "half" ? "w-1/2" : "w-1/3"}`,
|
|
5744
|
-
children: [label && /* @__PURE__ */ jsxs(Label, {
|
|
5745
|
-
htmlFor: id,
|
|
5746
|
-
children: [
|
|
5747
|
-
label,
|
|
5748
|
-
" ",
|
|
5749
|
-
isRequired && /* @__PURE__ */ jsx("span", {
|
|
5750
|
-
className: "text-destructive",
|
|
5751
|
-
children: "*"
|
|
5752
|
-
})
|
|
5753
|
-
]
|
|
5754
|
-
}), /* @__PURE__ */ jsx(Controller, {
|
|
5755
|
-
control,
|
|
5756
|
-
name,
|
|
5757
|
-
rules: { validate: (value) => {
|
|
5758
|
-
if (value === void 0 || value === null || value === "") return validation?.required ? "This field is required" : true;
|
|
5759
|
-
if (validateTextArea && value) {
|
|
5760
|
-
const validation$1 = validateTextArea(value);
|
|
5761
|
-
if (validation$1 !== true && typeof validation$1 === "string") return validation$1;
|
|
5762
|
-
}
|
|
5763
|
-
if (maxLength && typeof value === "string" && value.length > maxLength) return `Maximum length is ${maxLength} characters`;
|
|
5764
|
-
return true;
|
|
5765
|
-
} },
|
|
5766
|
-
render: ({ field, fieldState: { error, isTouched } }) => {
|
|
5767
|
-
const hasError = !!error;
|
|
5768
|
-
const shouldShowError = hasError && isTouched;
|
|
5769
|
-
const validationClasses = getValidationStateClasses(error, isTouched);
|
|
5770
|
-
const accessibilityProps = getAccessibilityProps({
|
|
5771
|
-
id,
|
|
5772
|
-
hasError,
|
|
5773
|
-
isRequired,
|
|
5774
|
-
hasHelperText: !!helperText
|
|
5775
|
-
});
|
|
5776
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5777
|
-
/* @__PURE__ */ jsx(Textarea, {
|
|
5778
|
-
...field,
|
|
5779
|
-
id,
|
|
5780
|
-
placeholder,
|
|
5781
|
-
rows,
|
|
5782
|
-
maxLength,
|
|
5783
|
-
className: validationClasses,
|
|
5784
|
-
value: field.value ?? "",
|
|
5785
|
-
...accessibilityProps,
|
|
5786
|
-
"aria-describedby": `${helperText ? descriptionId : ""} ${hasError ? errorId : ""}`,
|
|
5787
|
-
onKeyDown: handleEscapeKey((value) => {
|
|
5788
|
-
if (typeof field.onChange === "function") field.onChange(value);
|
|
5789
|
-
}, field.value)
|
|
5790
|
-
}),
|
|
5791
|
-
maxLength && typeof field.value === "string" && /* @__PURE__ */ jsxs("div", {
|
|
5792
|
-
className: "text-muted-foreground text-right text-xs",
|
|
5793
|
-
children: [
|
|
5794
|
-
field.value.length,
|
|
5795
|
-
"/",
|
|
5796
|
-
maxLength
|
|
5797
|
-
]
|
|
5798
|
-
}),
|
|
5799
|
-
helperText && /* @__PURE__ */ jsx("div", {
|
|
5800
|
-
id: descriptionId,
|
|
5801
|
-
className: "text-muted-foreground text-sm",
|
|
5802
|
-
children: helperText
|
|
5803
|
-
}),
|
|
5804
|
-
/* @__PURE__ */ jsx(ErrorMessage, {
|
|
5805
|
-
error,
|
|
5806
|
-
id: errorId,
|
|
5807
|
-
message: shouldShowError ? error?.message : void 0
|
|
5808
|
-
})
|
|
5809
|
-
] });
|
|
5810
|
-
}
|
|
5811
|
-
})]
|
|
5812
|
-
});
|
|
5813
|
-
}
|
|
5814
|
-
TextAreaField.displayName = "TextAreaField";
|
|
5815
|
-
|
|
5816
6041
|
//#endregion
|
|
5817
6042
|
//#region src/components/fields/TextField.tsx
|
|
5818
6043
|
/**
|
|
@@ -6356,6 +6581,35 @@ function NetworkServiceErrorBanner({ networkConfig, serviceType, errorMessage, t
|
|
|
6356
6581
|
});
|
|
6357
6582
|
}
|
|
6358
6583
|
|
|
6584
|
+
//#endregion
|
|
6585
|
+
//#region src/components/network-availability/NetworkAvailabilityNotice.tsx
|
|
6586
|
+
/**
|
|
6587
|
+
* Banner shown when the deployment disables mainnet or specific network IDs.
|
|
6588
|
+
* Hidden when no network availability policy is active.
|
|
6589
|
+
*/
|
|
6590
|
+
function NetworkAvailabilityNotice({ appName, selfHostRepoUrl, className }) {
|
|
6591
|
+
if (!isNetworkAvailabilityPolicyActive()) return null;
|
|
6592
|
+
const { title, descriptionBeforeLink, descriptionLinkLabel } = getHostedNetworkAvailabilityNoticeCopy(appName);
|
|
6593
|
+
return /* @__PURE__ */ jsxs(Alert, {
|
|
6594
|
+
className,
|
|
6595
|
+
children: [
|
|
6596
|
+
/* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }),
|
|
6597
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: title }),
|
|
6598
|
+
/* @__PURE__ */ jsxs(AlertDescription, { children: [
|
|
6599
|
+
descriptionBeforeLink,
|
|
6600
|
+
/* @__PURE__ */ jsx("a", {
|
|
6601
|
+
href: selfHostRepoUrl,
|
|
6602
|
+
target: "_blank",
|
|
6603
|
+
rel: "noopener noreferrer",
|
|
6604
|
+
className: "font-medium underline underline-offset-4 hover:text-foreground",
|
|
6605
|
+
children: descriptionLinkLabel
|
|
6606
|
+
}),
|
|
6607
|
+
"."
|
|
6608
|
+
] })
|
|
6609
|
+
]
|
|
6610
|
+
});
|
|
6611
|
+
}
|
|
6612
|
+
|
|
6359
6613
|
//#endregion
|
|
6360
6614
|
//#region src/components/ui/sonner.tsx
|
|
6361
6615
|
const Toaster = ({ ...props }) => {
|
|
@@ -6375,5 +6629,5 @@ const Toaster = ({ ...props }) => {
|
|
|
6375
6629
|
};
|
|
6376
6630
|
|
|
6377
6631
|
//#endregion
|
|
6378
|
-
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, 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 };
|
|
6632
|
+
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 };
|
|
6379
6633
|
//# sourceMappingURL=index.mjs.map
|