@windrun-huaiin/third-ui 14.4.0 → 14.4.1

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.
@@ -1,3 +1,7 @@
1
+ /**
2
+ * References most of its code and SVG animation design from
3
+ * https://github.com/fuma-nama/fumadocs/blob/dev/packages/radix-ui/src/components/toc/clerk.tsx
4
+ */
1
5
  import * as Primitive from 'fumadocs-core/toc';
2
6
  import { type ComponentProps, type ReactNode } from 'react';
3
7
  type TOCItemType = Primitive.TOCItemType;
@@ -37,6 +37,14 @@ const CLERK_TURN_CURVE_HEIGHT = 12;
37
37
  const CLERK_TURN_CONTROL_FACTOR = 0.68;
38
38
  // Safety margin that keeps turns away from the heading rows themselves.
39
39
  const CLERK_TURN_GAP_MARGIN = 7;
40
+ // Shared duration for active rail fade transitions and endpoint dot movement.
41
+ const CLERK_ACTIVE_ANIMATION_DURATION_MS = 300;
42
+ // Easing curve for the active rail and dot; tuned for a slightly delayed, softer motion.
43
+ const CLERK_ACTIVE_ANIMATION_EASING = 'cubic-bezier(0.22, 1, 0.36, 1)';
44
+ // Horizontal gap between the path centerline and the heading text start.
45
+ const CLERK_TEXT_GAP_FROM_PATH = 12;
46
+ // Radius of numbered step badges rendered on top of the path centerline.
47
+ const CLERK_STEP_BADGE_RADIUS = 7;
40
48
  function PortableClerkTOC({ toc, header, footer, title, emptyLabel = 'No headings', className, }) {
41
49
  return (jsxRuntime.jsxs(page.PageTOC, { className: className, children: [header, title !== null && title !== void 0 ? title : jsxRuntime.jsx(page.PageTOCTitle, {}), jsxRuntime.jsx(PortableClerkTOCScrollArea, { children: jsxRuntime.jsx(PortableClerkTOCItems, { toc: toc, emptyLabel: emptyLabel }) }), footer] }));
42
50
  }
@@ -128,22 +136,44 @@ function PortableClerkTOCItem({ item, isActive, resolvedContent, itemPadding, co
128
136
  }, className: cn('prose group relative py-1.5 text-sm transition-colors wrap-anywhere first:pt-0 last:pb-0 hover:text-fd-accent-foreground', isActive ? lib.themeIconColor : 'text-fd-muted-foreground'), children: jsxRuntime.jsx("span", { ref: contentRef, className: "relative z-10", children: resolvedContent }) }));
129
137
  }
