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
11 changes: 11 additions & 0 deletions .changeset/soft-cards-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@react-native-motion-kit/swipe-deck': patch
---

Lower the minimum `visibleCardCount` to 2 while keeping the default at 3, allowing compact decks to render only the active card and the immediate next card.

```tsx
<SwipeDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={2}>
<SwipeDeck.Card>{({ item }) => <ProfileCard profile={item} />}</SwipeDeck.Card>
</SwipeDeck.Root>
```
17 changes: 14 additions & 3 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ Deck은 전체 data set을 한 번에 렌더링하지 않습니다.

`visibleCardCount`는 최대 예산입니다.

- `3`보다 작은 값은 데이터가 충분할 때 `3`으로 정규화됩니다.
- 기본값은 더 자연스러운 next-card promotion을 위해 `3`입니다.
- `2`보다 작은 값은 데이터가 충분할 때 `2`로 정규화됩니다.
- `visibleCardCount={2}`는 현재 카드와 바로 다음 카드만 렌더링합니다.
- 실제 mount 개수는 active index부터 남은 데이터 개수를 넘지 않습니다.
- 짝수 값은 그대로 최대 예산으로 유지되며, 홀수로 올림 처리하지 않습니다.

Expand Down Expand Up @@ -638,9 +640,17 @@ SwipeDeckMotion.tinder({

```tsx
function CompactDeck() {
return (
<SwipeDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={2}>
{/* minimum budget: current + immediate next */}
</SwipeDeck.Root>
);
}

function DefaultDeck() {
return (
<SwipeDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={3}>
{/* default/minimum budget */}
{/* default budget for smoother next-card promotion */}
</SwipeDeck.Root>
);
}
Expand All @@ -656,7 +666,8 @@ function DeepStackDeck() {

| 입력 | Mount되는 card |
| ---------------------------------------------- | -------------------------------------------------------- |
| `visibleCardCount={2}` | 데이터가 충분할 때 최대 `3`개 |
| `visibleCardCount={1}` | 데이터가 충분할 때 최대 `2`개 |
| `visibleCardCount={2}` | 데이터가 충분할 때 최대 `2`개 |
| 남은 데이터가 10개이고 `visibleCardCount={20}` | 최대 남은 10개 |
| 짝수 값 | 최대 예산으로 그대로 유지되며, 홀수로 올림 처리하지 않음 |

Expand Down
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ This keeps the active/incoming stack continuous without backfilling dismissed pr

`visibleCardCount` is a maximum budget:

- values below `3` normalize to `3` when enough data exists;
- the default is `3` for smoother next-card promotion;
- values below `2` normalize to `2` when enough data exists;
- `visibleCardCount={2}` renders only the current card and the immediate next card;
- the mounted count never exceeds the remaining data from the active index;
- even values are kept as-is and are not rounded up.

Expand Down Expand Up @@ -644,9 +646,17 @@ The default is `Easing.out(Easing.cubic)`.

```tsx
function CompactDeck() {
return (
<SwipeDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={2}>
{/* minimum budget: current + immediate next */}
</SwipeDeck.Root>
);
}

function DefaultDeck() {
return (
<SwipeDeck.Root data={profiles} getKey={(item) => item.id} visibleCardCount={3}>
{/* default/minimum budget */}
{/* default budget for smoother next-card promotion */}
</SwipeDeck.Root>
);
}
Expand All @@ -662,7 +672,8 @@ function DeepStackDeck() {

| Input | Mounted cards |
| ----------------------------------------------- | ------------------------------------------- |
| `visibleCardCount={2}` | Up to `3` cards when enough data exists. |
| `visibleCardCount={1}` | Up to `2` cards when enough data exists. |
| `visibleCardCount={2}` | Up to `2` cards when enough data exists. |
| `visibleCardCount={20}` with 10 remaining items | At most those 10 remaining items. |
| even values | Kept as the maximum budget; not rounded up. |

Expand Down
26 changes: 26 additions & 0 deletions src/__tests__/rendering.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ describe('getSwipeRenderItems', () => {
expect(getSwipeStackRenderItems(items).map((item) => item.index)).toEqual([77, 76, 75]);
});

it('supports a compact current-plus-next render window', () => {
const items = getSwipeRenderItems(profiles, 75, getProfileKey, 2);

expect(items).toHaveLength(2);
expect(items.map((item) => item.index)).toEqual([75, 76]);
expect(items.map((item) => item.role)).toEqual(['current', 'next']);
});

it('does not backfill dismissed previous cards at the last valid index', () => {
const items = getSwipeRenderItems(profiles, 149, getProfileKey);

Expand Down Expand Up @@ -66,6 +74,24 @@ describe('getSwipeRenderItems', () => {
]);
});

it('supports compact undo render info when only current and next are visible', () => {
const items = getSwipeUndoRenderItems({
data: profiles,
currentIndex: 2,
getKey: getProfileKey,
restoredIndex: 1,
visibleCardCount: 2,
});

expect(items.map((item) => item.index)).toEqual([1, 2]);
expect(items.map((item) => item.offset)).toEqual([0, 1]);
expect(items.map((item) => item.role)).toEqual(['current', 'next']);
expect(items.map((item) => item.transition)).toEqual([
undefined,
{ fromOffset: 0, toOffset: 1 },
]);
});

it('skips the restored index from the old stack when data reorder moves it after current', () => {
const items = getSwipeUndoRenderItems({
data: profiles,
Expand Down
6 changes: 4 additions & 2 deletions src/__tests__/windowing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe('getSwipeWindow', () => {
});

it('keeps the configured visible card count within the remaining data length', () => {
expect(getSwipeWindow(5, 0, 2)).toHaveLength(2);
expect(getSwipeWindow(5, 2, 3)).toHaveLength(3);
expect(getSwipeWindow(9, 4, 5)).toHaveLength(5);
expect(getSwipeWindow(10, 4, 20)).toHaveLength(6);
Expand All @@ -72,9 +73,10 @@ describe('getSwipeWindow', () => {
});

describe('normalizeVisibleCardCount', () => {
it('defaults and clamps to the three-card minimum', () => {
it('defaults to three cards and clamps to the two-card minimum', () => {
expect(normalizeVisibleCardCount()).toBe(3);
expect(normalizeVisibleCardCount(2)).toBe(3);
expect(normalizeVisibleCardCount(1)).toBe(2);
expect(normalizeVisibleCardCount(2)).toBe(2);
});

it('keeps larger finite integer budgets', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/core/windowing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type SwipeWindowDescriptor = {
};

const DEFAULT_VISIBLE_CARD_COUNT = 3;
const MIN_VISIBLE_CARD_COUNT = 3;
const MIN_VISIBLE_CARD_COUNT = 2;

export function clampActiveIndex(dataLength: number, activeIndex: number): number {
if (dataLength <= 0) {
Expand Down
11 changes: 6 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,13 @@ export type SwipeDeckProps<T> = {
/**
* Maximum number of cards kept mounted from the active card forward.
*
* Keep this as small as your UI allows. The default/minimum budget is `3`, which covers the
* active card plus two forward buffered cards for smooth next-card promotion without rendering
* the full data set. Increase it only when your design visibly exposes a deeper stack or
* intentionally needs more cards pre-mounted.
* Keep this as small as your UI allows. The default budget is `3`, which covers the active card
* plus two forward buffered cards for smooth next-card promotion without rendering the full data
* set. The minimum budget is `2`, which renders only the active card and the immediate next card
* when enough forward data is available. Increase it only when your design visibly exposes a
* deeper stack or intentionally needs more cards pre-mounted.
*
* Values below `3` are normalized to `3` when enough forward data is available. The actual
* Values below `2` are normalized to `2` when enough forward data is available. The actual
* mounted count never exceeds the remaining item count from the active index.
*
* @default 3
Expand Down