@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 CHANGED
@@ -7,6 +7,8 @@ let react = require("react");
7
7
  react = require_ErrorMessage.__toESM(react);
8
8
  let _openzeppelin_ui_utils = require("@openzeppelin/ui-utils");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
+ let _radix_ui_react_tooltip = require("@radix-ui/react-tooltip");
11
+ _radix_ui_react_tooltip = require_ErrorMessage.__toESM(_radix_ui_react_tooltip);
10
12
  let _radix_ui_react_slot = require("@radix-ui/react-slot");
11
13
  let react_day_picker = require("react-day-picker");
12
14
  let _radix_ui_react_checkbox = require("@radix-ui/react-checkbox");
@@ -31,8 +33,6 @@ let _radix_ui_react_collapsible = require("@radix-ui/react-collapsible");
31
33
  _radix_ui_react_collapsible = require_ErrorMessage.__toESM(_radix_ui_react_collapsible);
32
34
  let _radix_ui_react_tabs = require("@radix-ui/react-tabs");
33
35
  _radix_ui_react_tabs = require_ErrorMessage.__toESM(_radix_ui_react_tabs);
34
- let _radix_ui_react_tooltip = require("@radix-ui/react-tooltip");
35
- _radix_ui_react_tooltip = require_ErrorMessage.__toESM(_radix_ui_react_tooltip);
36
36
  let _openzeppelin_ui_types = require("@openzeppelin/ui-types");
37
37
  let sonner = require("sonner");
38
38
  let next_themes = require("next-themes");
@@ -114,6 +114,19 @@ const AccordionContent = react.forwardRef(({ className, children, variant: varia
114
114
  });
115
115
  AccordionContent.displayName = "AccordionContent";
116
116
 
117
+ //#endregion
118
+ //#region src/components/ui/tooltip.tsx
119
+ const TooltipProvider = _radix_ui_react_tooltip.Provider;
120
+ const Tooltip = _radix_ui_react_tooltip.Root;
121
+ const TooltipTrigger = _radix_ui_react_tooltip.Trigger;
122
+ const TooltipContent = react.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Portal, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Content, {
123
+ ref,
124
+ sideOffset,
125
+ className: (0, _openzeppelin_ui_utils.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),
126
+ ...props
127
+ }) }));
128
+ TooltipContent.displayName = _radix_ui_react_tooltip.Content.displayName;
129
+
117
130
  //#endregion
118
131
  //#region src/components/ui/address-display/context.ts
119
132
  /**
@@ -126,8 +139,20 @@ const AddressLabelContext = (0, react.createContext)(null);
126
139
  //#endregion
127
140
  //#region src/components/ui/address-display/address-display.tsx
128
141
  /**
142
+ * True when the primary input can hover (e.g. desktop with mouse).
143
+ * Touch-first phones typically report false. SSR assumes hover-capable.
144
+ */
145
+ function usePrefersHover() {
146
+ return react.useSyncExternalStore(react.useCallback((onStoreChange) => {
147
+ if (typeof window === "undefined" || typeof window.matchMedia === "undefined") return () => {};
148
+ const mq = window.matchMedia("(hover: hover)");
149
+ mq.addEventListener("change", onStoreChange);
150
+ return () => mq.removeEventListener("change", onStoreChange);
151
+ }, []), () => typeof window !== "undefined" && typeof window.matchMedia !== "undefined" ? window.matchMedia("(hover: hover)").matches : true, () => true);
152
+ }
153
+ /**
129
154
  * Displays a blockchain address with optional truncation, copy button,
130
- * explorer link, and human-readable label.
155
+ * explorer link, tooltip, and human-readable label.
131
156
  *
132
157
  * Labels are resolved in priority order:
133
158
  * 1. Explicit `label` prop
@@ -152,11 +177,22 @@ const AddressLabelContext = (0, react.createContext)(null);
152
177
  *
153
178
  * // Suppress label resolution for a specific instance
154
179
  * <AddressDisplay address="0x742d35Cc..." disableLabel />
180
+ *
181
+ * // Reveal full address on hover (still truncated when idle)
182
+ * <AddressDisplay address="0x742d35Cc..." untruncateOnHover />
183
+ *
184
+ * // Tooltip with full address on hover + copy icon on hover
185
+ * <AddressDisplay address="0x742d35Cc..." showTooltip showCopyButton showCopyButtonOnHover />
186
+ *
187
+ * // Inline variant (no chip background) — useful inside wallet bars
188
+ * <AddressDisplay address="0x742d35Cc..." variant="inline" showTooltip showCopyButton />
155
189
  * ```
156
190
  */
