@openzeppelin/ui-components 1.5.0 → 1.6.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 +92 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -8
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +50 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +91 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import * as React$1 from "react";
|
|
|
6
6
|
import React, { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { cn, getDefaultValueForType, getInvalidUrlMessage, getServiceDisplayName, isValidUrl, truncateMiddle, validateBytesSimple } from "@openzeppelin/ui-utils";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
9
10
|
import { Slot, Slottable } from "@radix-ui/react-slot";
|
|
10
11
|
import { DayPicker } from "react-day-picker";
|
|
11
12
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
@@ -20,7 +21,6 @@ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
|
20
21
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
21
22
|
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
|
22
23
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
23
|
-
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
24
24
|
import { isMapEntryArray } from "@openzeppelin/ui-types";
|
|
25
25
|
import { Toaster as Toaster$1, toast } from "sonner";
|
|
26
26
|
import { useTheme } from "next-themes";
|
|
@@ -102,6 +102,19 @@ const AccordionContent = React$1.forwardRef(({ className, children, variant: var
|
|
|
102
102
|
});
|
|
103
103
|
AccordionContent.displayName = "AccordionContent";
|
|
104
104
|
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/components/ui/tooltip.tsx
|
|
107
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
108
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
109
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
110
|
+
const TooltipContent = React$1.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Content, {
|
|
111
|
+
ref,
|
|
112
|
+
sideOffset,
|
|
113
|
+
className: cn("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md px-3 py-1.5 text-xs", className),
|
|
114
|
+
...props
|
|
115
|
+
}) }));
|
|
116
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
117
|
+
|
|
105
118
|
//#endregion
|
|
106
119
|
//#region src/components/ui/address-display/context.ts
|
|
107
120
|
/**
|
|
@@ -114,8 +127,20 @@ const AddressLabelContext = createContext(null);
|
|
|
114
127
|
//#endregion
|
|
115
128
|
//#region src/components/ui/address-display/address-display.tsx
|
|
116
129
|
/**
|
|
130
|
+
* True when the primary input can hover (e.g. desktop with mouse).
|
|
131
|
+
* Touch-first phones typically report false. SSR assumes hover-capable.
|
|
132
|
+
*/
|
|
133
|
+
function usePrefersHover() {
|
|
134
|
+
return React$1.useSyncExternalStore(React$1.useCallback((onStoreChange) => {
|
|
135
|
+
if (typeof window === "undefined" || typeof window.matchMedia === "undefined") return () => {};
|
|
136
|
+
const mq = window.matchMedia("(hover: hover)");
|
|
137
|
+
mq.addEventListener("change", onStoreChange);
|
|
138
|
+
return () => mq.removeEventListener("change", onStoreChange);
|
|
139
|
+
}, []), () => typeof window !== "undefined" && typeof window.matchMedia !== "undefined" ? window.matchMedia("(hover: hover)").matches : true, () => true);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
117
142
|
* Displays a blockchain address with optional truncation, copy button,
|
|
118
|
-
* explorer link, and human-readable label.
|
|
143
|
+
* explorer link, tooltip, and human-readable label.
|
|
119
144
|
*
|
|
120
145
|
* Labels are resolved in priority order:
|
|
121
146
|
* 1. Explicit `label` prop
|
|
@@ -140,11 +165,22 @@ const AddressLabelContext = createContext(null);
|
|
|
140
165
|
*
|
|
141
166
|
* // Suppress label resolution for a specific instance
|
|
142
167
|
* <AddressDisplay address="0x742d35Cc..." disableLabel />
|
|
168
|
+
*
|
|
169
|
+
* // Reveal full address on hover (still truncated when idle)
|
|
170
|
+
* <AddressDisplay address="0x742d35Cc..." untruncateOnHover />
|
|
171
|
+
*
|
|
172
|
+
* // Tooltip with full address on hover + copy icon on hover
|
|
173
|
+
* <AddressDisplay address="0x742d35Cc..." showTooltip showCopyButton showCopyButtonOnHover />
|
|
174
|
+
*
|
|
175
|
+
* // Inline variant (no chip background) — useful inside wallet bars
|
|
176
|
+
* <AddressDisplay address="0x742d35Cc..." variant="inline" showTooltip showCopyButton />
|
|
143
177
|
* ```
|
|
144
178
|
*/
|
|
145
|
-
function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4, showCopyButton = false, showCopyButtonOnHover = false, explorerUrl, label: labelProp, onLabelEdit: onLabelEditProp, networkId, disableLabel = false, className, ...props }) {
|
|
179
|
+
function AddressDisplay({ address, truncate = true, untruncateOnHover = false, startChars = 6, endChars = 4, showCopyButton = false, showCopyButtonOnHover = false, explorerUrl, label: labelProp, onLabelEdit: onLabelEditProp, networkId, disableLabel = false, showTooltip = false, variant = "chip", className, onPointerEnter, onPointerLeave, onMouseEnter, onMouseLeave, onClick, ...props }) {
|
|
146
180
|
const [copied, setCopied] = React$1.useState(false);
|
|
181
|
+
const [isHovered, setIsHovered] = React$1.useState(false);
|
|
147
182
|
const copyTimeoutRef = React$1.useRef(null);
|
|
183
|
+
const prefersHover = usePrefersHover();
|
|
148
184
|
const resolver = React$1.useContext(AddressLabelContext);
|
|
149
185
|
const resolvedLabel = disableLabel ? void 0 : labelProp ?? resolver?.resolveLabel(address, networkId);
|
|
150
186
|
const contextEditHandler = React$1.useCallback(() => {
|
|
@@ -155,7 +191,25 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
|
|
|
155
191
|
networkId
|
|
156
192
|
]);
|
|
157
193
|
const editHandler = disableLabel ? void 0 : onLabelEditProp ?? (resolver?.onEditLabel ? contextEditHandler : void 0);
|
|
158
|
-
const
|
|
194
|
+
const canUntruncate = untruncateOnHover && truncate && !showTooltip;
|
|
195
|
+
const showFullAddress = !truncate || canUntruncate && isHovered;
|
|
196
|
+
const displayAddress = showFullAddress ? address : truncateMiddle(address, startChars, endChars);
|
|
197
|
+
const addressTextClassName = cn(!showFullAddress && "truncate", (showFullAddress || !truncate) && "break-all");
|
|
198
|
+
const expandInteractionClassName = canUntruncate && !prefersHover ? "cursor-pointer" : void 0;
|
|
199
|
+
const handlePointerEnter = (e) => {
|
|
200
|
+
if (canUntruncate && prefersHover) setIsHovered(true);
|
|
201
|
+
onPointerEnter?.(e);
|
|
202
|
+
onMouseEnter?.(e);
|
|
203
|
+
};
|
|
204
|
+
const handlePointerLeave = (e) => {
|
|
205
|
+
if (canUntruncate && prefersHover) setIsHovered(false);
|
|
206
|
+
onPointerLeave?.(e);
|
|
207
|
+
onMouseLeave?.(e);
|
|
208
|
+
};
|
|
209
|
+
const handleUntruncateClick = (e) => {
|
|
210
|
+
if (canUntruncate && !prefersHover) setIsHovered((open) => !open);
|
|
211
|
+
onClick?.(e);
|
|
212
|
+
};
|
|
159
213
|
const handleCopy = (e) => {
|
|
160
214
|
e.stopPropagation();
|
|
161
215
|
navigator.clipboard.writeText(address);
|
|
@@ -171,6 +225,7 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
|
|
|
171
225
|
if (copyTimeoutRef.current) window.clearTimeout(copyTimeoutRef.current);
|
|
172
226
|
};
|
|
173
227
|
}, []);
|
|
228
|
+
const isChip = variant === "chip";
|
|
174
229
|
const actionButtons = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
175
230
|
showCopyButton && /* @__PURE__ */ jsx("button", {
|
|
176
231
|
type: "button",
|
|
@@ -183,6 +238,9 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
|
|
|
183
238
|
href: explorerUrl,
|
|
184
239
|
target: "_blank",
|
|
185
240
|
rel: "noopener noreferrer",
|
|
241
|
+
onClick: (e) => {
|
|
242
|
+
e.stopPropagation();
|
|
243
|
+
},
|
|
186
244
|
className: "ml-1.5 shrink-0 text-slate-500 transition-colors hover:text-slate-700",
|
|
187
245
|
"aria-label": "View in explorer",
|
|
188
246
|
children: /* @__PURE__ */ jsx(ExternalLink$1, { className: "h-3.5 w-3.5" })
|
|
@@ -198,28 +256,48 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
|
|
|
198
256
|
children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" })
|
|
199
257
|
})
|
|
200
258
|
] });
|
|
201
|
-
|
|
202
|
-
|
|
259
|
+
const shouldShowTooltip = showTooltip && truncate;
|
|
260
|
+
const wrapWithTooltip = (content) => {
|
|
261
|
+
if (!shouldShowTooltip) return content;
|
|
262
|
+
return /* @__PURE__ */ jsx(TooltipProvider, {
|
|
263
|
+
delayDuration: 300,
|
|
264
|
+
children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
265
|
+
asChild: true,
|
|
266
|
+
children: content
|
|
267
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
268
|
+
className: "font-mono text-xs",
|
|
269
|
+
children: address
|
|
270
|
+
})] })
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
if (resolvedLabel) return wrapWithTooltip(/* @__PURE__ */ jsxs("div", {
|
|
274
|
+
className: cn("group inline-flex max-w-full min-w-0 flex-col", isChip && "rounded-md bg-slate-100 px-2 py-1", "text-xs text-slate-700", expandInteractionClassName, className),
|
|
275
|
+
onPointerEnter: handlePointerEnter,
|
|
276
|
+
onPointerLeave: handlePointerLeave,
|
|
277
|
+
onClick: handleUntruncateClick,
|
|
203
278
|
...props,
|
|
204
279
|
children: [/* @__PURE__ */ jsx("span", {
|
|
205
280
|
className: "truncate font-sans font-medium text-slate-900 leading-snug",
|
|
206
281
|
children: resolvedLabel
|
|
207
282
|
}), /* @__PURE__ */ jsxs("div", {
|
|
208
|
-
className: "flex items-center font-mono text-[10px] text-slate-400 leading-snug",
|
|
283
|
+
className: "flex min-w-0 items-center font-mono text-[10px] text-slate-400 leading-snug",
|
|
209
284
|
children: [/* @__PURE__ */ jsx("span", {
|
|
210
|
-
className:
|
|
285
|
+
className: addressTextClassName,
|
|
211
286
|
children: displayAddress
|
|
212
287
|
}), actionButtons]
|
|
213
288
|
})]
|
|
214
|
-
});
|
|
215
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
216
|
-
className: cn("group inline-flex max-w-full items-center rounded-md bg-slate-100 px-2 py-1", "text-xs font-mono text-slate-700", className),
|
|
289
|
+
}));
|
|
290
|
+
return wrapWithTooltip(/* @__PURE__ */ jsxs("div", {
|
|
291
|
+
className: cn("group inline-flex max-w-full min-w-0 items-center", isChip && "rounded-md bg-slate-100 px-2 py-1", "text-xs font-mono text-slate-700", expandInteractionClassName, className),
|
|
292
|
+
onPointerEnter: handlePointerEnter,
|
|
293
|
+
onPointerLeave: handlePointerLeave,
|
|
294
|
+
onClick: handleUntruncateClick,
|
|
217
295
|
...props,
|
|
218
296
|
children: [/* @__PURE__ */ jsx("span", {
|
|
219
|
-
className:
|
|
297
|
+
className: addressTextClassName,
|
|
220
298
|
children: displayAddress
|
|
221
299
|
}), actionButtons]
|
|
222
|
-
});
|
|
300
|
+
}));
|
|
223
301
|
}
|
|
224
302
|
|
|
225
303
|
//#endregion
|
|
@@ -1796,19 +1874,6 @@ const Textarea = React$1.forwardRef(({ className, ...props }, ref) => {
|
|
|
1796
1874
|
});
|
|
1797
1875
|
Textarea.displayName = "Textarea";
|
|
1798
1876
|
|
|
1799
|
-
//#endregion
|
|
1800
|
-
//#region src/components/ui/tooltip.tsx
|
|
1801
|
-
const TooltipProvider = TooltipPrimitive.Provider;
|
|
1802
|
-
const Tooltip = TooltipPrimitive.Root;
|
|
1803
|
-
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
1804
|
-
const TooltipContent = React$1.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Content, {
|
|
1805
|
-
ref,
|
|
1806
|
-
sideOffset,
|
|
1807
|
-
className: cn("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 overflow-hidden rounded-md px-3 py-1.5 text-xs", className),
|
|
1808
|
-
...props
|
|
1809
|
-
}));
|
|
1810
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
1811
|
-
|
|
1812
1877
|
//#endregion
|
|
1813
1878
|
//#region src/components/ui/view-contract-state-button.tsx
|
|
1814
1879
|
/**
|