Skip to content
Open
163 changes: 142 additions & 21 deletions apps/docs/docs/components/inputs/SlideButton/_mobileExamples.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';

### SlideButton
## Basics

Use the `onChange` prop to listen and make changes to the `checked` state.
Use the `onChange` callback to update the `checked` state. This is the primary callback that controls both the visual and accessible state of the component.

<ThemedImage
sources={{
Expand All @@ -25,17 +25,20 @@ function Example() {
<SlideButton
checked={checked}
onChange={setChecked}
onSlideComplete={() => console.log('Completed')}
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirming..."
/>
);
}
```

### Negative SlideButton
## Variants

Use the `variant` prop to change the visual style. The default variant is `primary`.

### Negative

You can use the `variant` prop to change the color of the button.
Use the `negative` variant for destructive or high-stakes confirmations.

<ThemedImage
sources={{
Expand All @@ -46,7 +49,7 @@ You can use the `variant` prop to change the color of the button.
borderRadius: 'var(--borderRadius-400)',
border: '1.5px solid var(--color-bgLine)',
}}
alt="Negative variant slide button"
alt="Negative variant slide button with red styling"
/>

```jsx
Expand All @@ -57,7 +60,6 @@ function Example() {
<SlideButton
checked={checked}
onChange={setChecked}
onSlideComplete={() => console.log('Completed')}
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirming..."
variant="negative"
Expand All @@ -66,7 +68,27 @@ function Example() {
}
```

### Compact SlideButton
### Positive

Use the `positive` variant for affirmative confirmations.

```jsx
function Example() {
const [checked, setChecked] = useState(false);

return (
<SlideButton
checked={checked}
onChange={setChecked}
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirmed!"
variant="positive"
/>
);
}
```

## Compact

Use the `compact` prop to reduce the height, border-radius and padding of the button:

Expand All @@ -78,7 +100,6 @@ function Example() {
<SlideButton
checked={checked}
onChange={setChecked}
onSlideComplete={() => console.log('Completed')}
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirming..."
compact
Expand All @@ -87,9 +108,31 @@ function Example() {
}
```

### Auto Complete on Threshold
## Disabled

Use the `disabled` prop to prevent interaction. This works for both unchecked and checked states.

```jsx
function Example() {
return (
<VStack gap={2}>
<SlideButton
checked={false}
disabled
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirming..."
/>
<SlideButton checked disabled uncheckedLabel="Swipe to confirm" checkedLabel="Confirmed" />
</VStack>
);
}
```

## Auto Complete on Threshold

By default, the user must release the handle past the threshold to complete. Set `autoCompleteSlideOnThresholdMet` to automatically complete as soon as the threshold is reached, without requiring release.

You can set the button to automatically complete when the slide reaches the threshold:
You can also adjust the threshold via `checkThreshold` (a value from 0 to 1, defaulting to 0.7).

```jsx
function Example() {
Expand All @@ -99,7 +142,6 @@ function Example() {
<SlideButton
checked={checked}
onChange={setChecked}
onSlideComplete={() => console.log('Completed')}
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirming..."
autoCompleteSlideOnThresholdMet
Expand All @@ -108,9 +150,41 @@ function Example() {
}
```

### Custom Nodes on SlideButton
## Callback Lifecycle

SlideButton fires callbacks in a specific order during the slide gesture:

You can also use SlideButton with custom nodes.
1. `onSlideStart` -- when the gesture begins
2. `onChange` -- when the slide completes past the threshold (sets `checked` to `true`)
3. `onSlideComplete` -- immediately after `onChange`
4. `onSlideEnd` -- always fires last

If the user releases before the threshold, `onSlideCancel` fires instead, followed by `onSlideEnd`.

**Important:** Always use `onChange` to manage the `checked` state. The `checked` prop drives the component's `accessibilityLabel` (switching between `uncheckedLabel` and `checkedLabel`), so failing to update it means screen readers won't announce the state change. Use `onSlideComplete` only for supplementary side effects (e.g. analytics, haptic feedback) that don't affect accessible state.

```jsx
function Example() {
const [checked, setChecked] = useState(false);

return (
<SlideButton
checked={checked}
onChange={setChecked}
onSlideStart={() => console.log('Started')}
onSlideComplete={() => console.log('Completed')}
onSlideCancel={() => console.log('Cancelled')}
onSlideEnd={() => console.log('Ended')}
uncheckedLabel="Swipe to confirm"
checkedLabel="Confirming..."
/>
);
}
```

## Custom Nodes

Use `startUncheckedNode` and `endCheckedNode` to replace the default arrow icon and loading indicator on the handle.

<ThemedImage
sources={{
Expand All @@ -121,7 +195,7 @@ You can also use SlideButton with custom nodes.
borderRadius: 'var(--borderRadius-400)',
border: '1.5px solid var(--color-bgLine)',
}}
alt="Custom nodes on slide button"
alt="Slide button with custom bell icons on the handle"
/>

```jsx
Expand All @@ -132,19 +206,41 @@ function Example() {
<SlideButton
checked={checked}
onChange={setChecked}
onSlideComplete={() => console.log('Completed')}
uncheckedLabel="Swipe to enable notifications"
checkedLabel="Enabling..."
startUncheckedNode={<Icon color="fgInverse" name="bellCheck" size="m" />}
endCheckedNode={<Icon color="fgInverse" name="bellInactive" size="m" />}
startUncheckedNode={<Icon color="fgInverse" name="bell" size="m" />}
endCheckedNode={<Icon color="fgInverse" name="bellCheck" size="m" />}
/>
);
}
```

### Custom Background and Handle Components
## Labels as Nodes

The `uncheckedLabel` and `checkedLabel` props accept `ReactNode`, so you can pass custom styled text or other components. When using non-string labels, the component uses `accessibilityLabelledBy` to associate the handle with the container element, so ensure your label nodes contain meaningful text content.

You can customize the background and handle components of the SlideButton.
```jsx
function Example() {
const [checked, setChecked] = useState(false);

return (
<SlideButton
checked={checked}
onChange={setChecked}
uncheckedLabel={<Text font="label2">Swipe to confirm</Text>}
checkedLabel={
<Text color="fgInverse" font="label2">
Confirming...
</Text>
}
/>
);
}
```

## Custom Background and Handle Components

You can fully customize the background and handle by providing your own components via `SlideButtonBackgroundComponent` and `SlideButtonHandleComponent`. Your components receive typed props (`SlideButtonBackgroundProps` and `SlideButtonHandleProps`) including a `progress` spring value and the current `checked` state.

<ThemedImage
sources={{
Expand All @@ -155,7 +251,7 @@ You can customize the background and handle components of the SlideButton.
borderRadius: 'var(--borderRadius-400)',
border: '1.5px solid var(--color-bgLine)',
}}
alt="Custom background and handle components"
alt="Slide button with custom green/red background and handle"
/>

```jsx
Expand Down Expand Up @@ -208,3 +304,28 @@ function Example() {
);
}
```

## Accessibility

SlideButton has built-in accessibility support. The component automatically derives its `accessibilityLabel` from the `checked` state -- displaying `uncheckedLabel` when unchecked and `checkedLabel` when checked. It also registers an `activate` accessibility action so screen readers can trigger the slide without performing a gesture.

**Use `onChange` as your primary callback.** The `onChange` callback updates the `checked` prop, which controls the accessible label. Placing critical logic in `onSlideComplete` without updating `checked` via `onChange` will leave the accessible state stale, meaning screen readers won't announce the confirmation.

When providing a custom `SlideButtonHandleComponent`, always spread the incoming props to preserve the built-in `accessibilityActions` and `onAccessibilityAction` handlers, and set `accessibilityLabel` and `accessibilityRole="button"` on the handle element.

When using `ReactNode` labels instead of strings, the component uses `accessibilityLabelledBy` to link to the container element, so ensure your custom label nodes contain meaningful text.

```jsx
function Example() {
const [checked, setChecked] = useState(false);

return (
<SlideButton
checked={checked}
onChange={setChecked}
uncheckedLabel="Swipe to send payment"
checkedLabel="Sending payment..."
/>
);
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,10 @@
"url": "/components/inputs/Pressable/"
}
],
"dependencies": []
"dependencies": [
{
"name": "react-native-gesture-handler",
"version": "^2.16.2"
}
]
}
15 changes: 13 additions & 2 deletions packages/mobile/src/carousel/__figma__/Carousel.figma.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,20 @@ figma.connect(
Carousel,
'https://www.figma.com/design/k5CtyJccNQUGMI5bI4lJ2g/%E2%9C%A8-CDS-Components?node-id=48671-10433',
{
variant: { platform: 'mobile' },
imports: ["import { Carousel, CarouselItem } from '@coinbase/cds-mobile/carousel'"],
example: () => (
<Carousel>
props: {
title: figma.boolean('show header', {
true: figma.string('title'),
false: undefined,
}),
hidePagination: figma.boolean('show pagination', {
true: undefined,
false: true,
}),
},
example: ({ title, hidePagination }) => (
<Carousel hidePagination={hidePagination} paginationVariant="dot" title={title}>
<CarouselItem id="1">{/* Item content */}</CarouselItem>
<CarouselItem id="2">{/* Item content */}</CarouselItem>
<CarouselItem id="3">{/* Item content */}</CarouselItem>
Expand Down
Loading