@forthtilliath/react-native-kit 0.15.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +103 -3
  2. package/dist/components/feedback/ErrorFallback.d.ts +63 -0
  3. package/dist/components/feedback/ErrorFallback.d.ts.map +1 -0
  4. package/dist/components/feedback/ErrorFallback.js +86 -0
  5. package/dist/components/feedback/index.d.ts +2 -0
  6. package/dist/components/feedback/index.d.ts.map +1 -0
  7. package/dist/components/feedback/index.js +1 -0
  8. package/dist/components/index.d.ts +2 -0
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +2 -0
  11. package/dist/components/list/AddButton.d.ts +27 -0
  12. package/dist/components/list/AddButton.d.ts.map +1 -0
  13. package/dist/components/list/AddButton.js +29 -0
  14. package/dist/components/list/DragHandle.d.ts +35 -0
  15. package/dist/components/list/DragHandle.d.ts.map +1 -0
  16. package/dist/components/list/DragHandle.js +31 -0
  17. package/dist/components/list/SwipeableRow.d.ts +18 -4
  18. package/dist/components/list/SwipeableRow.d.ts.map +1 -1
  19. package/dist/components/list/SwipeableRow.js +40 -13
  20. package/dist/components/list/index.d.ts +2 -0
  21. package/dist/components/list/index.d.ts.map +1 -1
  22. package/dist/components/list/index.js +2 -0
  23. package/dist/components/overlay/BottomSheet.d.ts +45 -0
  24. package/dist/components/overlay/BottomSheet.d.ts.map +1 -0
  25. package/dist/components/overlay/BottomSheet.js +48 -0
  26. package/dist/components/overlay/index.d.ts +2 -0
  27. package/dist/components/overlay/index.d.ts.map +1 -0
  28. package/dist/components/overlay/index.js +1 -0
  29. package/dist/components/picker/CheckboxGroup.d.ts +47 -0
  30. package/dist/components/picker/CheckboxGroup.d.ts.map +1 -0
  31. package/dist/components/picker/CheckboxGroup.js +50 -0
  32. package/dist/components/picker/MonthDayGrid.d.ts +58 -0
  33. package/dist/components/picker/MonthDayGrid.d.ts.map +1 -0
  34. package/dist/components/picker/MonthDayGrid.js +73 -0
  35. package/dist/components/picker/OptionsModal.d.ts +45 -0
  36. package/dist/components/picker/OptionsModal.d.ts.map +1 -0
  37. package/dist/components/picker/OptionsModal.js +45 -0
  38. package/dist/components/picker/QuantityStepper.d.ts +63 -0
  39. package/dist/components/picker/QuantityStepper.d.ts.map +1 -0
  40. package/dist/components/picker/QuantityStepper.js +101 -0
  41. package/dist/components/picker/SegmentedToggle.d.ts +52 -0
  42. package/dist/components/picker/SegmentedToggle.d.ts.map +1 -0
  43. package/dist/components/picker/SegmentedToggle.js +48 -0
  44. package/dist/components/picker/SelectField.d.ts +45 -0
  45. package/dist/components/picker/SelectField.d.ts.map +1 -0
  46. package/dist/components/picker/SelectField.js +54 -0
  47. package/dist/components/picker/index.d.ts +6 -0
  48. package/dist/components/picker/index.d.ts.map +1 -1
  49. package/dist/components/picker/index.js +6 -0
  50. package/dist/components/settings/SettingsCard.d.ts +63 -0
  51. package/dist/components/settings/SettingsCard.d.ts.map +1 -0
  52. package/dist/components/settings/SettingsCard.js +68 -0
  53. package/dist/components/settings/SwitchRow.d.ts +48 -0
  54. package/dist/components/settings/SwitchRow.d.ts.map +1 -0
  55. package/dist/components/settings/SwitchRow.js +44 -0
  56. package/dist/components/settings/index.d.ts +2 -0
  57. package/dist/components/settings/index.d.ts.map +1 -1
  58. package/dist/components/settings/index.js +2 -0
  59. package/dist/components/theme/ThemePreferenceProvider.d.ts +39 -0
  60. package/dist/components/theme/ThemePreferenceProvider.d.ts.map +1 -0
  61. package/dist/components/theme/ThemePreferenceProvider.js +53 -0
  62. package/dist/components/theme/index.d.ts +2 -0
  63. package/dist/components/theme/index.d.ts.map +1 -1
  64. package/dist/components/theme/index.js +2 -0
  65. package/dist/components/theme/themePreferenceContext.d.ts +20 -0
  66. package/dist/components/theme/themePreferenceContext.d.ts.map +1 -0
  67. package/dist/components/theme/themePreferenceContext.js +21 -0
  68. package/dist/index.d.ts +20 -0
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +20 -0
  71. package/dist/utils/calendar/index.d.ts +3 -0
  72. package/dist/utils/calendar/index.d.ts.map +1 -0
  73. package/dist/utils/calendar/index.js +2 -0
  74. package/dist/utils/calendar/syncDedicatedCalendar.d.ts +37 -0
  75. package/dist/utils/calendar/syncDedicatedCalendar.d.ts.map +1 -0
  76. package/dist/utils/calendar/syncDedicatedCalendar.js +42 -0
  77. package/dist/utils/calendar/toCalendarEventDetails.d.ts +32 -0
  78. package/dist/utils/calendar/toCalendarEventDetails.d.ts.map +1 -0
  79. package/dist/utils/calendar/toCalendarEventDetails.js +47 -0
  80. package/dist/utils/files/appFiles.d.ts +61 -0
  81. package/dist/utils/files/appFiles.d.ts.map +1 -0
  82. package/dist/utils/files/appFiles.js +126 -0
  83. package/dist/utils/files/imageThumbnail.d.ts +22 -0
  84. package/dist/utils/files/imageThumbnail.d.ts.map +1 -0
  85. package/dist/utils/files/imageThumbnail.js +28 -0
  86. package/dist/utils/files/index.d.ts +3 -0
  87. package/dist/utils/files/index.d.ts.map +1 -0
  88. package/dist/utils/files/index.js +2 -0
  89. package/dist/utils/helpers/haptics.d.ts +9 -0
  90. package/dist/utils/helpers/haptics.d.ts.map +1 -0
  91. package/dist/utils/helpers/haptics.js +28 -0
  92. package/dist/utils/helpers/index.d.ts +5 -0
  93. package/dist/utils/helpers/index.d.ts.map +1 -1
  94. package/dist/utils/helpers/index.js +5 -0
  95. package/dist/utils/helpers/isNativeModuleAvailable.d.ts +12 -0
  96. package/dist/utils/helpers/isNativeModuleAvailable.d.ts.map +1 -0
  97. package/dist/utils/helpers/isNativeModuleAvailable.js +15 -0
  98. package/dist/utils/helpers/shareFile.d.ts +23 -0
  99. package/dist/utils/helpers/shareFile.d.ts.map +1 -0
  100. package/dist/utils/helpers/shareFile.js +26 -0
  101. package/dist/utils/helpers/shareTextFile.d.ts +16 -0
  102. package/dist/utils/helpers/shareTextFile.d.ts.map +1 -0
  103. package/dist/utils/helpers/shareTextFile.js +23 -0
  104. package/dist/utils/helpers/shareViewAsImage.d.ts +21 -0
  105. package/dist/utils/helpers/shareViewAsImage.d.ts.map +1 -0
  106. package/dist/utils/helpers/shareViewAsImage.js +32 -0
  107. package/dist/utils/index.d.ts +2 -0
  108. package/dist/utils/index.d.ts.map +1 -1
  109. package/dist/utils/index.js +2 -0
  110. package/package.json +58 -7
