Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 70 additions & 34 deletions README.ko.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
# @react-native-motion-kit/swipe-deck
# React Native Swipe Deck

[English](README.md)
> [English](./README.md) | 한국어

<div align="center">
<img src="./assets/logo.png" width="300px" alt="React Native Motion Kit logo" />
<img src="./assets/logo.png" width="300px" alt="React Native Motion Kit 로고" />
</div>

Reanimated, Worklets, Gesture Handler 기반의 고성능 React Native용 Tinder 스타일
swipe deck / swipe card 라이브러리입니다.
## 개요

## 특징
React Native에서 Tinder 스타일 card stack을 만들 때 gesture state, programmatic action, animation state, commit event를 매 화면마다 직접 연결하고 있나요?

- **작은 render window**: 현재 카드와 bounded forward stack만 mount합니다.
- **Item-keyed rendering**: promoted card가 React Native view identity를 유지합니다.
- **Typed compound API**: `createSwipeDeck<T>()`로 typed deck family를 만듭니다.
- **Deck hooks**: state, action, event, animated overlay를 같은 factory에서 다룹니다.
- **Motion recipes**: gesture motion, programmatic action, undo restore를 따로 조정합니다.
`@react-native-motion-kit/swipe-deck`는 React Native용 고성능 swipe deck 라이브러리입니다.
Reanimated, Worklets, Gesture Handler를 기반으로 card stack, like/pass button, progress 기반 overlay, undo flow, 여러 독립 deck instance를 typed compound API로 다룰 수 있습니다.

## 설치
### 주요 기능

- 🤌 **Gesture 중심 Deck UX** - Tinder 스타일 card stack에 맞춰 drag, flick, threshold, direction control을 조정합니다
- 🏎️ **고성능 애니메이션** - React Native Reanimated와 Worklets 기반으로 UI thread에서 부드러운 card motion을 만듭니다
- 🪟 **Bounded Render Window** - 전체 데이터가 아니라 active card와 작은 forward stack만 mount합니다
- 🧬 **Item-Stable Promotion** - 안정적인 item key로 promoted card가 React Native view identity를 유지합니다
- 🧠 **Typed Compound API** - Root, Card, hook, action, event를 하나의 typed deck family로 묶습니다
- 🎛️ **외부 제어 API** - button이나 다른 UI에서 swipeLeft, swipeRight, undo를 programmatic하게 실행합니다
- 🎨 **Motion Recipes** - gesture motion, programmatic action, undo restore를 각각 독립적으로 조정합니다
- 🧩 **Multi-Instance Management** - 안정적인 factory-scoped id로 여러 deck root를 독립적으로 관리합니다
- ↩️ **Undo Support** - action-safe undo motion과 LIFO history로 back-swipe UX를 opt-in으로 제공합니다
- 🪄 **쉬운 API** - Root와 Card로 시작하고 control이나 event가 필요할 때만 hook을 추가합니다

## 빠른 시작

### 📚 문서

전체 문서는 여기에서 확인할 수 있습니다: <https://react-native-swipe-deck.pages.dev/ko/>

### 예제 & 데모