130
138
  function ClerkOutline({ path, items, activePath, activeAnchors, activeEndpoint, }) {
139
+ const activeSet = new Set(activeAnchors);
140
+ const [displayPath, setDisplayPath] = React.useState(activePath);
141
+ const [fadingPath, setFadingPath] = React.useState(null);
142
+ React.useEffect(() => {
143
+ if (activePath === displayPath)
144
+ return;
145
+ if (!displayPath) {
146
+ setDisplayPath(activePath);
147
+ setFadingPath(null);
148
+ return;
149
+ }
150
+ setFadingPath(displayPath);
151
+ setDisplayPath(activePath);
152
+ const timeout = window.setTimeout(() => {
153
+ setFadingPath(null);
154
+ }, CLERK_ACTIVE_ANIMATION_DURATION_MS);
155
+ return () => window.clearTimeout(timeout);
156
+ }, [activePath, displayPath]);
157
+ const dotTranslate = activeEndpoint
158
+ ? `translate(${activeEndpoint.x - CLERK_ACTIVE_DOT_RADIUS}px, ${activeEndpoint.y - CLERK_ACTIVE_DOT_RADIUS}px)`
159
+ : undefined;
160
+ const transitionStyle = {
161
+ transitionDuration: `${CLERK_ACTIVE_ANIMATION_DURATION_MS}ms`,
162
+ transitionTimingFunction: CLERK_ACTIVE_ANIMATION_EASING,
163
+ };
131
164
  if (!path)
132
165
  return null;
133
- const activeSet = new Set(activeAnchors);
134
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: jsxRuntime.jsx("path", { d: path, className: "stroke-fd-foreground/15", fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round" }) }), jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: activePath ? (jsxRuntime.jsx("path", { d: activePath, fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round", stroke: lib.themeSvgIconColor })) : null }), jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: activeEndpoint ? (jsxRuntime.jsx("circle", { cx: activeEndpoint.x, cy: activeEndpoint.y, r: CLERK_ACTIVE_DOT_RADIUS, fill: lib.themeSvgIconColor })) : null }), jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-1 overflow-visible", width: "100%", height: "100%", children: items.map((item) => {
166
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: jsxRuntime.jsx("path", { d: path, className: "stroke-fd-foreground/15", fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round" }) }), jsxRuntime.jsxs("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: [fadingPath ? (jsxRuntime.jsx("path", { d: fadingPath, fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round", stroke: lib.themeSvgIconColor, className: "transition-opacity", style: Object.assign({ opacity: 0 }, transitionStyle) })) : null, displayPath ? (jsxRuntime.jsx("path", { d: displayPath, fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round", stroke: lib.themeSvgIconColor, className: "transition-opacity", style: Object.assign({ opacity: 1 }, transitionStyle) })) : null] }), jsxRuntime.jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0", children: jsxRuntime.jsx("div", { className: "absolute rounded-full transition-transform", style: Object.assign({ width: CLERK_ACTIVE_DOT_RADIUS * 2, height: CLERK_ACTIVE_DOT_RADIUS * 2, backgroundColor: lib.themeSvgIconColor, transform: dotTranslate, opacity: activeEndpoint ? 1 : 0 }, transitionStyle) }) }), jsxRuntime.jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-1 overflow-visible", width: "100%", height: "100%", children: items.map((item) => {
135
167
  if (!item.stepNumber)
136
168
  return null;
137
169
  const isActive = activeSet.has(item.url.slice(1));
138
- return (jsxRuntime.jsxs("g", { transform: `translate(${item.x}, ${item.y})`, children: [jsxRuntime.jsx("circle", { r: "7", fill: isActive ? lib.themeSvgIconColor : undefined, className: cn(!isActive && 'fill-black dark:fill-white') }), jsxRuntime.jsx("text", { y: "0.5", textAnchor: "middle", dominantBaseline: "middle", className: "fill-white text-[9px] font-medium dark:fill-black", children: item.stepNumber })] }, item.url));
170
+ return (jsxRuntime.jsxs("g", { transform: `translate(${item.x}, ${item.y})`, children: [jsxRuntime.jsx("circle", { r: CLERK_STEP_BADGE_RADIUS, fill: isActive ? lib.themeSvgIconColor : undefined, className: cn(!isActive && 'fill-black dark:fill-white') }), jsxRuntime.jsx("text", { y: "0.5", textAnchor: "middle", dominantBaseline: "middle", className: "fill-white text-[9px] font-medium dark:fill-black", children: item.stepNumber })] }, item.url));
139
171
  }) })] }));
140
172
  }
141
173
  function getItemOffset(depth) {
142
- if (depth <= 2)
143
- return 14;
144
- if (depth === 3)
145
- return 26;
146
- return 36;
174
+ const lineOffset = getLineOffset(depth);
175
+ const badgeRadius = depth === 3 ? CLERK_STEP_BADGE_RADIUS : 0;
176
+ return lineOffset + badgeRadius + CLERK_TEXT_GAP_FROM_PATH;
147
177
  }
148
178
  function getLineOffset(depth) {
149
179
  return depth >= 3 ? 18 : 6;
@@ -3,7 +3,7 @@ import { __rest } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import * as Primitive from 'fumadocs-core/toc';
5
5
  import { PageTOC, PageTOCTitle, PageTOCPopover, PageTOCPopoverTrigger, PageTOCPopoverContent } from 'fumadocs-ui/layouts/docs/page';
6
- import { useRef, useState, useMemo, useLayoutEffect } from 'react';
6
+ import { useRef, useState, useMemo, useLayoutEffect, useEffect } from 'react';
7
7
  import { themeSvgIconColor, themeIconColor } from '@windrun-huaiin/base-ui/lib';
8
8
 
9
9
  // Base stroke width for both the inactive rail and the active highlight path.
@@ -16,6 +16,14 @@ const CLERK_TURN_CURVE_HEIGHT = 12;
16
16
  const CLERK_TURN_CONTROL_FACTOR = 0.68;
17
17
  // Safety margin that keeps turns away from the heading rows themselves.
18
18
  const CLERK_TURN_GAP_MARGIN = 7;
19
+ // Shared duration for active rail fade transitions and endpoint dot movement.
20
+ const CLERK_ACTIVE_ANIMATION_DURATION_MS = 300;
21
+ // Easing curve for the active rail and dot; tuned for a slightly delayed, softer motion.
22
+ const CLERK_ACTIVE_ANIMATION_EASING = 'cubic-bezier(0.22, 1, 0.36, 1)';
23
+ // Horizontal gap between the path centerline and the heading text start.
24
+ const CLERK_TEXT_GAP_FROM_PATH = 12;
25
+ // Radius of numbered step badges rendered on top of the path centerline.
26
+ const CLERK_STEP_BADGE_RADIUS = 7;
19
27
  function PortableClerkTOC({ toc, header, footer, title, emptyLabel = 'No headings', className, }) {
20
28
  return (jsxs(PageTOC, { className: className, children: [header, title !== null && title !== void 0 ? title : jsx(PageTOCTitle, {}), jsx(PortableClerkTOCScrollArea, { children: jsx(PortableClerkTOCItems, { toc: toc, emptyLabel: emptyLabel }) }), footer] }));
21
29
  }
@@ -107,22 +115,44 @@ function PortableClerkTOCItem({ item, isActive, resolvedContent, itemPadding, co
107
115
  }, className: cn('prose group relative py-1.5 text-sm transition-colors wrap-anywhere first:pt-0 last:pb-0 hover:text-fd-accent-foreground', isActive ? themeIconColor : 'text-fd-muted-foreground'), children: jsx("span", { ref: contentRef, className: "relative z-10", children: resolvedContent }) }));