package/README.md CHANGED
@@ -18,7 +18,7 @@ Or, from within this monorepo, as a workspace dependency:
18
18
  }
19
19
  ```
20
20
 
21
- `react` and `react-native` are peer dependencies — install them in the consuming app if not already present. `@expo/vector-icons`, `expo-image-picker`, `expo-speech-recognition`, `react-native-gesture-handler` and `react-native-reanimated` are also peer dependencies, needed only if you import the components that use them (`Thumbnail`/`PickerModal`/`SwipeableRow`, `PhotoPicker`, `VoiceSearchButton`/`PickerModal`, `SwipeableRow`, `SwipeableRow`, respectively).
21
+ `react` and `react-native` are the only required peer dependencies — install them in the consuming app if not already present. Every other peer dependency is **optional**, needed only if you import the components that use them: `@expo/vector-icons` (`Thumbnail`, `PickerModal`, `SwipeableRow`, the settings screens…), `expo-image-picker` (`PhotoPicker`), `expo-speech-recognition` (`VoiceSearchButton`/`PickerModal`), `react-native-gesture-handler` (`DragHandle`, `SwipeableRow`), `react-native-reanimated` (`SwipeableRow`) and `@forthtilliath/expo-release-updates-ui` (`UpdateSettingsScreen`). Package managers no longer install them for you, so a native module (and the Android permissions it declares, e.g. `CAMERA` for `expo-image-picker`) only ends up in your APK if you add it yourself.
22
22
 
23
23
  ## Usage
24
24
 
@@ -30,7 +30,7 @@ import { PhotoPicker } from "@forthtilliath/react-native-kit/components/picker/P
30
30
 
31
31
  This is the recommended way to import: Metro (React Native's bundler) doesn't reliably tree-shake, so pulling from a single deep-import path keeps peer dependencies you don't use (`expo-image-picker`, `expo-speech-recognition`, `react-native-gesture-handler`...) out of your bundle entirely, rather than merely unused.
32
32
 
33
- A root barrel is also available for convenience:
33
+ A root barrel is also available for convenience — but since it imports every module, Metro will fail to resolve any optional peer dependency you haven't installed. Use it only if you have them all:
34
34
 
35
35
  ```ts
36
36
  import { PhotoPicker, useSubmitGuard } from "@forthtilliath/react-native-kit";
@@ -321,6 +321,45 @@ import { ThemeOptionList } from "@forthtilliath/react-native-kit/components/them
321
321
  <ThemeOptionList value={themePreference} onChange={setThemePreference} />;
