@coinbase/cds-web 8.13.2 → 8.13.6

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/CHANGELOG.md CHANGED
@@ -8,6 +8,26 @@ All notable changes to this project will be documented in this file.
8
8
 
9
9
  <!-- template-start -->
10
10
 
11
+ ## 8.13.6 ((10/3/2025, 01:54 PM PST))
12
+
13
+ This is an artificial version bump with no new change.
14
+
15
+ ## 8.13.5 (10/3/2025 PST)
16
+
17
+ #### 🐞 Fixes
18
+
19
+ - Support custom font in SegmentedTab. [[#65](https://github.com/coinbase/cds/pull/65)]
20
+
21
+ ## 8.13.4 (10/1/2025 PST)
22
+
23
+ #### 🐞 Fixes
24
+
25
+ - Fix tour position flickering issue.
26
+
27
+ ## 8.13.3 ((10/1/2025, 03:05 PM PST))
28
+
29
+ This is an artificial version bump with no new change.
30
+
11
31
  ## 8.13.2 (10/1/2025 PST)
12
32
 
13
33
  #### 🐞 Fixes
@@ -1,23 +1,22 @@
1
1
  import React from 'react';
2
2
  import type { ThemeVars } from '@coinbase/cds-common/core/theme';
3
3
  import { type TabValue } from '@coinbase/cds-common/tabs/useTabs';
4
- import type { SharedProps } from '@coinbase/cds-common/types/SharedProps';
5
- export type SegmentedTabProps<T extends string = string> = {
6
- /**
7
- * Text color when the SegmentedTab is active.
8
- * @default negativeForeground
9
- */
10
- activeColor?: ThemeVars.Color;
11
- /**
12
- * Text color when the SegmentedTab is inactive.
13
- * @default foreground
14
- */
15
- color?: ThemeVars.Color;
16
- /** Callback that is fired when the SegmentedTab is clicked. */
17
- onClick?: (id: T, event: React.MouseEvent) => void;
18
- } & TabValue<T> &
19
- Omit<React.ComponentProps<'button'>, 'ref'> &
20
- SharedProps;
4
+ import { type PressableBaseProps } from '../system/Pressable';
5
+ export type SegmentedTabProps<T extends string = string> = PressableBaseProps &
6
+ TabValue<T> & {
7
+ /**
8
+ * Text color when the SegmentedTab is active.
9
+ * @default negativeForeground
10
+ */
11
+ activeColor?: ThemeVars.Color;
12
+ /**
13
+ * Text color when the SegmentedTab is inactive.
14
+ * @default foreground
15
+ */
16
+ color?: ThemeVars.Color;
17
+ /** Callback that is fired when the SegmentedTab is clicked. */
18
+ onClick?: (id: T, event: React.MouseEvent) => void;
19
+ };
21
20
  type SegmentedTabComponent = <T extends string = string>(
22
21
  props: SegmentedTabProps<T> & {
23
22
  ref?: React.ForwardedRef<HTMLButtonElement>;
@@ -25,26 +24,29 @@ type SegmentedTabComponent = <T extends string = string>(
25
24
  ) => React.ReactElement;
26
25
  declare const SegmentedTabComponent: React.MemoExoticComponent<
27
26
  React.ForwardRefExoticComponent<
28
- {
29
- /**
30
- * Text color when the SegmentedTab is active.
31
- * @default negativeForeground
32
- */
33
- activeColor?: ThemeVars.Color;
34
- /**
35
- * Text color when the SegmentedTab is inactive.
36
- * @default foreground
37
- */
38
- color?: ThemeVars.Color;
39
- /** Callback that is fired when the SegmentedTab is clicked. */
40
- onClick?: ((id: string, event: React.MouseEvent) => void) | undefined;
41
- } & TabValue<string> &
42
- Omit<
43
- React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
44
- 'ref'
27
+ import('@coinbase/cds-common').ComponentEventHandlerProps & {
28
+ noScaleOnPress?: boolean;
29
+ focusable?: boolean;
30
+ } & Omit<
31
+ import('../system').InteractableBaseProps,
32
+ | 'focusable'
33
+ | keyof import('@coinbase/cds-common').ComponentEventHandlerProps
34
+ | 'noScaleOnPress'
45
35
  > &
46
- SharedProps &
47
- React.RefAttributes<HTMLButtonElement>
36
+ TabValue<string> & {
37
+ /**
38
+ * Text color when the SegmentedTab is active.
39
+ * @default negativeForeground
40
+ */
41
+ activeColor?: ThemeVars.Color;
42
+ /**
43
+ * Text color when the SegmentedTab is inactive.
44
+ * @default foreground
45
+ */
46
+ color?: ThemeVars.Color;
47
+ /** Callback that is fired when the SegmentedTab is clicked. */
48
+ onClick?: ((id: string, event: React.MouseEvent) => void) | undefined;
49
+ } & React.RefAttributes<HTMLButtonElement>
48
50
  >
49
51
  >;
50
52
  export declare const SegmentedTab: SegmentedTabComponent;
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedTab.d.ts","sourceRoot":"","sources":["../../src/tabs/SegmentedTab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAC;AAuC1E,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;IACzD;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC;IACxB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CACpD,GAAG,QAAQ,CAAC,CAAC,CAAC,GACb,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,GAC3C,WAAW,CAAC;AAMd,KAAK,qBAAqB,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACrD,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAC1E,KAAK,CAAC,YAAY,CAAC;AAExB,QAAA,MAAM,qBAAqB;IAxBzB;;;OAGG;kBACW,SAAS,CAAC,KAAK;IAC7B;;;OAGG;YACK,SAAS,CAAC,KAAK;IACvB,+DAA+D;mCACtC,KAAK,CAAC,UAAU,KAAK,IAAI;sLAkFnD,CAAC;AAIF,eAAO,MAAM,YAAY,EAA4B,qBAAqB,CAAC"}
1
+ {"version":3,"file":"SegmentedTab.d.ts","sourceRoot":"","sources":["../../src/tabs/SegmentedTab.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAEjE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAOlE,OAAO,EAAa,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAkCzE,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,kBAAkB,GAC3E,QAAQ,CAAC,CAAC,CAAC,GAAG;IACZ;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC;IACxB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;CACpD,CAAC;AAMJ,KAAK,qBAAqB,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACrD,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAA;CAAE,KAC1E,KAAK,CAAC,YAAY,CAAC;AAExB,QAAA,MAAM,qBAAqB;;;;IAtBvB;;;OAGG;kBACW,SAAS,CAAC,KAAK;IAC7B;;;OAGG;YACK,SAAS,CAAC,KAAK;IACvB,+DAA+D;mCACtC,KAAK,CAAC,UAAU,KAAK,IAAI;4CAuGrD,CAAC;AAIF,eAAO,MAAM,YAAY,EAA4B,qBAAqB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tour.d.ts","sourceRoot":"","sources":["../../src/tour/Tour.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAQ9D,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EAEvB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qDAAqD,CAAC;AACpG,OAAO,EAGL,KAAK,oBAAoB,EAGzB,KAAK,aAAa,EAElB,KAAK,YAAY,EAElB,MAAM,wBAAwB,CAAC;AA0BhC,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,wBAAwB,EAAE,IAAI,CAAC;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC;AAEjE,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,oBAAoB,CAAC;IAC7C;;OAEG;IACH,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC;;OAEG;IACH,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,IAAI,CAAC,wBAAwB,EAAE,oBAAoB,GAAG,yBAAyB,GAAG,IAAI,CAAC,GACzF,WAAW,CAAC;AAsDd,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;AAkIzF,eAAO,MAAM,IAAI,EAAoB,MAAM,CAAC"}
1
+ {"version":3,"file":"Tour.d.ts","sourceRoot":"","sources":["../../src/tour/Tour.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAM9D,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,sBAAsB,EAEvB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,qDAAqD,CAAC;AACpG,OAAO,EAGL,KAAK,oBAAoB,EAGzB,KAAK,aAAa,EAElB,KAAK,YAAY,EAElB,MAAM,wBAAwB,CAAC;AA0BhC,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,wBAAwB,EAAE,IAAI,CAAC;IAC/B;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC;AAEjE,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,oBAAoB,CAAC;IAC7C;;OAEG;IACH,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC;;OAEG;IACH,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,IAAI,CAAC,wBAAwB,EAAE,oBAAoB,GAAG,yBAAyB,GAAG,IAAI,CAAC,GACzF,WAAW,CAAC;AAsDd,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;AA8KzF,eAAO,MAAM,IAAI,EAAoB,MAAM,CAAC"}
@@ -1,4 +1,4 @@
1
- const _excluded = ["id", "label", "disabled", "onClick", "color", "activeColor", "className", "testID"];
1
+ const _excluded = ["id", "label", "disabled", "onClick", "color", "activeColor", "className", "testID", "font", "fontFamily", "fontSize", "fontWeight", "lineHeight", "textAlign", "textTransform"];
2
2
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
4
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -11,6 +11,7 @@ import { useTabsContext } from '@coinbase/cds-common/tabs/TabsContext';
11
11
  import { m as motion } from 'framer-motion';
12
12
  import { cx } from '../cx';
13
13
  import { Box } from '../layout/Box';
14
+ import { Pressable } from '../system/Pressable';
14
15
  import { Text } from '../typography/Text';
15
16
  import { tabsTransitionConfig } from './Tabs';
16
17
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -28,7 +29,14 @@ const SegmentedTabComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
28
29
  color = 'fg',
29
30
  activeColor = 'fgInverse',
30
31
  className,
31
- testID
32
+ testID,
33
+ font = 'headline',
34
+ fontFamily,
35
+ fontSize,
36
+ fontWeight,
37
+ lineHeight,
38
+ textAlign,
39
+ textTransform
32
40
  } = _ref,
33
41
  props = _objectWithoutProperties(_ref, _excluded);
34
42
  const {
@@ -49,15 +57,22 @@ const SegmentedTabComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
49
57
  transition: tabsTransitionConfig,
50
58
  initial: false
51
59
  }), [activeColor, color, isActive]);
52
- return /*#__PURE__*/_jsx("button", _objectSpread(_objectSpread({
60
+ return /*#__PURE__*/_jsx(Pressable, _objectSpread(_objectSpread({
53
61
  ref: ref,
54
62
  "aria-checked": isActive,
55
63
  className: cx(insetFocusRingCss, buttonCss, isDisabled && buttonDisabledCss, disabledProp && !allTabsDisabled && disabledCss, className),
56
64
  "data-testid": testID,
57
65
  disabled: isDisabled,
66
+ font: font,
67
+ fontFamily: fontFamily,
68
+ fontSize: fontSize,
69
+ fontWeight: fontWeight,
58
70
  id: id,
71
+ lineHeight: lineHeight,
59
72
  onClick: handlePress,
60
73
  role: "radio",
74
+ textAlign: textAlign,
75
+ textTransform: textTransform,
61
76
  type: "button"
62
77
  }, props), {}, {
63
78
  children: /*#__PURE__*/_jsx(Box, {
@@ -66,7 +81,13 @@ const SegmentedTabComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, r
66
81
  paddingX: 2,
67
82
  paddingY: 1,
68
83
  children: typeof label === 'string' ? /*#__PURE__*/_jsx(MotionText, _objectSpread(_objectSpread({
69
- font: "headline"
84
+ font: font,
85
+ fontFamily: fontFamily,
86
+ fontSize: fontSize,
87
+ fontWeight: fontWeight,
88
+ lineHeight: lineHeight,
89
+ textAlign: textAlign,
90
+ textTransform: textTransform
70
91
  }, motionProps), {}, {
71
92
  children: label
72
93
  })) : label
package/esm/tour/Tour.js CHANGED
@@ -4,9 +4,7 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
4
4
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
5
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6
6
  import React, { useCallback, useEffect, useRef } from 'react';
7
- import { useRefMap } from '@coinbase/cds-common/hooks/useRefMap';
8
7
  import { OverlayContentContext } from '@coinbase/cds-common/overlays/OverlayContentContext';
9
- import { RefMapContext } from '@coinbase/cds-common/system/RefMapContext';
10
8
  import { TourContext } from '@coinbase/cds-common/tour/TourContext';
11
9
  import { useTour } from '@coinbase/cds-common/tour/useTour';
12
10
  import { arrow as arrowMiddleware, autoPlacement, autoUpdate, offset, shift, useFloating } from '@floating-ui/react-dom';
@@ -70,6 +68,8 @@ const scrollIntoView = async (element, scrollOptions) => {
70
68
  });
71
69
  await waitForScroll();
72
70
  };
71
+ const defaultTourStepOffset = 24;
72
+ const defaultTourStepShiftPadding = 32;
73
73
  const TourComponent = _ref => {
74
74
  var _activeTourStep$Arrow, _activeTourStep$hideO, _activeTourStep$tourM, _activeTourStep$tourM2;
75
75
  let {
@@ -80,7 +80,7 @@ const TourComponent = _ref => {
80
80
  TourMaskComponent = DefaultTourMask,
81
81
  TourStepArrowComponent = DefaultTourStepArrow,
82
82
  hideOverlay,
83
- tourStepOffset = 24,
83
+ tourStepOffset = defaultTourStepOffset,
84
84
  tourStepShift,
85
85
  tourStepAutoPlacement,
86
86
  tourMaskPadding,
@@ -93,12 +93,20 @@ const TourComponent = _ref => {
93
93
  id,
94
94
  testID
95
95
  } = _ref;
96
- const refMap = useRefMap();
97
96
  const tourStepArrowRef = useRef(null);
98
97
  const RenderedTourStep = activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.Component;
99
98
  const RenderedTourStepArrow = (_activeTourStep$Arrow = activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.ArrowComponent) !== null && _activeTourStep$Arrow !== void 0 ? _activeTourStep$Arrow : TourStepArrowComponent;
100
- const activeTourStepTarget = activeTourStep ? refMap.getRef(activeTourStep.id) : null;
101
- const defaultTourStepShiftPadding = 32;
99
+
100
+ // This state is used to store the active tour step target element.
101
+ // const [activeTourStepTarget, setActiveTourStepTarget] = useState<HTMLElement | null>(null);
102
+
103
+ const blockScroll = useScrollBlocker();
104
+ const [animation, animationApi] = useSpring(() => ({
105
+ from: {
106
+ opacity: 0
107
+ },
108
+ config: springConfig.slow
109
+ }), []);
102
110
  const {
103
111
  refs,
104
112
  floatingStyles,
@@ -114,88 +122,110 @@ const TourComponent = _ref => {
114
122
  })],
115
123
  whileElementsMounted: autoUpdate
116
124
  });
117
- const [animation, animationApi] = useSpring(() => ({
118
- from: {
119
- opacity: 0
120
- },
121
- config: springConfig.slow
122
- }), []);
123
125
  const handleChange = useCallback(tourStep => {
124
- // If the opacity is already 0, animating it to 0 does not trigger `onRest`
125
- if (animation.opacity.get() === 0) return onChange(tourStep);
126
126
  void animationApi.start({
127
127
  to: {
128
128
  opacity: 0
129
129
  },
130
130
  config: springConfig.stiff,
131
- onRest: () => onChange(tourStep)
131
+ onResolve: () => onChange(tourStep)
132
132
  });
133
- }, [animation.opacity, animationApi, onChange]);
134
- const revealTourStep = useCallback(async () => {
135
- var _activeTourStep$scrol;
136
- if (!disableAutoScroll && !(activeTourStep !== null && activeTourStep !== void 0 && activeTourStep.disableAutoScroll)) await scrollIntoView(activeTourStepTarget, (_activeTourStep$scrol = activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.scrollOptions) !== null && _activeTourStep$scrol !== void 0 ? _activeTourStep$scrol : scrollOptions);
137
- refs.setReference(activeTourStepTarget);
138
- void animationApi.start({
139
- to: {
140
- opacity: 1
141
- },
142
- config: springConfig.slow
143
- });
144
- }, [activeTourStep, activeTourStepTarget, refs, animationApi, disableAutoScroll, scrollOptions]);
133
+ }, [animationApi, onChange]);
145
134
  const api = useTour({
146
135
  steps,
147
136
  activeTourStep,
148
137
  onChange: handleChange
149
138
  });
150
- const blockScroll = useScrollBlocker();
151
- useEffect(() => {
152
- if (!activeTourStep) return;
153
- blockScroll(true);
139
+ const {
140
+ activeTourStepTarget,
141
+ setActiveTourStepTarget
142
+ } = api;
143
+
144
+ // Component Lifecycle & Side Effects
145
+ // ---------------------------------------------------------------------------
146
+ // This component's visual side effects (scrolling and animations) are driven
147
+ // by a single callback, `handleSetActiveTourStepTarget`.
148
+ //
149
+ // This function is called from the `TourStep` component's ref callback
150
+ // whenever the active step changes. Because the ref callback is tied to the
151
+ // lifecycle of the `TourStep`, it reliably fires whenever a new step becomes
152
+ // active, regardless of whether the change was triggered internally (by a
153
+ // "Next" button) or externally (by a direct prop change).
154
+ //
155
+ // This centralizes the logic for revealing a step: we get the target element,
156
+ // scroll it into view, and then kick off the fade-in animation, all in one
157
+ // sequential, event-driven flow.
158
+
159
+ const handleActiveTourStepTargetChange = useCallback(target => {
160
+ refs.setReference(target);
161
+ setActiveTourStepTarget(target);
162
+ const revealTourStep = async () => {
163
+ // Scroll the new target into view.
164
+ if (!disableAutoScroll && !(activeTourStep !== null && activeTourStep !== void 0 && activeTourStep.disableAutoScroll)) {
165
+ var _activeTourStep$scrol;
166
+ await scrollIntoView(target, (_activeTourStep$scrol = activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.scrollOptions) !== null && _activeTourStep$scrol !== void 0 ? _activeTourStep$scrol : scrollOptions);
167
+ }
168
+ void animationApi.start({
169
+ to: {
170
+ opacity: 1
171
+ },
172
+ config: springConfig.slow
173
+ });
174
+ };
154
175
  void revealTourStep();
155
- return () => blockScroll(false);
156
- }, [activeTourStep, blockScroll, revealTourStep]);
176
+ }, [refs, setActiveTourStepTarget, disableAutoScroll, activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.disableAutoScroll, activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.scrollOptions, animationApi, scrollOptions]);
177
+
178
+ // Manages scroll locking for the tour's duration. `useEffect` is used to
179
+ // guarantee that scroll is re-enabled when the tour is closed or unmounted.
180
+ useEffect(() => {
181
+ if (activeTourStep !== null && activeTourStep !== void 0 && activeTourStep.id) {
182
+ blockScroll(true);
183
+ }
184
+ return () => {
185
+ blockScroll(false);
186
+ };
187
+ }, [activeTourStep, animationApi, blockScroll, disableAutoScroll, scrollOptions]);
157
188
  return /*#__PURE__*/_jsx(OverlayContentContext.Provider, {
158
189
  value: overlayContentContextValue,
159
- children: /*#__PURE__*/_jsx(RefMapContext.Provider, {
160
- value: refMap,
161
- children: /*#__PURE__*/_jsxs(TourContext.Provider, {
162
- value: api,
163
- children: [children, !!RenderedTourStep && /*#__PURE__*/_jsx(Portal, {
164
- containerId: modalContainerId,
165
- disablePortal: disablePortal,
166
- children: /*#__PURE__*/_jsxs("div", {
167
- "aria-label": accessibilityLabel,
168
- "aria-labelledby": accessibilityLabelledBy,
169
- "aria-modal": "true",
170
- className: containerCss,
171
- "data-testid": testID,
172
- id: id,
173
- role: "dialog",
174
- children: [!((_activeTourStep$hideO = activeTourStep.hideOverlay) !== null && _activeTourStep$hideO !== void 0 ? _activeTourStep$hideO : hideOverlay) && activeTourStepTarget && /*#__PURE__*/_jsx(animated.div, {
175
- style: animation,
176
- children: /*#__PURE__*/_jsx(TourMaskComponent, {
177
- activeTourStepTargetRect: activeTourStepTarget.getBoundingClientRect(),
178
- borderRadius: (_activeTourStep$tourM = activeTourStep.tourMaskBorderRadius) !== null && _activeTourStep$tourM !== void 0 ? _activeTourStep$tourM : tourMaskBorderRadius,
179
- padding: (_activeTourStep$tourM2 = activeTourStep.tourMaskPadding) !== null && _activeTourStep$tourM2 !== void 0 ? _activeTourStep$tourM2 : tourMaskPadding
180
- })
181
- }), /*#__PURE__*/_jsx("div", {
182
- ref: refs.setFloating,
183
- style: floatingStyles,
184
- children: /*#__PURE__*/_jsx(FocusTrap, {
185
- children: /*#__PURE__*/_jsxs(animated.div, {
186
- style: animation,
187
- children: [/*#__PURE__*/_jsx(RenderedTourStepArrow, {
188
- ref: tourStepArrowRef,
189
- arrow: arrow,
190
- placement: placement,
191
- style: activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.arrowStyle
192
- }), /*#__PURE__*/_jsx(RenderedTourStep, _objectSpread({}, activeTourStep))]
193
- })
190
+ children: /*#__PURE__*/_jsxs(TourContext.Provider, {
191
+ value: _objectSpread(_objectSpread({}, api), {}, {
192
+ setActiveTourStepTarget: handleActiveTourStepTargetChange
193
+ }),
194
+ children: [children, !!RenderedTourStep && /*#__PURE__*/_jsx(Portal, {
195
+ containerId: modalContainerId,
196
+ disablePortal: disablePortal,
197
+ children: /*#__PURE__*/_jsxs("div", {
198
+ "aria-label": accessibilityLabel,
199
+ "aria-labelledby": accessibilityLabelledBy,
200
+ "aria-modal": "true",
201
+ className: containerCss,
202
+ "data-testid": testID,
203
+ id: id,
204
+ role: "dialog",
205
+ children: [!((_activeTourStep$hideO = activeTourStep.hideOverlay) !== null && _activeTourStep$hideO !== void 0 ? _activeTourStep$hideO : hideOverlay) && activeTourStepTarget && /*#__PURE__*/_jsx(animated.div, {
206
+ style: animation,
207
+ children: /*#__PURE__*/_jsx(TourMaskComponent, {
208
+ activeTourStepTargetRect: activeTourStepTarget.getBoundingClientRect(),
209
+ borderRadius: (_activeTourStep$tourM = activeTourStep.tourMaskBorderRadius) !== null && _activeTourStep$tourM !== void 0 ? _activeTourStep$tourM : tourMaskBorderRadius,
210
+ padding: (_activeTourStep$tourM2 = activeTourStep.tourMaskPadding) !== null && _activeTourStep$tourM2 !== void 0 ? _activeTourStep$tourM2 : tourMaskPadding
211
+ })
212
+ }), /*#__PURE__*/_jsx("div", {
213
+ ref: refs.setFloating,
214
+ style: floatingStyles,
215
+ children: /*#__PURE__*/_jsx(FocusTrap, {
216
+ children: /*#__PURE__*/_jsxs(animated.div, {
217
+ style: animation,
218
+ children: [/*#__PURE__*/_jsx(RenderedTourStepArrow, {
219
+ ref: tourStepArrowRef,
220
+ arrow: arrow,
221
+ placement: placement,
222
+ style: activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.arrowStyle
223
+ }), /*#__PURE__*/_jsx(RenderedTourStep, _objectSpread({}, activeTourStep))]
194
224
  })
195
- })]
196
- })
197
- })]
198
- })
225
+ })
226
+ })]
227
+ })
228
+ })]
199
229
  })
200
230
  });
201
231
  };
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback } from 'react';
2
- import { useRefMapContext } from '@coinbase/cds-common/system/RefMapContext';
2
+ import { useTourContext } from '@coinbase/cds-common/tour/TourContext';
3
3
  import { jsx as _jsx } from "react/jsx-runtime";
4
4
  /**
5
5
  * The TourStep component wraps the target element (children) that you want to highlight during a step
@@ -12,9 +12,10 @@ export const TourStep = _ref => {
12
12
  children
13
13
  } = _ref;
14
14
  const {
15
- registerRef
16
- } = useRefMapContext();
17
- const refCallback = useCallback(ref => ref && registerRef(id, ref), [registerRef, id]);
15
+ activeTourStep,
16
+ setActiveTourStepTarget
17
+ } = useTourContext();
18
+ const refCallback = useCallback(ref => (activeTourStep === null || activeTourStep === void 0 ? void 0 : activeTourStep.id) === id && ref && setActiveTourStepTarget(ref), [activeTourStep, id, setActiveTourStepTarget]);
18
19
  return /*#__PURE__*/_jsx("div", {
19
20
  ref: refCallback,
20
21
  children: children
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cds-web",
3
- "version": "8.13.2",
3
+ "version": "8.13.6",
4
4
  "description": "Coinbase Design System - Web",
5
5
  "repository": {
6
6
  "type": "git",
@@ -147,9 +147,9 @@
147
147
  "react-dom": "^18.3.1"
148
148
  },
149
149
  "dependencies": {
150
- "@coinbase/cds-common": "^8.13.2",
150
+ "@coinbase/cds-common": "^8.13.6",
151
151
  "@coinbase/cds-icons": "^5.4.1",
152
- "@coinbase/cds-illustrations": "^4.22.1",
152
+ "@coinbase/cds-illustrations": "^4.23.0",
153
153
  "@coinbase/cds-lottie-files": "^3.3.1",
154
154
  "@coinbase/cds-utils": "^2.3.2",
155
155
  "@floating-ui/react-dom": "^2.1.1",