108
116
  }
109
117
  function ClerkOutline({ path, items, activePath, activeAnchors, activeEndpoint, }) {
118
+ const activeSet = new Set(activeAnchors);
119
+ const [displayPath, setDisplayPath] = useState(activePath);
120
+ const [fadingPath, setFadingPath] = useState(null);
121
+ useEffect(() => {
122
+ if (activePath === displayPath)
123
+ return;
124
+ if (!displayPath) {
125
+ setDisplayPath(activePath);
126
+ setFadingPath(null);
127
+ return;
128
+ }
129
+ setFadingPath(displayPath);
130
+ setDisplayPath(activePath);
131
+ const timeout = window.setTimeout(() => {
132
+ setFadingPath(null);
133
+ }, CLERK_ACTIVE_ANIMATION_DURATION_MS);
134
+ return () => window.clearTimeout(timeout);
135
+ }, [activePath, displayPath]);
136
+ const dotTranslate = activeEndpoint
137
+ ? `translate(${activeEndpoint.x - CLERK_ACTIVE_DOT_RADIUS}px, ${activeEndpoint.y - CLERK_ACTIVE_DOT_RADIUS}px)`
138
+ : undefined;
139
+ const transitionStyle = {
140
+ transitionDuration: `${CLERK_ACTIVE_ANIMATION_DURATION_MS}ms`,
141
+ transitionTimingFunction: CLERK_ACTIVE_ANIMATION_EASING,
142
+ };
110
143
  if (!path)
111
144
  return null;
112
- const activeSet = new Set(activeAnchors);
113
- return (jsxs(Fragment, { children: [jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: jsx("path", { d: path, className: "stroke-fd-foreground/15", fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round" }) }), jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: activePath ? (jsx("path", { d: activePath, fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round", stroke: themeSvgIconColor })) : null }), jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: activeEndpoint ? (jsx("circle", { cx: activeEndpoint.x, cy: activeEndpoint.y, r: CLERK_ACTIVE_DOT_RADIUS, fill: themeSvgIconColor })) : null }), jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-1 overflow-visible", width: "100%", height: "100%", children: items.map((item) => {
145
+ return (jsxs(Fragment, { children: [jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: jsx("path", { d: path, className: "stroke-fd-foreground/15", fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round" }) }), jsxs("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0 overflow-visible", width: "100%", height: "100%", children: [fadingPath ? (jsx("path", { d: fadingPath, fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round", stroke: themeSvgIconColor, className: "transition-opacity", style: Object.assign({ opacity: 0 }, transitionStyle) })) : null, displayPath ? (jsx("path", { d: displayPath, fill: "none", strokeWidth: CLERK_PATH_STROKE_WIDTH, strokeLinecap: "round", strokeLinejoin: "round", stroke: themeSvgIconColor, className: "transition-opacity", style: Object.assign({ opacity: 1 }, transitionStyle) })) : null] }), jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-0", children: jsx("div", { className: "absolute rounded-full transition-transform", style: Object.assign({ width: CLERK_ACTIVE_DOT_RADIUS * 2, height: CLERK_ACTIVE_DOT_RADIUS * 2, backgroundColor: themeSvgIconColor, transform: dotTranslate, opacity: activeEndpoint ? 1 : 0 }, transitionStyle) }) }), jsx("svg", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 z-1 overflow-visible", width: "100%", height: "100%", children: items.map((item) => {
114
146
  if (!item.stepNumber)
115
147
  return null;
116
148
  const isActive = activeSet.has(item.url.slice(1));
117
- return (jsxs("g", { transform: `translate(${item.x}, ${item.y})`, children: [jsx("circle", { r: "7", fill: isActive ? themeSvgIconColor : undefined, className: cn(!isActive && 'fill-black dark:fill-white') }), jsx("text", { y: "0.5", textAnchor: "middle", dominantBaseline: "middle", className: "fill-white text-[9px] font-medium dark:fill-black", children: item.stepNumber })] }, item.url));
149
+ return (jsxs("g", { transform: `translate(${item.x}, ${item.y})`, children: [jsx("circle", { r: CLERK_STEP_BADGE_RADIUS, fill: isActive ? themeSvgIconColor : undefined, className: cn(!isActive && 'fill-black dark:fill-white') }), jsx("text", { y: "0.5", textAnchor: "middle", dominantBaseline: "middle", className: "fill-white text-[9px] font-medium dark:fill-black", children: item.stepNumber })] }, item.url));
118
150
  }) })] }));