322
322
  ```
323
323
 
324
+ ### `<ThemePreferenceProvider load save>` + `useThemePreference()` / `useResolvedScheme()`
325
+
326
+ Holds the light/dark/system preference in a context: loaded once on mount through `load`, changed from anywhere with `useThemePreference().setPreference`, persisted through `save`. Storage-agnostic. A choice made before `load` resolves isn't overwritten by the stored value. `useResolvedScheme()` returns the scheme actually applied (`"light" | "dark"`), to pick the palette.
327
+
328
+ ```tsx
329
+ import { ThemePreferenceProvider } from "@forthtilliath/react-native-kit/components/theme/ThemePreferenceProvider";
330
+ import { useResolvedScheme } from "@forthtilliath/react-native-kit/components/theme/themePreferenceContext";
331
+
332
+ <ThemePreferenceProvider
333
+ load={async () => (await getSettings()).theme}
334
+ save={async (theme) => saveSettings({ ...(await getSettings()), theme })}
335
+ >
336
+ <App />
337
+ </ThemePreferenceProvider>;
338
+
339
+ const colors = Colors[useResolvedScheme()];
340
+ ```
341
+
342
+ ### Overlays and pickers
343
+
344
+ Every component below has neutral default colors and a `styles` prop whose slots are each merged **after** their default (a partial override keeps the rest of the slot) — pass the app's palette there.
345
+
346
+ | Component | Import path | What it is |
347
+ | ----------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
348
+ | `<BottomSheet visible onClose bottomInset?>` | `components/overlay/BottomSheet` | Modal sliding up from the bottom, content scrollable past 85 % of the screen. Pass `useSafeAreaInsets().bottom` as `bottomInset` with Android edge-to-edge. |
349
+ | `<OptionsModal visible onClose options onSelect>` | `components/picker/OptionsModal` | Centered popup listing `{ value, label }` options; a tap selects and closes. |
350
+ | `<SelectField label valueLabel options onSelect>` | `components/picker/SelectField` | Native-looking "select": captioned button + `OptionsModal` (styled through `styles.optionsModal`). |
351
+ | `<SegmentedToggle options value onChange label?>` | `components/picker/SegmentedToggle` | Row of equal-width buttons, one active (Calendar / List…), announced as a radio group; each option may carry a smaller `hint` line under its label. |
352
+ | `<QuantityStepper value onChange label min? max?>` | `components/picker/QuantityStepper` | Whole-number counter `min…max` (`0…999`): −/+ buttons (disabled at the bounds, `longPressStep` to jump on long press) and a numeric field; screen-reader labels in French or English (`a11yLabels` to override). |
353
+ | `<SwitchRow label description? value onChange>` | `components/settings/SwitchRow` | Settings line "label + description + switch", the whole row pressable and read as one switch; `colors` for the track and thumb. |
354
+ | `<SettingsCard title icon?>` + `SettingsCardStatus`, `SettingsCardHint` | `components/settings/SettingsCard` | Settings card: icon + title header, then free content — status lines (small icon + text, `iconColor` per state) and a quieter hint. |
355
+ | `<CheckboxGroup options values onChange label?>` | `components/picker/CheckboxGroup` | Wrapping chips that each check and uncheck independently (extensions, tags…), each announced as a checkbox. |
356
+ | `<MonthDayGrid days selected onToggle>` | `components/picker/MonthDayGrid` | Monday-first month grid whose days toggle on and off (holidays, days off…); `YYYY-MM-DD` days. |
357
+ | `<AddButton label onPress>` | `components/list/AddButton` | Dashed "+ Add …" button closing a list. |
358
+ | `<DragHandle onPressIn accessibilityLabel>` | `components/list/DragHandle` | "⠿" grip for `react-native-draggable-flatlist` rows (gesture-handler's `Pressable`). |
359
+ | `<ErrorFallback error retry>` | `components/feedback/ErrorFallback` | Readable crash screen with a retry button, for Expo Router's `ErrorBoundary`; texts in French or English (`labels` to override). |
360
+
361
+ `SwipeableRow` also takes `fullSwipeToDelete`: swiping past half the row deletes right away (the row slides out over a full-width delete band) instead of revealing a button — pair it with an `UndoToast`.
362
+
324
363
  > `useUpdateCheck`, `<UpdateAvailableBanner>` and `<ChangelogNotes>` moved to
325
364
  > [`@forthtilliath/expo-release-updates-ui`](../expo-release-updates-ui#readme) —
326
365
  > install that package alongside this one if you need them.
@@ -462,7 +501,7 @@ import { ThemeSettingsScreen } from "@forthtilliath/react-native-kit/components/
462
501
 
463
502
  #### `<UpdateSettingsScreen currentVersion checkForUpdate compareVersions downloadAndInstallApk />`
464
503
 
465
- The full "check for update" screen: installed version, a manual check button, the available-update box (changelog + install button) or an up-to-date/error message, download progress, and — if `fetchReleaseHistory` is passed — a list of past releases. Checks automatically once on mount. Renders release notes via `@forthtilliath/expo-release-updates-ui`'s `ChangelogNotes`, and pairs naturally with that package's `useUpdateCheck`/`UpdateAvailableBanner` (same `checkForUpdate`/`compareVersions` you already pass those), but takes plain functions so it isn't tied to that package's exact shape.
504
+ The full "check for update" screen: installed version, a manual check button, the available-update box (changelog + install button) or an up-to-date/error message, download progress, and — if `fetchReleaseHistory` is passed — a list of past releases. Checks automatically once on mount. Renders release notes via `@forthtilliath/expo-release-updates-ui`'s `ChangelogNotes`, and pairs naturally with that package's `useUpdateCheck`/`UpdateAvailableBanner` (same `checkForUpdate`/`compareVersions` you already pass those), but takes plain functions so it isn't tied to that package's exact shape. `@forthtilliath/expo-release-updates-ui` is an optional peer dependency: install it to use this screen.
466
505
 
467
506
  ```tsx
