diff --git a/.changeset/soft-cards-wave.md b/.changeset/soft-cards-wave.md
new file mode 100644
index 0000000..d7ff003
--- /dev/null
+++ b/.changeset/soft-cards-wave.md
@@ -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
+ item.id} visibleCardCount={2}>
+ {({ item }) => }
+
+```
diff --git a/README.ko.md b/README.ko.md
index aefd39d..614643e 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -103,7 +103,9 @@ Deck은 전체 data set을 한 번에 렌더링하지 않습니다.
`visibleCardCount`는 최대 예산입니다.
-- `3`보다 작은 값은 데이터가 충분할 때 `3`으로 정규화됩니다.
+- 기본값은 더 자연스러운 next-card promotion을 위해 `3`입니다.
+- `2`보다 작은 값은 데이터가 충분할 때 `2`로 정규화됩니다.
+- `visibleCardCount={2}`는 현재 카드와 바로 다음 카드만 렌더링합니다.
- 실제 mount 개수는 active index부터 남은 데이터 개수를 넘지 않습니다.
- 짝수 값은 그대로 최대 예산으로 유지되며, 홀수로 올림 처리하지 않습니다.
@@ -638,9 +640,17 @@ SwipeDeckMotion.tinder({
```tsx
function CompactDeck() {
+ return (
+ item.id} visibleCardCount={2}>
+ {/* minimum budget: current + immediate next */}
+
+ );
+}
+
+function DefaultDeck() {
return (
item.id} visibleCardCount={3}>
- {/* default/minimum budget */}
+ {/* default budget for smoother next-card promotion */}
);
}
@@ -656,7 +666,8 @@ function DeepStackDeck() {
| 입력 | Mount되는 card |
| ---------------------------------------------- | -------------------------------------------------------- |
-| `visibleCardCount={2}` | 데이터가 충분할 때 최대 `3`개 |
+| `visibleCardCount={1}` | 데이터가 충분할 때 최대 `2`개 |
+| `visibleCardCount={2}` | 데이터가 충분할 때 최대 `2`개 |
| 남은 데이터가 10개이고 `visibleCardCount={20}` | 최대 남은 10개 |
| 짝수 값 | 최대 예산으로 그대로 유지되며, 홀수로 올림 처리하지 않음 |
diff --git a/README.md b/README.md
index b279711..cc7774b 100644
--- a/README.md
+++ b/README.md
@@ -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.
@@ -644,9 +646,17 @@ The default is `Easing.out(Easing.cubic)`.
```tsx
function CompactDeck() {
+ return (
+ item.id} visibleCardCount={2}>
+ {/* minimum budget: current + immediate next */}
+
+ );
+}
+
+function DefaultDeck() {
return (
item.id} visibleCardCount={3}>
- {/* default/minimum budget */}
+ {/* default budget for smoother next-card promotion */}
);
}
@@ -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. |
diff --git a/src/__tests__/rendering.test.ts b/src/__tests__/rendering.test.ts
index 1c3fa88..15a3378 100644
--- a/src/__tests__/rendering.test.ts
+++ b/src/__tests__/rendering.test.ts
@@ -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);
@@ -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,
diff --git a/src/__tests__/windowing.test.ts b/src/__tests__/windowing.test.ts
index 8b71cc9..e509d27 100644
--- a/src/__tests__/windowing.test.ts
+++ b/src/__tests__/windowing.test.ts
@@ -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);
@@ -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', () => {
diff --git a/src/core/windowing.ts b/src/core/windowing.ts
index 1fba055..e6671de 100644
--- a/src/core/windowing.ts
+++ b/src/core/windowing.ts
@@ -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) {
diff --git a/src/types.ts b/src/types.ts
index 5499031..905be6a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -438,12 +438,13 @@ export type SwipeDeckProps = {
/**
* 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