119
151
  }
120
152
  function getItemOffset(depth) {
121
- if (depth <= 2)
122
- return 14;
123
- if (depth === 3)
124
- return 26;
125
- return 36;
153
+ const lineOffset = getLineOffset(depth);
154
+ const badgeRadius = depth === 3 ? CLERK_STEP_BADGE_RADIUS : 0;
155
+ return lineOffset + badgeRadius + CLERK_TEXT_GAP_FROM_PATH;
126
156
  }
127
157
  function getLineOffset(depth) {
128
158
  return depth >= 3 ? 18 : 6;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/third-ui",
3
- "version": "14.4.0",
3
+ "version": "14.4.1",
4
4
  "description": "Third-party integrated UI components for windrun-huaiin projects",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -1,5 +1,9 @@
1
1
  'use client';
2
2
 
3
+ /**
4
+ * References most of its code and SVG animation design from
5
+ * https://github.com/fuma-nama/fumadocs/blob/dev/packages/radix-ui/src/components/toc/clerk.tsx
6
+ */
3
7
  import * as Primitive from 'fumadocs-core/toc';
4
8
  import {
5
9
  PageTOC,
@@ -11,6 +15,7 @@ import {
11
15
  import {
12
16
  type ComponentProps,
13
17
  type ReactNode,
18
+ useEffect,
14
19
  useLayoutEffect,
15
20
  useMemo,
16
21
  useRef,
@@ -57,6 +62,14 @@ const CLERK_TURN_CURVE_HEIGHT = 12;
57
62
  const CLERK_TURN_CONTROL_FACTOR = 0.68;
58
63
  // Safety margin that keeps turns away from the heading rows themselves.
59
64
  const CLERK_TURN_GAP_MARGIN = 7;
65
+ // Shared duration for active rail fade transitions and endpoint dot movement.
66
+ const CLERK_ACTIVE_ANIMATION_DURATION_MS = 300;
67
+ // Easing curve for the active rail and dot; tuned for a slightly delayed, softer motion.
68
+ const CLERK_ACTIVE_ANIMATION_EASING = 'cubic-bezier(0.22, 1, 0.36, 1)';
69
+ // Horizontal gap between the path centerline and the heading text start.
70
+ const CLERK_TEXT_GAP_FROM_PATH = 12;
71
+ // Radius of numbered step badges rendered on top of the path centerline.
72
+ const CLERK_STEP_BADGE_RADIUS = 7;
60
73
 
61
74
  export function PortableClerkTOC({
62
75
  toc,
@@ -298,9 +311,39 @@ function ClerkOutline({
298
311
  activeAnchors: string[];
299
312
  activeEndpoint: { x: number; y: number } | null;
300
313
  }) {
301
- if (!path) return null;
302
-
303
314
  const activeSet = new Set(activeAnchors);
315
+ const [displayPath, setDisplayPath] = useState(activePath);
316
+ const [fadingPath, setFadingPath] = useState<string | null>(null);
317
+
318
+ useEffect(() => {
319
+ if (activePath === displayPath) return;
320
+ if (!displayPath) {
321
+ setDisplayPath(activePath);
322
+ setFadingPath(null);
323
+ return;
324
+ }
325
+
326
+ setFadingPath(displayPath);
327
+ setDisplayPath(activePath);
328
+
329
+ const timeout = window.setTimeout(() => {
330
+ setFadingPath(null);
331
+ }, CLERK_ACTIVE_ANIMATION_DURATION_MS);
332
+
333
+ return () => window.clearTimeout(timeout);
334
+ }, [activePath, displayPath]);
335
+
336
+ const dotTranslate = activeEndpoint
337
+ ? `translate(${activeEndpoint.x - CLERK_ACTIVE_DOT_RADIUS}px, ${
338
+ activeEndpoint.y - CLERK_ACTIVE_DOT_RADIUS
339
+ }px)`
340
+ : undefined;
341
+ const transitionStyle = {
342
+ transitionDuration: `${CLERK_ACTIVE_ANIMATION_DURATION_MS}ms`,
343
+ transitionTimingFunction: CLERK_ACTIVE_ANIMATION_EASING,
344
+ };
345
+
346
+ if (!path) return null;
304
347
 
305
348
  return (
306
349
  <>
@@ -325,32 +368,50 @@ function ClerkOutline({
325
368
  width="100%"
326
369
  height="100%"
327
370
  >
328
- {activePath ? (
371
+ {fadingPath ? (
329
372
  <path
330
- d={activePath}
373
+ d={fadingPath}
331
374
  fill="none"
332
375
  strokeWidth={CLERK_PATH_STROKE_WIDTH}
333
376
  strokeLinecap="round"
334
377
  strokeLinejoin="round"
335
378
  stroke={themeSvgIconColor}
379
+ className="transition-opacity"
380
+ style={{
381
+ opacity: 0,
382
+ ...transitionStyle,
383
+ }}
336
384
  />
337
385
  ) : null}
338
- </svg>
339
- <svg
340
- aria-hidden="true"
341
- className="pointer-events-none absolute inset-0 z-0 overflow-visible"
342
- width="100%"
343
- height="100%"
344
- >
345
- {activeEndpoint ? (
346
- <circle
347
- cx={activeEndpoint.x}
348
- cy={activeEndpoint.y}
349
- r={CLERK_ACTIVE_DOT_RADIUS}
350
- fill={themeSvgIconColor}
386
+ {displayPath ? (
387
+ <path
388
+ d={displayPath}
389
+ fill="none"
390
+ strokeWidth={CLERK_PATH_STROKE_WIDTH}
391
+ strokeLinecap="round"
392
+ strokeLinejoin="round"
393
+ stroke={themeSvgIconColor}
394
+ className="transition-opacity"
395
+ style={{
396
+ opacity: 1,
397
+ ...transitionStyle,
398
+ }}
351
399
  />
352
400
  ) : null}
353
401
  </svg>
402
+ <div aria-hidden="true" className="pointer-events-none absolute inset-0 z-0">
403
+ <div
404
+ className="absolute rounded-full transition-transform"
405
+ style={{
406
+ width: CLERK_ACTIVE_DOT_RADIUS * 2,
407
+ height: CLERK_ACTIVE_DOT_RADIUS * 2,
408
+ backgroundColor: themeSvgIconColor,
409
+ transform: dotTranslate,
410
+ opacity: activeEndpoint ? 1 : 0,
411
+ ...transitionStyle,
412
+ }}
413
+ />
414
+ </div>
354
415
  <svg
355
416
  aria-hidden="true"
356
417
  className="pointer-events-none absolute inset-0 z-1 overflow-visible"
@@ -365,7 +426,7 @@ function ClerkOutline({
365
426
  return (
366
427
  <g key={item.url} transform={`translate(${item.x}, ${item.y})`}>
367
428
  <circle
368
- r="7"
429
+ r={CLERK_STEP_BADGE_RADIUS}
369
430
  fill={isActive ? themeSvgIconColor : undefined}
370
431
  className={cn(!isActive && 'fill-black dark:fill-white')}
371
432
  />
@@ -386,9 +447,9 @@ function ClerkOutline({
386
447
  }
387
448
 
388
449
  function getItemOffset(depth: number): number {
389
- if (depth <= 2) return 14;
390
- if (depth === 3) return 26;
391
- return 36;
450
+ const lineOffset = getLineOffset(depth);
451
+ const badgeRadius = depth === 3 ? CLERK_STEP_BADGE_RADIUS : 0;
452
+ return lineOffset + badgeRadius + CLERK_TEXT_GAP_FROM_PATH;
392
453
  }
393
454
 
394
455
  function getLineOffset(depth: number): number {
@@ -614,7 +675,7 @@ function mergeRefs<T>(
614
675
  }
615
676
 
616
677
  try {
617
- (ref as React.MutableRefObject<T | null>).current = node;
678
+ (ref as React.RefObject<T | null>).current = node;
618
679
  } catch {
619
680
  // ignore readonly refs
620
681
  }