468
507
  import { UpdateSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/UpdateSettingsScreen";
@@ -594,7 +633,68 @@ For all 7 components above, styling works the same way as `ChangelogNotes`: an o
594
633
  ### Utils (`utils/helpers/`)
595
634
 
596
635
  - `confirmDestructive` — wraps React Native's `Alert`, so it isn't framework-agnostic (see its own section above).
636
+ - `tapFeedback()`, `selectFeedback()`, `successFeedback()`, `errorFeedback()` (`utils/helpers/haptics`) — semantic aliases over `expo-haptics` (light impact, selection tick, success / error notification). Fire-and-forget: a failed vibration never throws. Needs `expo-haptics`.
597
637
  - `mergeSlotStyles(defaultStyles, styles)` — merges a component's per-slot `styles` overrides onto its defaults as `[default, override]` style arrays, so overriding one property of a slot keeps the rest of its default. Every component of this package uses it; handy for your own components following the same `styles` convention.
638
+ - `shareFile(uri, { mimeType, dialogTitle?, locale? })` — opens the native share sheet for a local file; throws a user-facing `Error` (French, or English with `locale: "en"`) when the device can't share. Needs `expo-sharing`.
639
+ - `shareTextFile(filename, content, options)` — writes the text to a file in the cache (replacing any previous one), then `shareFile`s it: `.ics`, `.csv`, JSON exports… Needs `expo-file-system` and `expo-sharing`.
640
+ - `shareViewAsImage(ref, { dialogTitle?, locale? })` (`utils/helpers/shareViewAsImage`) — captures a view as a PNG and shares it (scoreboard, schedule…). Needs `react-native-view-shot` and `expo-sharing`; the capture library is loaded on demand, so a binary built without it only fails this call, with a user-facing `Error`.
641
+ - `isNativeModuleAvailable(name)` — whether a native module is compiled into this binary (either architecture): check it before loading a library that crashes on import without its module.
642
+
643
+ ### Calendar (`utils/calendar/`)
644
+
645
+ Write an app's events to its **own** calendar on the phone — a local account the user can hide in one tap, never their personal calendars. Needs `expo-calendar` (and its config plugin's permission text).
646
+
647
+ ```ts
648
+ import { syncDedicatedCalendar } from "@forthtilliath/react-native-kit/utils/calendar/syncDedicatedCalendar";
649
+ import { toCalendarEventDetails } from "@forthtilliath/react-native-kit/utils/calendar/toCalendarEventDetails";
650
+
651
+ const result = await syncDedicatedCalendar({
652
+ calendar: { name: "my-app", title: "My App", color: "#293897" },
653
+ from: new Date(2026, 6, 1),
654
+ to: new Date(2026, 7, 1),
655
+ events: shifts.map((s) =>
656
+ toCalendarEventDetails({
657
+ title: s.code,
658
+ date: s.date,
659
+ start: s.start,
660
+ end: s.end,
661
+ }),
662
+ ),
663
+ });
664
+ if (result.status === "denied") explainPermission();
665
+ ```
666
+
667
+ - `syncDedicatedCalendar({ calendar, from, to, events })` — asks for the permission, finds the calendar by its internal `name` (creating it on first use), deletes its events in `[from, to)`, writes the new ones: a new sync replaces the previous one, no duplicates. Returns `{ status: "denied" }` or `{ status: "done", count }`.
668
+ - `toCalendarEventDetails({ title, notes?, date, start?, end? }, timeZone?)` — converts a day + optional `HH:MM` hours to what `createEvent` expects. An `end` at or before `start` ends the next day (night shift); without hours the event lasts all day, set at UTC midnight in `"UTC"` — otherwise Android shows it a day off depending on the device's time zone.
669
+
670
+ ### App files (`utils/files/`)
671
+
672
+ Files the app keeps for good (photos, avatars…): pickers return URIs in a cache folder the OS may empty at any time, so anything worth keeping is copied into a sub-folder of the app's documents. Needs `expo-file-system`.
673
+
674
+ ```ts
675
+ import {
676
+ deleteFileQuietly,
677
+ persistFile,
678
+ } from "@forthtilliath/react-native-kit/utils/files/appFiles";
679
+
680
+ const photoUri = await persistFile(pickedUri, {
681
+ directory: "foods",
682
+ prefix: "food",
683
+ });
684
+ // later, when the photo is replaced or its entity deleted:
685
+ deleteFileQuietly(oldPhotoUri);
686
+ ```
687
+
688
+ - `getAppDirectory(name)` — the persistent sub-folder `name` of documents, created on first use.
689
+ - `persistFile(sourceUri, { directory, prefix })` — copies a temporary file there as `<prefix>-<timestamp>.<ext>` and returns the copy's URI.
690
+ - `getFileExtension(uri, fallback = "jpg")` — extension of a URI, query string ignored.
691
+ - `fileExists(uri)` — `false` for a malformed URI instead of throwing.
692
+ - `deleteFileQuietly(uri)` — best-effort deletion: an absent file, `null` or a failure never throws.
693
+ - `pruneAppDirectory(name, keep)` — deletes the folder's files whose URI isn't in `keep` (orphaned photos after an edit or an import); returns how many were deleted.
694
+ - `readFileBase64(uri)` / `writeBase64File(base64, { directory, prefix, extension? })` — embed a photo in a JSON backup, then recreate it on restore (random file names: a restore writes many files in the same millisecond). `readFileBase64` returns `null` for a file already gone.
695
+ - `saveImageThumbnail(sourceUri, { directory, prefix, width?, quality? })` (`utils/files/imageThumbnail`) — shrinks a picked photo to a JPEG thumbnail (256 px wide, quality 0.8 by default) kept in the folder: small enough to embed in backups. Needs `expo-image-manipulator`.
696
+
697
+ `expo-sharing`, `expo-file-system`, `expo-calendar`, `expo-haptics`, `expo-image-manipulator` and `react-native-view-shot` are **optional** peer dependencies: only install those your app uses, and deep-import these helpers.
598
698
 
