@openzeppelin/ui-components 1.4.0 → 1.5.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
@@ -3,7 +3,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion";
3
3
  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
- import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
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
9
  import { Slot, Slottable } from "@radix-ui/react-slot";
@@ -1593,12 +1593,12 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
1593
1593
  * Can render as a button or anchor element depending on whether href is provided.
1594
1594
  */
1595
1595
  function SidebarButton({ icon, children, onClick, size = "default", badge, disabled = false, isSelected = false, href, target, rel, className }) {
1596
- const commonClass = cn("group relative flex items-center gap-2 px-3 py-2.5 rounded-lg font-semibold text-sm transition-colors", badge ? "justify-between" : "justify-start", disabled ? "text-gray-400 cursor-not-allowed" : isSelected ? "text-[#111928] bg-neutral-100" : "text-gray-600 hover:text-gray-700 cursor-pointer hover:before:content-[\"\"] hover:before:absolute hover:before:inset-x-0 hover:before:top-1 hover:before:bottom-1 hover:before:bg-muted/80 hover:before:rounded-lg hover:before:-z-10", size === "small" ? "h-10" : "h-11", className);
1596
+ const commonClass = cn("group relative flex flex-wrap items-center gap-x-2 gap-y-0.5 px-3 py-2 rounded-lg font-semibold text-sm transition-colors", badge ? "justify-between" : "justify-start", disabled ? "text-gray-400 cursor-not-allowed" : isSelected ? "text-[#111928] bg-neutral-100" : "text-gray-600 hover:text-gray-700 cursor-pointer hover:before:content-[\"\"] hover:before:absolute hover:before:inset-x-0 hover:before:top-1 hover:before:bottom-1 hover:before:bg-muted/80 hover:before:rounded-lg hover:before:-z-10", size === "small" ? "min-h-10" : "min-h-11", className);
1597
1597
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
1598
1598
  className: "flex items-center gap-2",
1599
1599
  children: [icon, children]
1600
1600
  }), badge && /* @__PURE__ */ jsx("span", {
1601
- className: "text-xs px-2 py-1 bg-muted text-muted-foreground rounded-full font-medium",
1601
+ className: "text-xs px-2 py-0.5 bg-muted text-muted-foreground rounded-full font-medium",
1602
1602
  children: badge
1603
1603
  })] });
1604
1604
  if (href) return /* @__PURE__ */ jsx("a", {
@@ -1843,6 +1843,546 @@ function ViewContractStateButton({ contractAddress, onToggle }) {
1843
1843
  });
1844
1844
  }
1845
1845
 