157
- function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4, showCopyButton = false, showCopyButtonOnHover = false, explorerUrl, label: labelProp, onLabelEdit: onLabelEditProp, networkId, disableLabel = false, className, ...props }) {
191
+ 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 }) {
158
192
  const [copied, setCopied] = react.useState(false);
193
+ const [isHovered, setIsHovered] = react.useState(false);
159
194
  const copyTimeoutRef = react.useRef(null);
195
+ const prefersHover = usePrefersHover();
160
196
  const resolver = react.useContext(AddressLabelContext);
161
197
  const resolvedLabel = disableLabel ? void 0 : labelProp ?? resolver?.resolveLabel(address, networkId);
162
198
  const contextEditHandler = react.useCallback(() => {
@@ -167,7 +203,25 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
167
203
  networkId
168
204
  ]);
169
205
  const editHandler = disableLabel ? void 0 : onLabelEditProp ?? (resolver?.onEditLabel ? contextEditHandler : void 0);
170
- const displayAddress = truncate ? (0, _openzeppelin_ui_utils.truncateMiddle)(address, startChars, endChars) : address;
206
+ const canUntruncate = untruncateOnHover && truncate && !showTooltip;
207
+ const showFullAddress = !truncate || canUntruncate && isHovered;
208
+ const displayAddress = showFullAddress ? address : (0, _openzeppelin_ui_utils.truncateMiddle)(address, startChars, endChars);
209
+ const addressTextClassName = (0, _openzeppelin_ui_utils.cn)(!showFullAddress && "truncate", (showFullAddress || !truncate) && "break-all");
210
+ const expandInteractionClassName = canUntruncate && !prefersHover ? "cursor-pointer" : void 0;
211
+ const handlePointerEnter = (e) => {
212
+ if (canUntruncate && prefersHover) setIsHovered(true);
213
+ onPointerEnter?.(e);
214
+ onMouseEnter?.(e);
215
+ };
216
+ const handlePointerLeave = (e) => {
217
+ if (canUntruncate && prefersHover) setIsHovered(false);
218
+ onPointerLeave?.(e);
219
+ onMouseLeave?.(e);
220
+ };
221
+ const handleUntruncateClick = (e) => {
222
+ if (canUntruncate && !prefersHover) setIsHovered((open) => !open);
223
+ onClick?.(e);
224
+ };
171
225
  const handleCopy = (e) => {
172
226
  e.stopPropagation();
173
227
  navigator.clipboard.writeText(address);
@@ -183,6 +237,7 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
183
237
  if (copyTimeoutRef.current) window.clearTimeout(copyTimeoutRef.current);
184
238
  };
185
239
  }, []);
240
+ const isChip = variant === "chip";
186
241
  const actionButtons = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
187
242
  showCopyButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
188
243
  type: "button",
@@ -195,6 +250,9 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
195
250
  href: explorerUrl,
196
251
  target: "_blank",
197
252
  rel: "noopener noreferrer",
253
+ onClick: (e) => {
254
+ e.stopPropagation();
255
+ },
198
256
  className: "ml-1.5 shrink-0 text-slate-500 transition-colors hover:text-slate-700",
199
257
  "aria-label": "View in explorer",
200
258
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ExternalLink, { className: "h-3.5 w-3.5" })
@@ -210,28 +268,48 @@ function AddressDisplay({ address, truncate = true, startChars = 6, endChars = 4
210
268
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Pencil, { className: "h-3.5 w-3.5" })
211
269
  })
212
270
  ] });
213
- if (resolvedLabel) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
214
- className: (0, _openzeppelin_ui_utils.cn)("group inline-flex max-w-full flex-col rounded-md bg-slate-100 px-2 py-1", "text-xs text-slate-700", className),
271
+ const shouldShowTooltip = showTooltip && truncate;
272
+ const wrapWithTooltip = (content) => {
273
+ if (!shouldShowTooltip) return content;
274
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipProvider, {
275
+ delayDuration: 300,
276
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipTrigger, {
277
+ asChild: true,
278
+ children: content
279
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TooltipContent, {
280
+ className: "font-mono text-xs",
281
+ children: address
282
+ })] })
283
+ });
284
+ };
285
+ if (resolvedLabel) return wrapWithTooltip(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
286
+ className: (0, _openzeppelin_ui_utils.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),
287
+ onPointerEnter: handlePointerEnter,
288
+ onPointerLeave: handlePointerLeave,
289
+ onClick: handleUntruncateClick,
215
290
  ...props,
216
291
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
217
292
  className: "truncate font-sans font-medium text-slate-900 leading-snug",
218
293
  children: resolvedLabel
219
294
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
220
- className: "flex items-center font-mono text-[10px] text-slate-400 leading-snug",
295
+ className: "flex min-w-0 items-center font-mono text-[10px] text-slate-400 leading-snug",
221
296
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
222
- className: (0, _openzeppelin_ui_utils.cn)("truncate", truncate ? "" : "break-all"),
297
+ className: addressTextClassName,
223
298
  children: displayAddress
224
299
  }), actionButtons]
225
300
  })]
226
- });
227
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
228
- className: (0, _openzeppelin_ui_utils.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),
301
+ }));
302
+ return wrapWithTooltip(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
303
+ className: (0, _openzeppelin_ui_utils.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),
304
+ onPointerEnter: handlePointerEnter,
305
+ onPointerLeave: handlePointerLeave,
306
+ onClick: handleUntruncateClick,
229
307
  ...props,
230
308
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
231
- className: (0, _openzeppelin_ui_utils.cn)("truncate", truncate ? "" : "break-all"),
309
+ className: addressTextClassName,
232
310
  children: displayAddress
233
311
  }), actionButtons]
234
- });
312
+ }));
235
313
  }
236
314
 
237
315
  //#endregion
@@ -1808,19 +1886,6 @@ const Textarea = react.forwardRef(({ className, ...props }, ref) => {
1808
1886
  });
1809
1887
  Textarea.displayName = "Textarea";
1810
1888
 
1811
- //#endregion
1812
- //#region src/components/ui/tooltip.tsx
1813
- const TooltipProvider = _radix_ui_react_tooltip.Provider;
1814
- const Tooltip = _radix_ui_react_tooltip.Root;
1815
- const TooltipTrigger = _radix_ui_react_tooltip.Trigger;
1816
- const TooltipContent = react.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_radix_ui_react_tooltip.Content, {
1817
- ref,
1818
- sideOffset,
1819
- className: (0, _openzeppelin_ui_utils.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),
1820
- ...props
1821
- }));
1822
- TooltipContent.displayName = _radix_ui_react_tooltip.Content.displayName;
1823
-
1824
1889
  //#endregion
1825
1890
  //#region src/components/ui/view-contract-state-button.tsx
1826
1891
  /**