599
699
  The framework-agnostic helpers that used to live here (`getPeriodStartMs`, `getMostRecentIds`, `nextInCycle`, `normalizeForSearch`, `rankByNameMatch`, `escapeCsvField`, `formatCsvNumber`, `escapeHtml`) moved to [`@forthtilliath/ts-kit`](../ts-kit#readme) — no React Native dependency on them, so they're usable from Node/web too. They're still re-exported from this package's root barrel for convenience:
600
700
 
@@ -0,0 +1,63 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import { type KitLocale } from "../../i18n/locale.js";
3
+ /** Per-slot style overrides for {@link ErrorFallback}, each merged after its default. */
4
+ export interface ErrorFallbackStyles {
5
+ /** The full-screen, centered container (its background included). */
6
+ container?: StyleProp<ViewStyle>;
7
+ emoji?: StyleProp<TextStyle>;
8
+ title?: StyleProp<TextStyle>;
9
+ hint?: StyleProp<TextStyle>;
10
+ /** The retry button. */
11
+ button?: StyleProp<ViewStyle>;
12
+ buttonText?: StyleProp<TextStyle>;
13
+ /** The scrollable box showing the error message. */
14
+ detailsBox?: StyleProp<ViewStyle>;
15
+ /** The details box's content container (its padding). */
16
+ detailsContent?: StyleProp<ViewStyle>;
17
+ detailsText?: StyleProp<TextStyle>;
18
+ }
19
+ /** The fallback's texts; each defaults to the locale's. */
20
+ export interface ErrorFallbackLabels {
21
+ emoji: string;
22
+ title: string;
23
+ hint: string;
24
+ retry: string;
25
+ }
26
+ export interface ErrorFallbackProps {
27
+ error: Error;
28
+ /** Re-renders the failed tree — Expo Router's `retry`, or a boundary's reset. */
29
+ retry: () => void | Promise<void>;
30
+ /** Overrides some or all of the texts — see {@link ErrorFallbackLabels}. */
31
+ labels?: Partial<ErrorFallbackLabels>;
32
+ /**
33
+ * Language of the default texts — French by default, or the nearest
34
+ * `KitLocaleProvider`'s.
35
+ */
36
+ locale?: KitLocale;
37
+ /**
38
+ * Show `error.message` in a small box under the button.
39
+ *
40
+ * @defaultValue true
41
+ */
42
+ showDetails?: boolean;
43
+ /** Per-slot style overrides — see {@link ErrorFallbackStyles}. */
44
+ styles?: ErrorFallbackStyles;
45
+ }
46
+ /**
47
+ * A readable crash screen with a retry button, to replace Expo Router's
48
+ * default error screen (black background, raw message, meant for
49
+ * development). Export it as a route's `ErrorBoundary`, or render it from any
50
+ * error boundary.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * // app/_layout.tsx
55
+ * import { ErrorFallback } from "@forthtilliath/react-native-kit/components/feedback/ErrorFallback";
56
+ *
57
+ * export function ErrorBoundary(props: ErrorBoundaryProps) {
58
+ * return <ErrorFallback {...props} />;
59
+ * }
60
+ * ```
61
+ */
62
+ export declare function ErrorFallback({ error, retry, labels, locale, showDetails, styles, }: ErrorFallbackProps): import("react").JSX.Element;
63
+ //# sourceMappingURL=ErrorFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorFallback.d.ts","sourceRoot":"","sources":["../../../src/components/feedback/ErrorFallback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AAGpE,yFAAyF;AACzF,MAAM,WAAW,mBAAmB;IAClC,qEAAqE;IACrE,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,wBAAwB;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,oDAAoD;IACpD,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,yDAAyD;IACzD,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACpC;AAED,2DAA2D;AAC3D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,KAAK,CAAC;IACb,iFAAiF;IACjF,KAAK,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtC;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kEAAkE;IAClE,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AA+DD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,WAAkB,EAClB,MAAM,GACP,EAAE,kBAAkB,+BA4BpB"}
@@ -0,0 +1,86 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, ScrollView, Text, View } from "react-native";
3
+ import { useKitLocale } from "../../i18n/locale.js";
4
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
5
+ const defaultLabels = {
6
+ fr: {
7
+ emoji: "😕",
8
+ title: "Une erreur est survenue",
9
+ hint: "Désolé, quelque chose s'est mal passé. Réessaie ci-dessous.",
10
+ retry: "Réessayer",
11
+ },
12
+ en: {
13
+ emoji: "😕",
14
+ title: "Something went wrong",
15
+ hint: "Sorry, something broke. Try again below.",
16
+ retry: "Try again",
17
+ },
18
+ };
19
+ const defaultStyles = {
20
+ container: {
21
+ flex: 1,
22
+ backgroundColor: "#ffffff",
23
+ alignItems: "center",
24
+ justifyContent: "center",
25
+ padding: 24,
26
+ },
27
+ emoji: { fontSize: 40, marginBottom: 12 },
28
+ title: {
29
+ fontSize: 20,
30
+ fontWeight: "700",
31
+ marginBottom: 8,
32
+ textAlign: "center",
33
+ color: "#111111",
34
+ },
35
+ hint: {
36
+ fontSize: 14,
37
+ opacity: 0.7,
38
+ textAlign: "center",
39
+ marginBottom: 20,
40
+ color: "#111111",
41
+ },
42
+ button: {
43
+ paddingVertical: 12,
44
+ paddingHorizontal: 24,
45
+ borderRadius: 8,
46
+ backgroundColor: "#2563eb",
47
+ marginBottom: 24,
48
+ },
49
+ buttonText: { color: "#ffffff", fontWeight: "700" },
50
+ detailsBox: {
51
+ maxHeight: 120,
52
+ width: "100%",
53
+ borderRadius: 8,
54
+ backgroundColor: "rgba(128,128,128,0.08)",
55
+ },
56
+ detailsContent: { padding: 12 },
57
+ detailsText: {
58
+ fontSize: 12,
59
+ opacity: 0.6,
60
+ fontFamily: "monospace",
61
+ color: "#111111",
62
+ },
63
+ };
64
+ /**
65
+ * A readable crash screen with a retry button, to replace Expo Router's
66
+ * default error screen (black background, raw message, meant for
67
+ * development). Export it as a route's `ErrorBoundary`, or render it from any
68
+ * error boundary.
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * // app/_layout.tsx
73
+ * import { ErrorFallback } from "@forthtilliath/react-native-kit/components/feedback/ErrorFallback";
74
+ *
75
+ * export function ErrorBoundary(props: ErrorBoundaryProps) {
76
+ * return <ErrorFallback {...props} />;
77
+ * }
78
+ * ```
79
+ */
80
+ export function ErrorFallback({ error, retry, labels, locale, showDetails = true, styles, }) {
81
+ const texts = { ...defaultLabels[useKitLocale(locale)], ...labels };
82
+ const merged = mergeSlotStyles(defaultStyles, styles);
83
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.emoji, children: texts.emoji }), _jsx(Text, { style: merged.title, children: texts.title }), _jsx(Text, { style: merged.hint, children: texts.hint }), _jsx(Pressable, { style: merged.button, accessibilityRole: "button", onPress: () => {
84
+ void retry();
85
+ }, children: _jsx(Text, { style: merged.buttonText, children: texts.retry }) }), showDetails && (_jsx(ScrollView, { style: merged.detailsBox, contentContainerStyle: merged.detailsContent, children: _jsx(Text, { style: merged.detailsText, children: error.message }) }))] }));
86
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./ErrorFallback.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/feedback/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from "./ErrorFallback.js";
@@ -1,4 +1,6 @@
1
+ export * from "./feedback/index.js";
1
2
  export * from "./list/index.js";