- [📁 예제 프로젝트](https://github.com/react-native-motion-kit/react-native-swipe-deck/tree/main/example) - 다양한 use case를 담은 실제 구현 코드입니다

### 🤖 AI

- [llms.txt](https://react-native-swipe-deck.pages.dev/ko/llms.txt): 모든 문서 페이지의 제목, 링크, 간단한 설명을 담은 structured index 파일입니다.
- [llms-full.txt](https://react-native-swipe-deck.pages.dev/ko/llms-full.txt): 모든 문서 페이지의 전체 내용을 하나로 합친 full-content 파일입니다.

### 설치

```sh
npm install @react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets
Expand All @@ -26,15 +50,16 @@ npm install @react-native-motion-kit/swipe-deck react-native-gesture-handler rea
사용 중인 React Native 또는 Expo 버전에 맞춰 Reanimated/Worklets 설정을 완료하세요.
Babel 설정에서는 `react-native-worklets/plugin`을 마지막 Babel plugin으로 추가해야 합니다.

## 빠른 시작
### 기본 사용법

```tsx
import { Text, View } from 'react-native';
import { Pressable, Text, View } from 'react-native';
import { createSwipeDeck, SwipeDeckMotion } from '@react-native-motion-kit/swipe-deck';

type Profile = {
id: string;
name: string;
bio: string;
};

const ProfileDeck = createSwipeDeck<Profile>({
Expand All @@ -45,38 +70,49 @@ function ProfileCard({ profile }: { profile: Profile }) {
return (
<View>
<Text>{profile.name}</Text>
<Text>{profile.bio}</Text>
</View>
);
}

export function ProfileDeckScreen({ profiles }: { profiles: Profile[] }) {
function ProfileDeckControls() {
const { activeIndex, count, canSwipe } = ProfileDeck.useDeckState();
const { swipeLeft, swipeRight } = ProfileDeck.useDeckActions();
const current = activeIndex >= 0 ? activeIndex + 1 : 0;

return (
<ProfileDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={3}>
<ProfileDeck.Card>{({ item }) => <ProfileCard profile={item} />}</ProfileDeck.Card>
</ProfileDeck.Root>
<View>
<Text>{`${current} / ${count}`}</Text>
<Pressable disabled={!canSwipe} onPress={swipeLeft}>
<Text>Nope</Text>
</Pressable>
<Pressable disabled={!canSwipe} onPress={swipeRight}>
<Text>Like</Text>
</Pressable>
</View>
);
}
```

Control, animated overlay, commit event가 필요하다면 같은 factory Root 주변에서
`ProfileDeck.useDeckState()`, `ProfileDeck.useDeckActions()`,
`ProfileDeck.useDeckInteraction()`, `ProfileDeck.useDeckEvent()`,
`ProfileDeck.useDeckEventListener()`를 사용하세요.

## 문서
export function ProfileDeckScreen({ profiles }: { profiles: Profile[] }) {
return (
<View>
<ProfileDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={3}>
<ProfileDeck.Card>{({ item }) => <ProfileCard profile={item} />}</ProfileDeck.Card>
</ProfileDeck.Root>

공개 사이트를 준비하는 동안 전체 가이드는 이 저장소의 `docs/` 아래에서 관리합니다.
<ProfileDeckControls />
</View>
);
}
```

- [개요](docs/docs/1.x/ko/guide/getting-started/overview.mdx)
- [빠른 시작](docs/docs/1.x/ko/guide/getting-started/quick-start.mdx)
- [API 레퍼런스](docs/docs/1.x/ko/guide/usage/api-reference.mdx)
- [AI 사용 가이드](docs/docs/1.x/ko/guide/getting-started/ai.mdx)
Control, animated overlay, commit event가 필요하다면 `Root`와 같은 `ProfileDeck` factory에서 제공하는 hook을 사용하세요:
`ProfileDeck.useDeckState()`, `ProfileDeck.useDeckActions()`, `ProfileDeck.useDeckInteraction()`, `ProfileDeck.useDeckEvent()`, `ProfileDeck.useDeckEventListener()`.

AI가 읽기 좋은 문서는 docs build 시 생성됩니다.
## 기여

- [llms.txt](docs/doc_build/ko/llms.txt)
- [llms-full.txt](docs/doc_build/ko/llms-full.txt)
프로젝트 기여 방법과 개발 환경 설정은 [Contributing Guide](CONTRIBUTING.md)를 참고하세요.

## 라이선스

MIT
[MIT](./LICENSE)
101 changes: 67 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,46 @@
# @react-native-motion-kit/swipe-deck
# React Native Swipe Deck

[한국어](README.ko.md)
> English | [한국어](./README.ko.md)

<div align="center">
<img src="./assets/logo.png" width="300px" alt="React Native Motion Kit logo" />
</div>

High-performance Tinder-style swipe deck and swipe cards for React Native,
powered by Reanimated, Worklets, and Gesture Handler.
## Overview

## Highlights
Need Tinder-style cards without hand-wiring gesture state, programmatic actions, animation state, and committed events on every screen?

- **Small render window**: mounts only the active card and a bounded forward stack.
- **Item-keyed rendering**: promoted cards keep their React Native view identity.
- **Typed compound API**: create one typed deck family with `createSwipeDeck<T>()`.
- **Deck hooks**: read state, run actions, subscribe to events, and drive animated overlays.
- **Motion recipes**: tune gesture motion, programmatic actions, and undo restores separately.
`@react-native-motion-kit/swipe-deck` is a high-performance swipe deck library for React Native. It is built on Reanimated, Worklets, and Gesture Handler, with a typed compound API for card stacks, like/pass buttons, progress-driven overlays, undo flows, and multiple independent deck instances.

## Installation
### Key Features

- 🤌 **Gesture-First Deck UX** - Drag, flick, threshold, and direction controls tuned for Tinder-style card stacks
- 🏎️ **High-Performance Animations** - Smooth UI-thread card motion powered by React Native Reanimated and Worklets
- 🪟 **Bounded Render Window** - Mount only the active card and a small forward stack instead of the whole data set
- 🧬 **Item-Stable Promotion** - Stable item keys let promoted cards keep their React Native view identity
- 🧠 **Typed Compound API** - Create one typed deck family with Root, Card, hooks, actions, and events
- 🎛️ **External Control API** - Trigger swipeLeft, swipeRight, and undo from buttons or other UI components
- 🎨 **Motion Recipes** - Tune gesture motion, programmatic actions, and undo restores independently
- 🧩 **Multi-Instance Management** - Manage multiple deck roots independently with stable factory-scoped IDs
- ↩️ **Undo Support** - Opt into back-swipe UX with action-safe undo motion and LIFO history
- 🪄 **Easy-to-Use API** - Start with Root and Card, then add hooks only when controls or events need them

## Quick Start

### 📚 Documentation

Full documentation is available at: <https://react-native-swipe-deck.pages.dev>

### Examples & Demo

- [📁 Example Project](https://github.com/react-native-motion-kit/react-native-swipe-deck/tree/main/example) - Real implementation code with various use cases

### 🤖 AI

- [llms.txt](https://react-native-swipe-deck.pages.dev/llms.txt): A structured index file containing the titles, links, and brief descriptions of all documentation pages.
- [llms-full.txt](https://react-native-swipe-deck.pages.dev/llms-full.txt): A full-content file that concatenates the complete content of every documentation page into a single file.

### Installation

```sh
npm install @react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets
Expand All @@ -26,15 +49,16 @@ npm install @react-native-motion-kit/swipe-deck react-native-gesture-handler rea
Then follow the Reanimated/Worklets setup for your React Native or Expo version.
Make sure `react-native-worklets/plugin` is the last Babel plugin.

## Quick Start
### Basic Usage

```tsx
import { Text, View } from 'react-native';
import { Pressable, Text, View } from 'react-native';
import { createSwipeDeck, SwipeDeckMotion } from '@react-native-motion-kit/swipe-deck';

type Profile = {
id: string;
name: string;
bio: string;
};

const ProfileDeck = createSwipeDeck<Profile>({
Expand All @@ -45,39 +69,48 @@ function ProfileCard({ profile }: { profile: Profile }) {
return (
<View>
<Text>{profile.name}</Text>
<Text>{profile.bio}</Text>
</View>
);
}

export function ProfileDeckScreen({ profiles }: { profiles: Profile[] }) {
function ProfileDeckControls() {
const { activeIndex, count, canSwipe } = ProfileDeck.useDeckState();
const { swipeLeft, swipeRight } = ProfileDeck.useDeckActions();
const current = activeIndex >= 0 ? activeIndex + 1 : 0;

return (
<ProfileDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={3}>
<ProfileDeck.Card>{({ item }) => <ProfileCard profile={item} />}</ProfileDeck.Card>
</ProfileDeck.Root>
<View>
<Text>{`${current} / ${count}`}</Text>
<Pressable disabled={!canSwipe} onPress={swipeLeft}>
<Text>Nope</Text>
</Pressable>
<Pressable disabled={!canSwipe} onPress={swipeRight}>
<Text>Like</Text>
</Pressable>
</View>
);
}
```

Use `ProfileDeck.useDeckState()`, `ProfileDeck.useDeckActions()`,
`ProfileDeck.useDeckInteraction()`, `ProfileDeck.useDeckEvent()`, and
`ProfileDeck.useDeckEventListener()` around the same factory Root when you need
controls, animated overlays, or committed model events.

## Docs
export function ProfileDeckScreen({ profiles }: { profiles: Profile[] }) {
return (
<View>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Give the README screen wrapper a flex height

When this README example is pasted as a normal screen, the new outer View has no flex: 1 or fixed height, so the deck's own flex: 1 container can measure with height 0; getSwipeDeckState requires both layout dimensions to be positive before canSwipe becomes true, leaving the sample buttons disabled and gestures unable to commit. Add style={{ flex: 1 }} here or otherwise give the deck a definite height.

Useful? React with 👍 / 👎.

<ProfileDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={3}>
<ProfileDeck.Card>{({ item }) => <ProfileCard profile={item} />}</ProfileDeck.Card>
</ProfileDeck.Root>

Full guides are maintained in this repository under `docs/` while the public
site is being prepared.
<ProfileDeckControls />
</View>
);
}
```

- [Overview](docs/docs/1.x/en/guide/getting-started/overview.mdx)
- [Quick Start](docs/docs/1.x/en/guide/getting-started/quick-start.mdx)
- [API Reference](docs/docs/1.x/en/guide/usage/api-reference.mdx)
- [AI Usage Guide](docs/docs/1.x/en/guide/getting-started/ai.mdx)
Use hooks from the same `ProfileDeck` factory as the `Root` when you need controls, animated overlays, or committed model events: `ProfileDeck.useDeckState()`, `ProfileDeck.useDeckActions()`, `ProfileDeck.useDeckInteraction()`, `ProfileDeck.useDeckEvent()`, and `ProfileDeck.useDeckEventListener()`.

AI-readable docs are generated during the docs build:
## Contributing

- [llms.txt](docs/doc_build/llms.txt)
- [llms-full.txt](docs/doc_build/llms-full.txt)
For details on how to contribute to the project and set up the development environment, please refer to the [Contributing Guide](CONTRIBUTING.md).

## License

MIT
[MIT](./LICENSE)
8 changes: 4 additions & 4 deletions docs/docs/1.x/en/guide/getting-started/ai.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ This site publishes Markdown files for AI tools during `rspress build`.
- [`/llms-full.txt`](/llms-full.txt): full English documentation in one
Markdown file. Use this when the agent has enough context budget and should
understand the whole API surface.
- [`/ko/llms.txt`](/ko/llms.txt) and
[`/ko/llms-full.txt`](/ko/llms-full.txt): Korean equivalents.

Before the public docs URL is available, build locally and read the generated
files from `docs/doc_build/`.
Public URLs:

- [`https://react-native-swipe-deck.pages.dev/llms.txt`](https://react-native-swipe-deck.pages.dev/llms.txt)
- [`https://react-native-swipe-deck.pages.dev/llms-full.txt`](https://react-native-swipe-deck.pages.dev/llms-full.txt)

## Recommended Prompt

Expand Down
24 changes: 11 additions & 13 deletions docs/docs/1.x/en/guide/getting-started/installation.mdx
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
# Installation
import { PackageManagerTabs } from '@rspress/core/theme';

Install the package and its React Native gesture/animation peer dependencies:
# Installation

```sh
npm install @react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets
```
Install the package and its required React Native gesture/animation peer dependencies:

You can use your package manager of choice. For Yarn:

```sh
yarn add @react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets
```
<PackageManagerTabs command="@react-native-motion-kit/swipe-deck react-native-gesture-handler react-native-reanimated react-native-worklets" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include the install verb in PackageManagerTabs

Rspress' PackageManagerTabs string form expects the command tail, e.g. official docs use command="install -D @rspress/core" and then rewrite install to add for yarn/pnpm/bun. Passing only package names here renders invalid install tabs such as npm @react-native-motion-kit/swipe-deck ... instead of npm install ...; the same pattern in docs/theme/index.tsx makes the homepage install command invalid too. Please include install in the string or use an explicit per-manager command object.

Useful? React with 👍 / 👎.


## Minimum Versions

Expand All @@ -24,9 +18,13 @@ yarn add @react-native-motion-kit/swipe-deck react-native-gesture-handler react-

## React Native Setup

Follow the Reanimated and Worklets setup instructions for your React Native or
Expo version. In Babel config, make sure `react-native-worklets/plugin` is the
last Babel plugin.
Follow the official setup guides for your React Native or Expo version:

- [React Native Reanimated getting started](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/)
- [React Native Gesture Handler installation](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation)

In Babel config, make sure `react-native-worklets/plugin` is the last Babel
plugin.

```js title="babel.config.js"
module.exports = {
Expand Down
5 changes: 5 additions & 0 deletions docs/docs/1.x/en/guide/getting-started/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
Create one typed deck family for your item type. `Root`, `Card`, hooks, actions,
and events from that factory share the same registry namespace.

## Examples & Demo

[📁 Example Project](https://github.com/react-native-motion-kit/react-native-swipe-deck/tree/main/example) -
Real implementation code with various use cases.

```tsx
import { Text, View } from 'react-native';
import { createSwipeDeck, SwipeDeckMotion } from '@react-native-motion-kit/swipe-deck';
Expand Down
8 changes: 6 additions & 2 deletions docs/docs/1.x/ko/guide/getting-started/ai.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ AI assistant에게 코드를 생성하게 할 때 아래 규칙을 함께 전달
파일입니다. Agent의 context budget이 충분하고 전체 API를 이해해야 할 때 사용하세요.
- [`/llms.txt`](/llms.txt)와 [`/llms-full.txt`](/llms-full.txt): English equivalents.

공개 문서 URL이 생기기 전에는 로컬에서 build한 뒤 `docs/doc_build/` 아래의 파일을
사용하세요.
공개 URL:

- [`https://react-native-swipe-deck.pages.dev/ko/llms.txt`](https://react-native-swipe-deck.pages.dev/ko/llms.txt)
- [`https://react-native-swipe-deck.pages.dev/ko/llms-full.txt`](https://react-native-swipe-deck.pages.dev/ko/llms-full.txt)
- [`https://react-native-swipe-deck.pages.dev/llms.txt`](https://react-native-swipe-deck.pages.dev/llms.txt)
- [`https://react-native-swipe-deck.pages.dev/llms-full.txt`](https://react-native-swipe-deck.pages.dev/llms-full.txt)

## 추천 프롬프트

Expand Down
Loading
Loading