1846
+ //#endregion
1847
+ //#region src/components/ui/wizard/WizardStepper.tsx
1848
+ function resolveState(step, index, currentStepIndex, furthestStepIndex) {
1849
+ if (step.status === "completed" || step.status === "skipped") return "completed";
1850
+ if (index === currentStepIndex) return "current";
1851
+ if (step.isInvalid && (index < currentStepIndex || index <= furthestStepIndex)) return "invalid";
1852
+ if (index < currentStepIndex) return "completed";
1853
+ if (index <= furthestStepIndex) return "visited";
1854
+ return "upcoming";
1855
+ }
1856
+ function canClick(state, freeNavigation = false) {
1857
+ if (freeNavigation) return true;
1858
+ return state !== "upcoming";
1859
+ }
1860
+ function StepCircle({ state, index }) {
1861
+ return /* @__PURE__ */ jsx("span", {
1862
+ className: cn("flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-semibold transition-all", state === "completed" && "bg-blue-600 text-white", state === "current" && "bg-blue-600 text-white ring-2 ring-blue-200", state === "visited" && "bg-blue-100 text-blue-600 ring-1 ring-blue-300", state === "invalid" && "bg-red-100 text-red-600 ring-1 ring-red-300", state === "upcoming" && "bg-zinc-100 text-zinc-400"),
1863
+ children: state === "completed" ? /* @__PURE__ */ jsx(Check, { className: "size-3.5" }) : state === "visited" ? /* @__PURE__ */ jsx(Pencil, { className: "size-3" }) : state === "invalid" ? /* @__PURE__ */ jsx(AlertCircle, { className: "size-3.5" }) : index + 1
1864
+ });
1865
+ }
1866
+ function StepLabel({ title, state, isSkipped }) {
1867
+ return /* @__PURE__ */ jsxs("div", {
1868
+ className: "min-w-0 flex-1",
1869
+ children: [/* @__PURE__ */ jsx("span", {
1870
+ className: cn("text-sm font-medium transition-colors", state === "current" && "text-blue-700", state === "completed" && "text-zinc-700", state === "visited" && "text-blue-600", state === "invalid" && "text-red-600", state === "upcoming" && "text-zinc-400"),
1871
+ children: title
1872
+ }), isSkipped && /* @__PURE__ */ jsx("span", {
1873
+ className: "mt-0.5 block text-[11px] text-zinc-400",
1874
+ children: "Skipped"
1875
+ })]
1876
+ });
1877
+ }
1878
+ function VerticalStepper({ steps, currentStepIndex, furthestStepIndex = currentStepIndex, onStepClick, freeNavigation, className }) {
1879
+ return /* @__PURE__ */ jsx("nav", {
1880
+ "aria-label": "Wizard steps",
1881
+ className: cn("rounded-2xl border border-zinc-200 bg-white p-6", className),
1882
+ children: /* @__PURE__ */ jsx("div", {
1883
+ className: "flex flex-col gap-1",
1884
+ children: steps.map((step, index) => {
1885
+ const state = resolveState(step, index, currentStepIndex, furthestStepIndex);
1886
+ const clickable = canClick(state, freeNavigation) && !!onStepClick;
1887
+ return /* @__PURE__ */ jsxs("button", {
1888
+ type: "button",
1889
+ onClick: () => clickable && onStepClick?.(index),
1890
+ disabled: !clickable,
1891
+ className: cn("flex items-center gap-3 rounded-xl border border-transparent px-3 py-3 text-left transition-all duration-150", clickable ? "cursor-pointer" : "cursor-not-allowed opacity-50", state === "current" && "border-blue-200 bg-blue-50", state === "completed" && "bg-white hover:bg-gray-50", state === "visited" && "bg-white hover:bg-blue-50/50", state === "invalid" && "border-red-200 bg-red-50 hover:bg-red-100/60", state === "upcoming" && "bg-white"),
1892
+ "aria-current": state === "current" ? "step" : void 0,
1893
+ children: [/* @__PURE__ */ jsx(StepCircle, {
1894
+ state,
1895
+ index
1896
+ }), /* @__PURE__ */ jsx(StepLabel, {
1897
+ title: step.title,
1898
+ state,
1899
+ isSkipped: step.status === "skipped"
1900
+ })]
1901
+ }, step.id);
1902
+ })
1903
+ })
1904
+ });
1905
+ }
1906
+ function HorizontalStepper({ steps, currentStepIndex, furthestStepIndex = currentStepIndex, onStepClick, freeNavigation, className }) {
1907
+ return /* @__PURE__ */ jsx("nav", {
1908
+ "aria-label": "Wizard steps",
1909
+ className: cn("rounded-2xl border border-zinc-200 bg-white p-6", className),
1910
+ children: /* @__PURE__ */ jsx("div", {
1911
+ className: "flex w-full items-center",
1912
+ children: steps.map((step, index) => {
1913
+ const state = resolveState(step, index, currentStepIndex, furthestStepIndex);
1914
+ const clickable = canClick(state, freeNavigation) && !!onStepClick;
1915
+ const isLast = index === steps.length - 1;
1916
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsxs("button", {
1917
+ type: "button",
1918
+ onClick: () => clickable && onStepClick?.(index),
1919
+ disabled: !clickable,
1920
+ className: cn("flex items-center gap-2 rounded-xl border border-transparent px-3 py-2 text-left transition-all duration-150", clickable ? "cursor-pointer" : "cursor-not-allowed opacity-50", state === "current" && "border-blue-200 bg-blue-50", state === "completed" && "bg-white hover:bg-gray-50", state === "visited" && "bg-white hover:bg-blue-50/50", state === "invalid" && "border-red-200 bg-red-50 hover:bg-red-100/60", state === "upcoming" && "bg-white"),
1921
+ "aria-current": state === "current" ? "step" : void 0,
1922
+ "aria-label": `Step ${index + 1}: ${step.title}`,
1923
+ children: [/* @__PURE__ */ jsx(StepCircle, {
1924
+ state,
1925
+ index
1926
+ }), /* @__PURE__ */ jsx("div", {
1927
+ className: "hidden sm:block",
1928
+ children: /* @__PURE__ */ jsx(StepLabel, {
1929
+ title: step.title,
1930
+ state,
1931
+ isSkipped: step.status === "skipped"
1932
+ })
1933
+ })]
1934
+ }), !isLast && /* @__PURE__ */ jsx("div", { className: cn("mx-1 h-px flex-1 transition-colors sm:mx-2", index < currentStepIndex ? "bg-blue-600" : "bg-zinc-200") })] }, step.id);
1935
+ })
1936
+ })
1937
+ });
1938
+ }
1939
+ /**
1940
+ * A stepper component for navigating through a series of steps.
1941
+ *
1942
+ * @param props - The props for the WizardStepper component.
1943
+ * @returns A React node representing the stepper component.
1944
+ */
1945
+ function WizardStepper(props) {
1946
+ const { variant = "horizontal", ...rest } = props;
1947
+ return variant === "vertical" ? /* @__PURE__ */ jsx(VerticalStepper, {
1948
+ ...rest,
1949
+ variant
1950
+ }) : /* @__PURE__ */ jsx(HorizontalStepper, {
1951
+ ...rest,
1952
+ variant
1953
+ });
1954
+ }
1955
+
1956
+ //#endregion
1957
+ //#region src/components/ui/wizard/WizardNavigation.tsx
1958
+ /**
1959
+ * A navigation component for the wizard.
1960
+ *
1961
+ * @param props - The props for the WizardNavigation component.
1962
+ * @returns A React node representing the navigation component.
1963
+ */
1964
+ function WizardNavigation({ isFirstStep, isLastStep, canProceed = true, onPrevious, onNext, onCancel, extraActions, nextLabel = "Next", lastStepLabel = "Finish", className }) {
1965
+ return /* @__PURE__ */ jsxs("div", {
1966
+ className: cn("flex items-center justify-between", className),
1967
+ children: [/* @__PURE__ */ jsxs("div", {
1968
+ className: "flex gap-2",
1969
+ children: [onCancel && /* @__PURE__ */ jsxs(Button, {
1970
+ type: "button",
1971
+ variant: "outline",
1972
+ onClick: onCancel,
1973
+ className: "gap-2",
1974
+ children: [/* @__PURE__ */ jsx(X, { className: "size-4" }), "Cancel"]
1975
+ }), !isFirstStep && /* @__PURE__ */ jsxs(Button, {
1976
+ type: "button",
1977
+ variant: "outline",
1978
+ onClick: onPrevious,
1979
+ className: "gap-2",
1980
+ children: [/* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" }), "Previous"]
1981
+ })]
1982
+ }), /* @__PURE__ */ jsxs("div", {
1983
+ className: "flex gap-2",
1984
+ children: [extraActions, /* @__PURE__ */ jsxs(Button, {
1985
+ type: "button",
1986
+ onClick: onNext,
1987
+ disabled: !canProceed,
1988
+ className: "gap-2",
1989
+ children: [isLastStep ? lastStepLabel : nextLabel, !isLastStep && /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })]
1990
+ })]
1991
+ })]
1992
+ });
1993
+ }
1994
+
1995
+ //#endregion
1996
+ //#region src/components/ui/wizard/hooks.ts
1997
+ /**
1998
+ * Clamp a step index into the valid range for the current wizard.
1999
+ */
2000
+ function getSafeStepIndex(stepCount, currentStepIndex) {
2001
+ if (stepCount === 0) return 0;
2002
+ return Math.max(0, Math.min(currentStepIndex, stepCount - 1));
2003
+ }
2004
+ /**
2005
+ * Track the highest step reached unless a controlled value is provided.
2006
+ */
2007
+ function useFurthestStepIndex(currentStepIndex, controlledFurthestStepIndex) {
2008
+ const [internalFurthestStepIndex, setInternalFurthestStepIndex] = useState(currentStepIndex);
2009
+ useEffect(() => {
2010
+ setInternalFurthestStepIndex((prev) => Math.max(prev, currentStepIndex));
2011
+ }, [currentStepIndex]);
2012
+ return controlledFurthestStepIndex ?? internalFurthestStepIndex;
2013
+ }
2014
+ /**
2015
+ * Keep the scrollable wizard's active and visited step state in sync with scrolling and clicks.
2016
+ */
2017
+ function useScrollableWizardStepTracking({ steps, currentStepIndex, onStepChange, scrollRef, sectionId, scrollPadding = SCROLL_PADDING_PX }) {
2018
+ const safeIndex = getSafeStepIndex(steps.length, currentStepIndex);
2019
+ const initialIndexRef = useRef(safeIndex);
2020
+ const rafRef = useRef(null);
2021
+ const manualSelectionIndexRef = useRef(null);
2022
+ const stepsRef = useRef(steps);
2023
+ const sectionIdRef = useRef(sectionId);
2024
+ const onStepChangeRef = useRef(onStepChange);
2025
+ const scrollPaddingRef = useRef(scrollPadding);
2026
+ useEffect(() => {
2027
+ stepsRef.current = steps;
2028
+ sectionIdRef.current = sectionId;
2029
+ onStepChangeRef.current = onStepChange;
2030
+ scrollPaddingRef.current = scrollPadding;
2031
+ });
2032
+ const [activeIndex, setActiveIndex] = useState(initialIndexRef.current);
2033
+ const activeIndexRef = useRef(initialIndexRef.current);
2034
+ const [furthestStepIndex, setFurthestStepIndex] = useState(initialIndexRef.current);
2035
+ const isMountedRef = useRef(false);
2036
+ const clearManualSelection = useCallback(() => {
2037
+ manualSelectionIndexRef.current = null;
2038
+ }, []);
2039
+ useEffect(() => {
2040
+ const container = scrollRef.current;
2041
+ if (!container) return;
2042
+ const ownerDocument = container.ownerDocument;
2043
+ isMountedRef.current = false;
2044
+ let didCompleteInitialRaf = false;
2045
+ const releaseManualSelectionOnUserScroll = () => {
2046
+ clearManualSelection();
2047
+ };
2048
+ const handleKeyDown = (event) => {
2049
+ if (isScrollableNavigationKey(event)) clearManualSelection();
2050
+ };
2051
+ const handleScroll = () => {
2052
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
2053
+ rafRef.current = requestAnimationFrame(() => {
2054
+ const currentSteps = stepsRef.current;
2055
+ const currentSectionId = sectionIdRef.current;
2056
+ const currentOnStepChange = onStepChangeRef.current;
2057
+ if (currentSteps.length === 0) return;
2058
+ const manualSelectionIndex = manualSelectionIndexRef.current;
2059
+ const naturalState = resolveScrollableActiveIndex(container, currentSteps, currentSectionId);
2060
+ const naturalActiveIndex = naturalState.activeIndex;
2061
+ const newActiveIndex = manualSelectionIndex ?? naturalActiveIndex;
2062
+ const shouldCommitFurthestStepIndex = manualSelectionIndex !== null ? true : naturalState.commitFurthestStepIndex;
2063
+ if (activeIndexRef.current !== newActiveIndex) {
2064
+ activeIndexRef.current = newActiveIndex;
2065
+ setActiveIndex(newActiveIndex);
2066
+ if (isMountedRef.current) {
2067
+ lastEmittedIndexRef.current = newActiveIndex;
2068
+ currentOnStepChange(newActiveIndex);
2069
+ }
2070
+ } else setActiveIndex(newActiveIndex);
2071
+ if (shouldCommitFurthestStepIndex) setFurthestStepIndex((prev) => Math.max(prev, newActiveIndex));
2072
+ rafRef.current = null;
2073
+ if (!didCompleteInitialRaf) {
2074
+ didCompleteInitialRaf = true;
2075
+ isMountedRef.current = true;
2076
+ }
2077
+ });
2078
+ };
2079
+ container.addEventListener("wheel", releaseManualSelectionOnUserScroll, { passive: true });
2080
+ container.addEventListener("touchmove", releaseManualSelectionOnUserScroll, { passive: true });
2081
+ container.addEventListener("pointerdown", releaseManualSelectionOnUserScroll);
2082
+ ownerDocument.addEventListener("keydown", handleKeyDown);
2083
+ container.addEventListener("scroll", handleScroll, { passive: true });
2084
+ handleScroll();
2085
+ return () => {
2086
+ isMountedRef.current = false;
2087
+ container.removeEventListener("wheel", releaseManualSelectionOnUserScroll);
2088
+ container.removeEventListener("touchmove", releaseManualSelectionOnUserScroll);
2089
+ container.removeEventListener("pointerdown", releaseManualSelectionOnUserScroll);
2090
+ ownerDocument.removeEventListener("keydown", handleKeyDown);
2091
+ container.removeEventListener("scroll", handleScroll);
2092
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
2093
+ };
2094
+ }, [clearManualSelection, scrollRef]);
2095
+ const lastEmittedIndexRef = useRef(safeIndex);
2096
+ useEffect(() => {
2097
+ const newSafeIndex = getSafeStepIndex(stepsRef.current.length, currentStepIndex);
2098
+ if (newSafeIndex === lastEmittedIndexRef.current) return;
2099
+ lastEmittedIndexRef.current = newSafeIndex;
2100
+ activeIndexRef.current = newSafeIndex;
2101
+ setActiveIndex(newSafeIndex);
2102
+ setFurthestStepIndex((prev) => Math.max(prev, newSafeIndex));
2103
+ const step = stepsRef.current[newSafeIndex];
2104
+ if (!step) return;
2105
+ const sectionElement = scrollRef.current?.querySelector(`#${CSS.escape(sectionIdRef.current(step.id))}`);
2106
+ if (scrollRef.current && sectionElement) scrollSectionIntoView(scrollRef.current, sectionElement, scrollPaddingRef.current);
2107
+ }, [currentStepIndex, scrollRef]);
2108
+ return {
2109
+ activeIndex,
2110
+ furthestStepIndex,
2111
+ scrollToSection: useCallback((index) => {
2112
+ const step = stepsRef.current[index];
2113
+ if (!step) return;
2114
+ manualSelectionIndexRef.current = index;
2115
+ activeIndexRef.current = index;
2116
+ lastEmittedIndexRef.current = index;
2117
+ setActiveIndex(index);
2118
+ setFurthestStepIndex((prev) => Math.max(prev, index));
2119
+ onStepChangeRef.current(index);
2120
+ const container = scrollRef.current;
2121
+ const sectionElement = container?.querySelector(`#${CSS.escape(sectionIdRef.current(step.id))}`);
2122
+ if (container && sectionElement) scrollSectionIntoView(container, sectionElement, scrollPaddingRef.current);
2123
+ }, [scrollRef])
2124
+ };
2125
+ }
2126
+ function resolveScrollableActiveIndex(container, steps, sectionId) {
2127
+ if (steps.length === 0) return {
2128
+ activeIndex: 0,
2129
+ commitFurthestStepIndex: false
2130
+ };
2131
+ const containerRect = container.getBoundingClientRect();
2132
+ const anchorY = containerRect.top + Math.min(containerRect.height * .35, 220);
2133
+ const isScrollable = container.scrollHeight > container.clientHeight + 1;
2134
+ const isAtBottom = isScrollable && container.scrollTop + container.clientHeight >= container.scrollHeight - 1;
2135
+ const isNearBottom = isScrollable && container.scrollTop + container.clientHeight >= container.scrollHeight - 4;
2136
+ if (isAtBottom) return {
2137
+ activeIndex: steps.length - 1,
2138
+ commitFurthestStepIndex: false
2139
+ };
2140
+ let activeIndex = 0;
2141
+ let highestScore = Number.NEGATIVE_INFINITY;
2142
+ for (let i = 0; i < steps.length; i++) {
2143
+ const sectionMetrics = getSectionMetrics(container, steps[i].id, sectionId, containerRect);
2144
+ if (!sectionMetrics) continue;
2145
+ const score = scoreScrollableStep({
2146
+ stepIndex: i,
2147
+ stepCount: steps.length,
2148
+ containerRect,
2149
+ anchorY,
2150
+ isNearBottom,
2151
+ ...sectionMetrics
2152
+ });
2153
+ if (score >= highestScore) {
2154
+ highestScore = score;
2155
+ activeIndex = i;
2156
+ }
2157
+ }
2158
+ return {
2159
+ activeIndex,
2160
+ commitFurthestStepIndex: true
2161
+ };
2162
+ }
2163
+ const SCROLL_PADDING_PX = 32;
2164
+ function getSectionElement(container, stepId, sectionId) {
2165
+ return container.querySelector(`#${CSS.escape(sectionId(stepId))}`);
2166
+ }
2167
+ function scrollSectionIntoView(container, sectionElement, padding) {
2168
+ const elementTop = sectionElement.getBoundingClientRect().top;
2169
+ const containerTop = container.getBoundingClientRect().top;
2170
+ const targetScrollTop = container.scrollTop + (elementTop - containerTop) - padding;
2171
+ container.scrollTo({
2172
+ top: targetScrollTop,
2173
+ behavior: "smooth"
2174
+ });
2175
+ }
2176
+ function getSectionMetrics(container, stepId, sectionId, containerRect) {
2177
+ const sectionElement = getSectionElement(container, stepId, sectionId);
2178
+ if (!sectionElement) return null;
2179
+ const sectionRect = sectionElement.getBoundingClientRect();
2180
+ return {
2181
+ sectionRect,
2182
+ visibleHeight: getVisibleHeight(containerRect, sectionRect)
2183
+ };
2184
+ }
2185
+ function getVisibleHeight(containerRect, sectionRect) {
2186
+ return Math.max(0, Math.min(sectionRect.bottom, containerRect.bottom) - Math.max(sectionRect.top, containerRect.top));
2187
+ }
2188
+ function scoreScrollableStep({ stepIndex, stepCount, containerRect, sectionRect, visibleHeight, anchorY, isNearBottom }) {
2189
+ const isVisible = visibleHeight > 0;
2190
+ const focusBandTop = containerRect.top + Math.min(containerRect.height * .2, 140);
2191
+ const focusBandBottom = containerRect.top + Math.min(containerRect.height * .55, 360);
2192
+ const focusBandOverlap = getBandOverlapHeight(sectionRect, focusBandTop, focusBandBottom);
2193
+ const distanceToFocusBand = focusBandOverlap > 0 ? 0 : Math.min(Math.abs(sectionRect.top - focusBandBottom), Math.abs(sectionRect.bottom - focusBandTop));
2194
+ const lastStepProminent = stepIndex === stepCount - 1 && visibleHeight >= Math.min(sectionRect.height, containerRect.height) * .25 && sectionRect.top <= containerRect.top + containerRect.height * .65;
2195
+ let score = isVisible ? visibleHeight : Number.NEGATIVE_INFINITY;
2196
+ if (focusBandOverlap > 0) score += 12e3 + focusBandOverlap * 25;
2197
+ if (sectionRect.top <= anchorY) score += 250;
2198
+ score += Math.max(0, 1e3 - distanceToFocusBand);
2199
+ if (isNearBottom && lastStepProminent && isVisible) score += 15e3;
2200
+ return score;
2201
+ }
2202
+ function getBandOverlapHeight(sectionRect, bandTop, bandBottom) {
2203
+ return Math.max(0, Math.min(sectionRect.bottom, bandBottom) - Math.max(sectionRect.top, bandTop));
2204
+ }
2205
+ function isScrollableNavigationKey(event) {
2206
+ if (event.metaKey || event.ctrlKey || event.altKey) return false;
2207
+ return [
2208
+ "ArrowDown",
2209
+ "ArrowUp",
2210
+ "PageDown",
2211
+ "PageUp",
2212
+ "Home",
2213
+ "End",
2214
+ " "
2215
+ ].includes(event.key);
2216
+ }
2217
+
2218
+ //#endregion
2219
+ //#region src/components/ui/wizard/WizardLayout.tsx
2220
+ function PagedLayout({ steps, currentStepIndex, furthestStepIndex: furthestStepIndexProp, onStepChange, onComplete, onCancel, navActions, header, variant, className }) {
2221
+ const safeIndex = getSafeStepIndex(steps.length, currentStepIndex);
2222
+ const resolvedFurthestStepIndex = useFurthestStepIndex(safeIndex, furthestStepIndexProp);
2223
+ if (steps.length === 0) return null;
2224
+ const isFirstStep = safeIndex === 0;
2225
+ const isLastStep = safeIndex === steps.length - 1;
2226
+ const currentStep = steps[safeIndex];
2227
+ const canProceed = currentStep?.isValid !== false;
2228
+ const handleNext = () => {
2229
+ if (isLastStep) {
2230
+ onComplete?.();
2231
+ return;
2232
+ }
2233
+ onStepChange(safeIndex + 1);
2234
+ };
2235
+ const handlePrevious = () => {
2236
+ if (!isFirstStep) onStepChange(safeIndex - 1);
2237
+ };
2238
+ const stepDefs = toStepDefs(steps, safeIndex);
2239
+ const footer = /* @__PURE__ */ jsx("div", {
2240
+ className: "shrink-0 border-t border-border bg-background px-8 py-4",
2241
+ children: /* @__PURE__ */ jsx("div", {
2242
+ className: "mx-auto max-w-5xl",
2243
+ children: /* @__PURE__ */ jsx(WizardNavigation, {
2244
+ isFirstStep,
2245
+ isLastStep,
2246
+ canProceed,
2247
+ onPrevious: handlePrevious,
2248
+ onNext: handleNext,
2249
+ onCancel,
2250
+ extraActions: navActions
2251
+ })
2252
+ })
2253
+ });
2254
+ if (variant === "vertical") return /* @__PURE__ */ jsxs("div", {
2255
+ className: cn("flex h-full gap-6", className),
2256
+ children: [/* @__PURE__ */ jsx("div", {
2257
+ className: "w-[220px] shrink-0 py-6 pl-6",
2258
+ children: /* @__PURE__ */ jsx(WizardStepper, {
2259
+ variant: "vertical",
2260
+ steps: stepDefs,
2261
+ currentStepIndex: safeIndex,
2262
+ furthestStepIndex: resolvedFurthestStepIndex,
2263
+ onStepClick: onStepChange,
2264
+ className: "h-full"
2265
+ })
2266
+ }), /* @__PURE__ */ jsxs("div", {
2267
+ className: "flex min-w-0 flex-1 flex-col overflow-hidden",
2268
+ children: [/* @__PURE__ */ jsx("div", {
2269
+ className: "flex-1 overflow-y-auto p-8",
2270
+ children: /* @__PURE__ */ jsxs("div", {
2271
+ className: "mx-auto max-w-5xl",
2272
+ children: [header, currentStep?.component]
2273
+ })
2274
+ }), footer]
2275
+ })]
2276
+ });
2277
+ return /* @__PURE__ */ jsxs("div", {
2278
+ className: cn("flex h-full flex-col", className),
2279
+ children: [/* @__PURE__ */ jsx("div", {
2280
+ className: "shrink-0 p-6 pb-0",
2281
+ children: /* @__PURE__ */ jsx(WizardStepper, {
2282
+ variant: "horizontal",
2283
+ steps: stepDefs,
2284
+ currentStepIndex: safeIndex,
2285
+ furthestStepIndex: resolvedFurthestStepIndex,
2286
+ onStepClick: onStepChange
2287
+ })
2288
+ }), /* @__PURE__ */ jsxs("div", {
2289
+ className: "flex min-w-0 flex-1 flex-col overflow-hidden",
2290
+ children: [/* @__PURE__ */ jsx("div", {
2291
+ className: "flex-1 overflow-y-auto p-8",
2292
+ children: /* @__PURE__ */ jsxs("div", {
2293
+ className: "mx-auto max-w-5xl",
2294
+ children: [header, currentStep?.component]
2295
+ })
2296
+ }), footer]
2297
+ })]
2298
+ });
2299
+ }
2300
+ function ScrollableLayout({ steps, currentStepIndex, onStepChange, header, onComplete, scrollPadding, className }) {
2301
+ const instanceId = useId();
2302
+ const scrollRef = useRef(null);
2303
+ const sectionId = useCallback((stepId) => `wizard-section-${instanceId}-${stepId}`, [instanceId]);
2304
+ const { activeIndex, furthestStepIndex, scrollToSection } = useScrollableWizardStepTracking({
2305
+ steps,
2306
+ currentStepIndex,
2307
+ onStepChange,
2308
+ scrollRef,
2309
+ sectionId,
2310
+ scrollPadding
2311
+ });
2312
+ if (steps.length === 0) return null;
2313
+ const stepDefs = toStepDefs(steps, activeIndex);
2314
+ return /* @__PURE__ */ jsxs("div", {
2315
+ className: cn("flex h-full gap-6", className),
2316
+ children: [/* @__PURE__ */ jsx("div", {
2317
+ className: "w-[220px] shrink-0 py-6 pl-6",
2318
+ children: /* @__PURE__ */ jsx(WizardStepper, {
2319
+ variant: "vertical",
2320
+ steps: stepDefs,
2321
+ currentStepIndex: activeIndex,
2322
+ furthestStepIndex,
2323
+ onStepClick: scrollToSection,
2324
+ freeNavigation: true,
2325
+ className: "h-full"
2326
+ })
2327
+ }), /* @__PURE__ */ jsxs("div", {
2328
+ ref: scrollRef,
2329
+ className: "flex min-w-0 flex-1 flex-col overflow-y-auto p-8",
2330
+ children: [
2331
+ header,
2332
+ /* @__PURE__ */ jsx("div", {
2333
+ className: "space-y-12",
2334
+ children: steps.map((step) => /* @__PURE__ */ jsx("section", {
2335
+ id: sectionId(step.id),
2336
+ children: step.component
2337
+ }, step.id))
2338
+ }),
2339
+ onComplete && /* @__PURE__ */ jsx("div", {
2340
+ className: "flex justify-end pt-8",
2341
+ children: /* @__PURE__ */ jsx(Button, {
2342
+ type: "button",
2343
+ onClick: onComplete,
2344
+ children: "Finish"
2345
+ })
2346
+ })
2347
+ ]
2348
+ })]
2349
+ });
2350
+ }
2351
+ function toStepDefs(steps, currentStepIndex) {
2352
+ return steps.map((s, i) => {
2353
+ const isInvalid = s.isInvalid ?? s.isValid === false;
2354
+ const status = s.status ?? (i < currentStepIndex && !isInvalid ? "completed" : "pending");
2355
+ return {
2356
+ id: s.id,
2357
+ title: s.title,
2358
+ status,
2359
+ isInvalid
2360
+ };
2361
+ });
2362
+ }
2363
+ /**
2364
+ * A layout component for the wizard.
2365
+ *
2366
+ * @param props - The props for the WizardLayout component.
2367
+ * @returns A React node representing the layout component.
2368
+ */
2369
+ function WizardLayout(props) {
2370
+ const { variant = "horizontal", ...rest } = props;
2371
+ if (variant === "scrollable") return /* @__PURE__ */ jsx(ScrollableLayout, {
2372
+ steps: rest.steps,
2373
+ currentStepIndex: rest.currentStepIndex,
2374
+ onStepChange: rest.onStepChange,
2375
+ header: rest.header,
2376
+ onComplete: rest.onComplete,
2377
+ scrollPadding: rest.scrollPadding,
2378
+ className: rest.className
2379
+ });
2380
+ return /* @__PURE__ */ jsx(PagedLayout, {
2381
+ ...rest,
2382
+ variant
2383
+ });
2384
+ }
2385
+
1846
2386
  //#endregion
1847
2387
  //#region src/components/fields/address-suggestion/context.ts
1848
2388
  /**
@@ -5720,5 +6260,5 @@ const Toaster = ({ ...props }) => {
5720
6260
  };
5721
6261
 
5722
6262
  //#endregion
5723
- 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, ViewContractStateButton, 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 };
6263
+ 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, 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 };
5724
6264
  //# sourceMappingURL=index.mjs.map