3
+ export * from "./overlay/index.js";
2
4
  export * from "./picker/index.js";
3
5
  export * from "./settings/index.js";
4
6
  export * from "./theme/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC"}
@@ -1,4 +1,6 @@
1
+ export * from "./feedback/index.js";
1
2
  export * from "./list/index.js";
3
+ export * from "./overlay/index.js";
2
4
  export * from "./picker/index.js";
3
5
  export * from "./settings/index.js";
4
6
  export * from "./theme/index.js";
@@ -0,0 +1,27 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ /** Per-slot style overrides for {@link AddButton}, each merged after its default. */
3
+ export interface AddButtonStyles {
4
+ /** The dashed, full-width button. */
5
+ button?: StyleProp<ViewStyle>;
6
+ /** Its label. */
7
+ text?: StyleProp<TextStyle>;
8
+ }
9
+ export interface AddButtonProps {
10
+ /** Usually starts with "+", e.g. "+ Ajouter un groupe". */
11
+ label: string;
12
+ onPress: () => void;
13
+ /** Per-slot style overrides — see {@link AddButtonStyles}. */
14
+ styles?: AddButtonStyles;
15
+ }
16
+ /**
17
+ * The dashed "+ Add …" button closing a list, to append a row to it.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { AddButton } from "@forthtilliath/react-native-kit/components/list/AddButton";
22
+ *
23
+ * <AddButton label="+ Ajouter un groupe" onPress={addGroup} />
24
+ * ```
25
+ */
26
+ export declare function AddButton({ label, onPress, styles }: AddButtonProps): import("react").JSX.Element;
27
+ //# sourceMappingURL=AddButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddButton.d.ts","sourceRoot":"","sources":["../../../src/components/list/AddButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,iBAAiB;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAeD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,cAAc,+BAYnE"}
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Pressable, Text } from "react-native";
3
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
4
+ const defaultStyles = {
5
+ button: {
6
+ marginTop: 4,
7
+ padding: 12,
8
+ borderRadius: 8,
9
+ borderWidth: 1,
10
+ borderStyle: "dashed",
11
+ borderColor: "#d4d4d4",
12
+ alignItems: "center",
13
+ },
14
+ text: { fontWeight: "600", color: "#111111" },
15
+ };
16
+ /**
17
+ * The dashed "+ Add …" button closing a list, to append a row to it.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { AddButton } from "@forthtilliath/react-native-kit/components/list/AddButton";
22
+ *
23
+ * <AddButton label="+ Ajouter un groupe" onPress={addGroup} />
24
+ * ```
25
+ */
26
+ export function AddButton({ label, onPress, styles }) {
27
+ const merged = mergeSlotStyles(defaultStyles, styles);
28
+ return (_jsx(Pressable, { style: merged.button, onPress: onPress, accessibilityRole: "button", children: _jsx(Text, { style: merged.text, children: label }) }));
29
+ }
@@ -0,0 +1,35 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ /** Per-slot style overrides for {@link DragHandle}, each merged after its default. */
3
+ export interface DragHandleStyles {
4
+ /** The touch target around the glyph. */
5
+ handle?: StyleProp<ViewStyle>;
6
+ /** The "⠿" glyph. */
7
+ text?: StyleProp<TextStyle>;
8
+ }
9
+ export interface DragHandleProps {
10
+ /** Start the drag here — typically `drag` from a `DraggableFlatList` row. */
11
+ onPressIn: () => void;
12
+ disabled?: boolean;
13
+ /** E.g. `"Déplacer « Marie »"`. */
14
+ accessibilityLabel: string;
15
+ /** Per-slot style overrides — see {@link DragHandleStyles}. */
16
+ styles?: DragHandleStyles;
17
+ }
18
+ /**
19
+ * The "⠿" grip that reorders a row of a `react-native-draggable-flatlist`
20
+ * list: pressing it starts the drag, the rest of the row stays tappable.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * import { DragHandle } from "@forthtilliath/react-native-kit/components/list/DragHandle";
25
+ *
26
+ * renderItem={({ item, drag, isActive }) => (
27
+ * <Row>
28
+ * <DragHandle onPressIn={drag} disabled={isActive} accessibilityLabel={`Déplacer ${item.name}`} />
29
+ * <Text>{item.name}</Text>
30
+ * </Row>
31
+ * )}
32
+ * ```
33
+ */
34
+ export declare function DragHandle({ onPressIn, disabled, accessibilityLabel, styles, }: DragHandleProps): import("react").JSX.Element;
35
+ //# sourceMappingURL=DragHandle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DragHandle.d.ts","sourceRoot":"","sources":["../../../src/components/list/DragHandle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AASpE,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,qBAAqB;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAOD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,QAAQ,EACR,kBAAkB,EAClB,MAAM,GACP,EAAE,eAAe,+BAejB"}
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text } from "react-native";
3
+ // Gesture-handler's Pressable, not react-native's: the same gesture system as
4
+ // the pan handler of react-native-draggable-flatlist. With RN's Pressable the
5
+ // two fight over the responder and a drag sometimes starts "empty".
6
+ import { Pressable } from "react-native-gesture-handler";
7
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
8
+ const defaultStyles = {
9
+ handle: { paddingHorizontal: 4, paddingVertical: 4 },
10
+ text: { fontSize: 20, opacity: 0.5, color: "#111111" },
11
+ };
12
+ /**
13
+ * The "⠿" grip that reorders a row of a `react-native-draggable-flatlist`
14
+ * list: pressing it starts the drag, the rest of the row stays tappable.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * import { DragHandle } from "@forthtilliath/react-native-kit/components/list/DragHandle";
19
+ *
20
+ * renderItem={({ item, drag, isActive }) => (
21
+ * <Row>
22
+ * <DragHandle onPressIn={drag} disabled={isActive} accessibilityLabel={`Déplacer ${item.name}`} />
23
+ * <Text>{item.name}</Text>
24
+ * </Row>
25
+ * )}
26
+ * ```
27
+ */
28
+ export function DragHandle({ onPressIn, disabled, accessibilityLabel, styles, }) {
29
+ const merged = mergeSlotStyles(defaultStyles, styles);
30
+ return (_jsx(Pressable, { onPressIn: onPressIn, disabled: disabled, hitSlop: 10, style: merged.handle, accessibilityRole: "button", accessibilityLabel: accessibilityLabel, children: _jsx(Text, { style: merged.text, children: "\u283F" }) }));
31
+ }
@@ -2,8 +2,11 @@ import type { ReactNode } from "react";
2
2
  import type { StyleProp, TextStyle, ViewStyle } from "react-native";
