{"_id":"@curious-cat-consulting/react-native-swipeable-list","name":"@curious-cat-consulting/react-native-swipeable-list","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@curious-cat-consulting/react-native-swipeable-list","version":"0.1.0","description":"iOS 26 Mail-style swipeable list row for React Native and Expo","author":{"name":"Curious Cat Consulting"},"license":"MIT","homepage":"https://github.com/curious-cat-consulting/react-native-swipeable-list#readme","repository":{"type":"git","url":"git+https://github.com/curious-cat-consulting/react-native-swipeable-list.git"},"bugs":{"url":"https://github.com/curious-cat-consulting/react-native-swipeable-list/issues"},"keywords":["react-native","expo","swipeable","swipe","swipe-to-delete","flashlist","list","gesture-handler","reanimated","ios"],"main":"src/index.ts","types":"src/index.ts","exports":{".":{"types":"./src/index.ts","default":"./src/index.ts"},"./package.json":"./package.json"},"sideEffects":false,"scripts":{"typecheck":"tsc --noEmit","test":"jest","test:watch":"jest --watch","lint":"eslint \"**/*.{js,jsx,ts,tsx}\" && prettier -c \"**/*.{js,jsx,ts,tsx,json}\"","format":"eslint \"**/*.{js,jsx,ts,tsx}\" --fix && prettier \"**/*.{js,jsx,ts,tsx,json}\" --write","pass":"yarn typecheck && yarn format && yarn lint && yarn test","prepublishOnly":"yarn typecheck && yarn lint && yarn test"},"peerDependencies":{"@expo/vector-icons":">=14.0.0","@shopify/flash-list":">=2.0.0","expo-haptics":">=14.0.0","expo-symbols":">=0.2.0","react":">=18.0.0","react-native":">=0.74.0","react-native-gesture-handler":">=3.0.0","react-native-reanimated":">=3.0.0","react-native-worklets":">=0.8.0"},"devDependencies":{"@babel/core":"^7.26.0","@expo/vector-icons":"^15.0.2","@react-native/jest-preset":"^0.85.0","@shopify/flash-list":"^2.3.2","@testing-library/react-native":"^14.0.1","@types/jest":"^30.0.0","@types/react":"~19.2.10","eslint":"^9.39.1","eslint-config-expo":"~56.0.4","eslint-config-prettier":"^10.1.8","expo":"~56.0.0","expo-haptics":"~56.0.3","expo-symbols":"~56.0.6","jest":"^29.7.0","jest-expo":"~56.0.0","prettier":"^3.8.3","react":"19.2.3","react-native":"0.85.3","react-native-gesture-handler":"~3.0.2","react-native-reanimated":"4.3.1","react-native-worklets":"0.8.3","react-test-renderer":"19.2.3","test-renderer":"^1.0.0","typescript":"~6.0.3"},"publishConfig":{"access":"public"},"gitHead":"2efc0ad6c6ca7cedee52ab9287d4c0cbdcd422a7","_id":"@curious-cat-consulting/react-native-swipeable-list@0.1.0","_nodeVersion":"26.3.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-lcAn4K5Iqnt48z4DdXntJ6Z+gkNdMO+SvOzeLHPJKoaAkp1EfDUkz71MRra/4VMZdmVarLcNB1ccY86wPIh84g==","shasum":"ebaa30410bacf43e02bc578ec0e066a79c5cac1f","tarball":"https://registry.npmjs.org/@curious-cat-consulting/react-native-swipeable-list/-/react-native-swipeable-list-0.1.0.tgz","fileCount":16,"unpackedSize":61755,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGYNH1gB2DHWwL7Tf/7mXlT/IvzNAFb1oXbwsua/obtCAiEAklIAKQMit4peJ3++J2akxWlCssI/CgIgC96I7alkJN8="}]},"_npmUser":{"name":"warmfire540","email":"patrickwmasters@gmail.com"},"directories":{},"maintainers":[{"name":"warmfire540","email":"patrickwmasters@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native-swipeable-list_0.1.0_1782681849206_0.30888323532286366"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-28T21:24:09.062Z","0.1.0":"2026-06-28T21:24:09.396Z","modified":"2026-06-28T21:24:09.648Z"},"maintainers":[{"name":"warmfire540","email":"patrickwmasters@gmail.com"}],"description":"iOS 26 Mail-style swipeable list row for React Native and Expo","homepage":"https://github.com/curious-cat-consulting/react-native-swipeable-list#readme","keywords":["react-native","expo","swipeable","swipe","swipe-to-delete","flashlist","list","gesture-handler","reanimated","ios"],"repository":{"type":"git","url":"git+https://github.com/curious-cat-consulting/react-native-swipeable-list.git"},"author":{"name":"Curious Cat Consulting"},"bugs":{"url":"https://github.com/curious-cat-consulting/react-native-swipeable-list/issues"},"license":"MIT","readme":"# react-native-swipeable-list\n\nA drop-in [`FlashList`](https://shopify.github.io/flash-list/) plus a coordinated\n[`SwipeableRow`](https://docs.swmansion.com/react-native-gesture-handler/docs/components/reanimated_swipeable/)\nthat gives you iOS Mail-style \"only one row open at a time\" behavior for free.\n\n## Install\n\n```bash\nnpx expo install @curious-cat-consulting/react-native-swipeable-list\n```\n\nThen add the peer dependencies your app doesn't already have:\n\n```bash\nnpx expo install @shopify/flash-list react-native-gesture-handler react-native-reanimated\n```\n\nThis library also expects `react`, `react-native`, and `react-native-worklets` at\nthe versions listed in `package.json`.\n\n## Quick start\n\nUse `SwipeableFlashList` like `FlashList`. It wraps each item in a `SwipeableRow`\nfor you — just pass `renderTrailingActions` (and/or `renderLeadingActions`):\n\n```tsx\nimport { Pressable, StyleSheet, Text, View } from 'react-native';\nimport { SwipeableFlashList } from '@curious-cat-consulting/react-native-swipeable-list';\n\ntype Item = { id: string; title: string };\n\nconst items: Item[] = [\n  { id: '1', title: 'Inbox' },\n  { id: '2', title: 'Sent' },\n];\n\nexport function MailList() {\n  return (\n    <SwipeableFlashList\n      data={items}\n      keyExtractor={(item) => item.id}\n      renderItem={({ item }) => (\n        <View style={styles.row}>\n          <Text>{item.title}</Text>\n        </View>\n      )}\n      renderTrailingActions={({ item }) => (\n        <Pressable style={styles.delete} onPress={() => console.log('delete', item.id)}>\n          <Text style={styles.deleteLabel}>Delete</Text>\n        </Pressable>\n      )}\n    />\n  );\n}\n\nconst styles = StyleSheet.create({\n  row: { padding: 16, backgroundColor: '#fff' },\n  delete: {\n    backgroundColor: '#ff3b30',\n    justifyContent: 'center',\n    paddingHorizontal: 24,\n  },\n  deleteLabel: { color: '#fff', fontWeight: '600' },\n});\n```\n\nThat's the whole integration: `SwipeableFlashList` wraps a `FlashList` in a\ncoordination provider and wraps each item in a `SwipeableRow` that registers\nwith that provider, so opening one row closes the others. The action renderers\nreceive the row's `renderItem` info first, so actions can vary per item.\n\nNeed finer control? Render `SwipeableRow` yourself inside `renderItem` instead\nof passing the action props.\n\n## Action buttons\n\nInstead of hand-rolling the revealed buttons, drop in `SwipeActionCircle` — a\n44pt circular button styled after iOS 26's Mail swipe actions, which scales and\nstaggers into place as the row opens (no animation wiring needed):\n\n```tsx\nimport { SwipeableFlashList, SwipeActionCircle } from '@curious-cat-consulting/react-native-swipeable-list';\n\n<SwipeableFlashList\n  data={items}\n  keyExtractor={(item) => item.id}\n  renderItem={({ item }) => <Row item={item} />}\n  renderTrailingActions={({ item }) => [\n    <SwipeActionCircle\n      icon=\"archivebox.fill\"\n      label=\"Archive\"\n      color=\"#34c759\"\n      onPress={() => archive(item.id)}\n    />,\n    <SwipeActionCircle icon=\"trash.fill\" label=\"Delete\" onPress={() => remove(item.id)} />,\n  ]}\n/>;\n```\n\n`icon` accepts an **SF Symbol name** (a string), a **platform map**\n(`{ ios: 'square.and.arrow.up', android: 'share' }` for cross-platform rendering\nvia [`expo-symbols`](https://docs.expo.dev/versions/latest/sdk/symbols/)), or a\n**render function** `({ tintColor, size }) => ReactNode` for a custom icon.\nPassing a render function means your app doesn't depend on `expo-symbols` at all:\n\n```tsx\nimport { Ionicons } from '@expo/vector-icons';\n\n<SwipeActionCircle\n  icon={({ tintColor, size }) => <Ionicons name=\"trash\" color={tintColor} size={size} />}\n  label=\"Delete\"\n  onPress={() => remove(item.id)}\n/>;\n```\n\nTapping a circle does what iOS does when you take a swipe action: a **light\nhaptic** fires, your `onPress` runs, and the **row closes**. Both extras are on by\ndefault and independently opt-out-able — `noHaptic` to stay silent, `noClose` to\nleave the row open (e.g. an in-place toggle). The haptic uses `expo-haptics`,\nalready a peer dependency.\n\nSee [`src/action/README.md`](src/action/README.md) for the full prop list.\n\n## Pull to commit (iOS full-swipe)\n\nDrag a row far enough past open and a confirming **haptic** fires — the iOS \"let\ngo now\" cue. Release there and the **edge action** (the outermost one) runs as if\ntapped; ease back under the line first and a lighter **reset haptic** says it\nwon't. It's on automatically for any row with actions — nothing to wire up — and\nthe commit point is tunable (`commitTokens.threshold`, default an 85%-of-open\noverdrag). See [`src/row/README.md`](src/row/README.md#commit-on-release-pull-to-commit).\n\n## What each piece does\n\n| Component            | Role                                                                                                                                                                                                        |\n| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `SwipeableFlashList` | Drop-in `FlashList`. Forwards every FlashList prop and its ref, auto-wraps each item in a `SwipeableRow`, and adds `allowMultipleOpen` plus `renderLeadingActions`/`renderTrailingActions`.                 |\n| `SwipeableRow`       | Drop-in `ReanimatedSwipeable`. Forwards every swipeable prop. Participates in list coordination when inside a `SwipeableFlashList`. Use directly only when you need finer control than the list props give. |\n\n`SwipeableFlashListProps<T>` is `FlashListProps<T>` plus:\n\n| Prop                    | Default | Description                                                              |\n| ----------------------- | ------- | ------------------------------------------------------------------------ |\n| `allowMultipleOpen`     | `false` | When `false`, opening a row closes any other open row.                   |\n| `renderLeadingActions`  | —       | Actions revealed by swiping a row from the leading edge (left in LTR).   |\n| `renderTrailingActions` | —       | Actions revealed by swiping a row from the trailing edge (right in LTR). |\n\n`SwipeableRowProps` mirrors gesture-handler's `SwipeableProps` — see their\n[ReanimatedSwipeable docs](https://docs.swmansion.com/react-native-gesture-handler/docs/components/reanimated_swipeable/)\nfor thresholds, callbacks, and the rest — except `renderLeftActions`/`renderRightActions`\nare renamed to `renderLeadingActions`/`renderTrailingActions`.\n\n## Imperative control\n\n`SwipeableFlashList`'s ref is a normal `FlashListRef<T>` — `scrollToIndex`, etc. work\nunchanged.\n\n`SwipeableRow`'s ref exposes a small handle for closing a row:\n\n```tsx\nimport { useRef } from 'react';\nimport { SwipeableRow, type SwipeableRowHandle } from '@curious-cat-consulting/react-native-swipeable-list';\n\nconst rowRef = useRef<SwipeableRowHandle>(null);\nrowRef.current?.close();\n```\n\n## Advanced: custom lists\n\nIf you aren't using `FlashList`, wrap your own list in `SwipeableListProvider` and\nrender `SwipeableRow`s inside it. See [`src/context/README.md`](src/context/README.md).\n\n`SwipeableRow` also works standalone outside any provider — it behaves exactly like\n`ReanimatedSwipeable` with no coordination.\n\n## Further reading\n\n- [`src/list/README.md`](src/list/README.md) — `SwipeableFlashList` details\n- [`src/row/README.md`](src/row/README.md) — `SwipeableRow` details\n- [`src/context/README.md`](src/context/README.md) — coordination layer\n- [`src/action/README.md`](src/action/README.md) — `SwipeActionCircle` and the action layer\n","readmeFilename":"README.md","_rev":"1-b06b04b31b8a6bab5384a2ff3a3317ec"}