3
3
  import { type KitLocale } from "../../i18n/locale.js";
4
4
  export interface SwipeableRowStyles {
5
+ /** The swipeable's outer container (clips the row while it slides). */
6
+ container?: StyleProp<ViewStyle>;
5
7
  deleteAction?: StyleProp<ViewStyle>;
6
8
  deleteActionText?: StyleProp<TextStyle>;
9
+ /** Ignored with `fullSwipeToDelete`, which shows no icon. */
7
10
  deleteIconColor?: string;
8
11
  }
9
12
  export interface SwipeableRowProps {
@@ -11,8 +14,17 @@ export interface SwipeableRowProps {
11
14
  onDelete: () => void;
12
15
  /** Accessibility label of the delete button, e.g. `"Delete « Marie »"`. */
13
16
  deleteLabel: string;
14
- /** Text under the trash icon. Defaults to "Supprimer" (`"Delete"` with `locale="en"`). */
17
+ /** Text of the delete action. Defaults to "Supprimer" (`"Delete"` with `locale="en"`). */
15
18
  deleteText?: string;
19
+ /**
20
+ * Swiping past half the row's width and letting go deletes right away —
21
+ * the row slides out, no button to tap. A full-width delete band shows
22
+ * under the row while swiping. Pair it with an `UndoToast`, as nothing
23
+ * asks for confirmation.
24
+ *
25
+ * By default (`false`), the swipe reveals a trash button to tap.
26
+ */
27
+ fullSwipeToDelete?: boolean;
16
28
  /**
17
29
  * Language of the default `deleteText` — French by default, or the
18
30
  * nearest `KitLocaleProvider`'s.
@@ -21,9 +33,11 @@ export interface SwipeableRowProps {
21
33
  styles?: SwipeableRowStyles;
22
34
  }
23
35
  /**
24
- * Swipe a list row left to reveal a delete button, on top of a tap to edit it.
25
- * Built on react-native-gesture-handler's `ReanimatedSwipeable`, so the app
36
+ * Swipe a list row left to delete it: either reveal a delete button, on top
37
+ * of a tap to edit the row, or — with `fullSwipeToDelete` — delete as soon
38
+ * as the swipe passes half the row. Built on react-native-gesture-handler's
39
+ * `ReanimatedSwipeable`, so the gesture runs on the UI thread and the app
26
40
  * needs `react-native-reanimated` (a peer dependency).
27
41
  */
28
- export declare function SwipeableRow({ children, onDelete, deleteLabel, deleteText, locale, styles, }: SwipeableRowProps): import("react").JSX.Element;
42
+ export declare function SwipeableRow({ children, onDelete, deleteLabel, deleteText, fullSwipeToDelete, locale, styles, }: SwipeableRowProps): import("react").JSX.Element;
29
43
  //# sourceMappingURL=SwipeableRow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SwipeableRow.d.ts","sourceRoot":"","sources":["../../../src/components/list/SwipeableRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AAGpE,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAqBD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,UAAU,EACV,MAAM,EACN,MAAM,GACP,EAAE,iBAAiB,+BAiCnB"}
1
+ {"version":3,"file":"SwipeableRow.d.ts","sourceRoot":"","sources":["../../../src/components/list/SwipeableRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AAGpE,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AA6CD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,UAAU,EACV,iBAAyB,EACzB,MAAM,EACN,MAAM,GACP,EAAE,iBAAiB,+BA2CnB"}
@@ -9,6 +9,7 @@ const defaultDeleteTexts = {
9
9
  en: "Delete",
10
10
  };
11
11
  const defaultStyles = {
12
+ container: {},
12
13
  deleteAction: {
13
14
  backgroundColor: "#dc2626",
14
15
  justifyContent: "center",
@@ -18,22 +19,48 @@ const defaultStyles = {
18
19
  borderRadius: 12,
19
20
  gap: 2,
20
21
  },
21
- deleteActionText: { color: "#ffffff", fontSize: 11, fontWeight: "600" },
22
- deleteIconColor: "#ffffff",
22
+ deleteActionText: {
23
+ color: "#ffffff",
24
+ fontSize: 11,
25
+ fontWeight: "600",
26
+ },
27
+ };
28
+ const fullSwipeDefaultStyles = {
29
+ container: {},
30
+ // Full width: the swipeable opens by the action's width, so the row slides
31
+ // all the way out, and its default threshold (half that width) is half the row.
32
+ deleteAction: {
33
+ flex: 1,
34
+ backgroundColor: "#dc2626",
35
+ justifyContent: "center",
36
+ alignItems: "flex-end",
37
+ paddingEnd: 16,
38
+ },
39
+ deleteActionText: {
40
+ color: "#ffffff",
41
+ fontSize: 15,
42
+ fontWeight: "700",
43
+ },
23
44
  };
45
+ const DEFAULT_ICON_COLOR = "#ffffff";
24
46
  /**
25
- * Swipe a list row left to reveal a delete button, on top of a tap to edit it.
26
- * Built on react-native-gesture-handler's `ReanimatedSwipeable`, so the app
47
+ * Swipe a list row left to delete it: either reveal a delete button, on top
48
+ * of a tap to edit the row, or — with `fullSwipeToDelete` — delete as soon
49
+ * as the swipe passes half the row. Built on react-native-gesture-handler's
50
+ * `ReanimatedSwipeable`, so the gesture runs on the UI thread and the app
27
51
  * needs `react-native-reanimated` (a peer dependency).
28
52
  */
29
- export function SwipeableRow({ children, onDelete, deleteLabel, deleteText, locale, styles, }) {
53
+ export function SwipeableRow({ children, onDelete, deleteLabel, deleteText, fullSwipeToDelete = false, locale, styles, }) {
30
54
  const resolvedLocale = useKitLocale(locale);
31
- const merged = {
32
- ...mergeSlotStyles(defaultStyles, styles),
33
- deleteIconColor: styles?.deleteIconColor ?? defaultStyles.deleteIconColor,
34
- };
35
- return (_jsx(ReanimatedSwipeable, { renderRightActions: (_progress, _translation, swipeable) => (_jsxs(Pressable, { style: merged.deleteAction, onPress: () => {
36
- swipeable.close();
37
- onDelete();
38
- }, accessibilityRole: "button", accessibilityLabel: deleteLabel, children: [_jsx(Ionicons, { name: "trash-outline", size: 22, color: merged.deleteIconColor }), _jsx(Text, { style: merged.deleteActionText, children: deleteText ?? defaultDeleteTexts[resolvedLocale] })] })), children: children }));
55
+ const merged = mergeSlotStyles(fullSwipeToDelete ? fullSwipeDefaultStyles : defaultStyles, styles);
56
+ return (_jsx(ReanimatedSwipeable, { containerStyle: merged.container, overshootRight: !fullSwipeToDelete, onSwipeableOpen: fullSwipeToDelete ? onDelete : undefined, renderRightActions: (_progress, _translation, swipeable) => (_jsxs(Pressable, { style: merged.deleteAction, onPress: () => {
57
+ if (fullSwipeToDelete) {
58
+ // Slide the row out like a swipe would; onSwipeableOpen deletes.
59
+ swipeable.openRight();
60
+ }
61
+ else {
62
+ swipeable.close();
63
+ onDelete();
64
+ }
65
+ }, accessibilityRole: "button", accessibilityLabel: deleteLabel, children: [!fullSwipeToDelete && (_jsx(Ionicons, { name: "trash-outline", size: 22, color: styles?.deleteIconColor ?? DEFAULT_ICON_COLOR })), _jsx(Text, { style: merged.deleteActionText, children: deleteText ?? defaultDeleteTexts[resolvedLocale] })] })), children: children }));
39
66
  }
@@ -1,4 +1,6 @@
1
+ export * from "./AddButton.js";
1
2
  export * from "./ColorDot.js";
3
+ export * from "./DragHandle.js";
2
4
  export * from "./SwipeableRow.js";
3
5
  export * from "./Thumbnail.js";
4
6
  export * from "./UndoToast.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/list/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/list/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC"}
@@ -1,4 +1,6 @@
1
+ export * from "./AddButton.js";
1
2
  export * from "./ColorDot.js";
3
+ export * from "./DragHandle.js";
2
4
  export * from "./SwipeableRow.js";
3
5
  export * from "./Thumbnail.js";
4
6
  export * from "./UndoToast.js";