@forthtilliath/react-native-kit 0.15.0 → 0.16.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.
- package/README.md +102 -0
- package/dist/components/feedback/ErrorFallback.d.ts +63 -0
- package/dist/components/feedback/ErrorFallback.d.ts.map +1 -0
- package/dist/components/feedback/ErrorFallback.js +86 -0
- package/dist/components/feedback/index.d.ts +2 -0
- package/dist/components/feedback/index.d.ts.map +1 -0
- package/dist/components/feedback/index.js +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/list/AddButton.d.ts +27 -0
- package/dist/components/list/AddButton.d.ts.map +1 -0
- package/dist/components/list/AddButton.js +29 -0
- package/dist/components/list/DragHandle.d.ts +35 -0
- package/dist/components/list/DragHandle.d.ts.map +1 -0
- package/dist/components/list/DragHandle.js +31 -0
- package/dist/components/list/SwipeableRow.d.ts +18 -4
- package/dist/components/list/SwipeableRow.d.ts.map +1 -1
- package/dist/components/list/SwipeableRow.js +40 -13
- package/dist/components/list/index.d.ts +2 -0
- package/dist/components/list/index.d.ts.map +1 -1
- package/dist/components/list/index.js +2 -0
- package/dist/components/overlay/BottomSheet.d.ts +45 -0
- package/dist/components/overlay/BottomSheet.d.ts.map +1 -0
- package/dist/components/overlay/BottomSheet.js +48 -0
- package/dist/components/overlay/index.d.ts +2 -0
- package/dist/components/overlay/index.d.ts.map +1 -0
- package/dist/components/overlay/index.js +1 -0
- package/dist/components/picker/CheckboxGroup.d.ts +47 -0
- package/dist/components/picker/CheckboxGroup.d.ts.map +1 -0
- package/dist/components/picker/CheckboxGroup.js +50 -0
- package/dist/components/picker/MonthDayGrid.d.ts +58 -0
- package/dist/components/picker/MonthDayGrid.d.ts.map +1 -0
- package/dist/components/picker/MonthDayGrid.js +73 -0
- package/dist/components/picker/OptionsModal.d.ts +45 -0
- package/dist/components/picker/OptionsModal.d.ts.map +1 -0
- package/dist/components/picker/OptionsModal.js +45 -0
- package/dist/components/picker/PhotoPicker.d.ts +5 -0
- package/dist/components/picker/PhotoPicker.d.ts.map +1 -1
- package/dist/components/picker/PhotoPicker.js +3 -9
- package/dist/components/picker/QuantityStepper.d.ts +63 -0
- package/dist/components/picker/QuantityStepper.d.ts.map +1 -0
- package/dist/components/picker/QuantityStepper.js +101 -0
- package/dist/components/picker/SegmentedToggle.d.ts +52 -0
- package/dist/components/picker/SegmentedToggle.d.ts.map +1 -0
- package/dist/components/picker/SegmentedToggle.js +48 -0
- package/dist/components/picker/SelectField.d.ts +45 -0
- package/dist/components/picker/SelectField.d.ts.map +1 -0
- package/dist/components/picker/SelectField.js +54 -0
- package/dist/components/picker/index.d.ts +6 -0
- package/dist/components/picker/index.d.ts.map +1 -1
- package/dist/components/picker/index.js +6 -0
- package/dist/components/settings/SettingsCard.d.ts +63 -0
- package/dist/components/settings/SettingsCard.d.ts.map +1 -0
- package/dist/components/settings/SettingsCard.js +68 -0
- package/dist/components/settings/SwitchRow.d.ts +48 -0
- package/dist/components/settings/SwitchRow.d.ts.map +1 -0
- package/dist/components/settings/SwitchRow.js +44 -0
- package/dist/components/settings/index.d.ts +2 -0
- package/dist/components/settings/index.d.ts.map +1 -1
- package/dist/components/settings/index.js +2 -0
- package/dist/components/theme/ThemePreferenceProvider.d.ts +39 -0
- package/dist/components/theme/ThemePreferenceProvider.d.ts.map +1 -0
- package/dist/components/theme/ThemePreferenceProvider.js +53 -0
- package/dist/components/theme/index.d.ts +2 -0
- package/dist/components/theme/index.d.ts.map +1 -1
- package/dist/components/theme/index.js +2 -0
- package/dist/components/theme/themePreferenceContext.d.ts +20 -0
- package/dist/components/theme/themePreferenceContext.d.ts.map +1 -0
- package/dist/components/theme/themePreferenceContext.js +21 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/utils/calendar/index.d.ts +3 -0
- package/dist/utils/calendar/index.d.ts.map +1 -0
- package/dist/utils/calendar/index.js +2 -0
- package/dist/utils/calendar/syncDedicatedCalendar.d.ts +37 -0
- package/dist/utils/calendar/syncDedicatedCalendar.d.ts.map +1 -0
- package/dist/utils/calendar/syncDedicatedCalendar.js +42 -0
- package/dist/utils/calendar/toCalendarEventDetails.d.ts +32 -0
- package/dist/utils/calendar/toCalendarEventDetails.d.ts.map +1 -0
- package/dist/utils/calendar/toCalendarEventDetails.js +47 -0
- package/dist/utils/files/appFiles.d.ts +61 -0
- package/dist/utils/files/appFiles.d.ts.map +1 -0
- package/dist/utils/files/appFiles.js +126 -0
- package/dist/utils/files/imageThumbnail.d.ts +22 -0
- package/dist/utils/files/imageThumbnail.d.ts.map +1 -0
- package/dist/utils/files/imageThumbnail.js +28 -0
- package/dist/utils/files/index.d.ts +3 -0
- package/dist/utils/files/index.d.ts.map +1 -0
- package/dist/utils/files/index.js +2 -0
- package/dist/utils/helpers/haptics.d.ts +9 -0
- package/dist/utils/helpers/haptics.d.ts.map +1 -0
- package/dist/utils/helpers/haptics.js +28 -0
- package/dist/utils/helpers/index.d.ts +5 -0
- package/dist/utils/helpers/index.d.ts.map +1 -1
- package/dist/utils/helpers/index.js +5 -0
- package/dist/utils/helpers/isNativeModuleAvailable.d.ts +12 -0
- package/dist/utils/helpers/isNativeModuleAvailable.d.ts.map +1 -0
- package/dist/utils/helpers/isNativeModuleAvailable.js +15 -0
- package/dist/utils/helpers/shareFile.d.ts +23 -0
- package/dist/utils/helpers/shareFile.d.ts.map +1 -0
- package/dist/utils/helpers/shareFile.js +26 -0
- package/dist/utils/helpers/shareTextFile.d.ts +16 -0
- package/dist/utils/helpers/shareTextFile.d.ts.map +1 -0
- package/dist/utils/helpers/shareTextFile.js +23 -0
- package/dist/utils/helpers/shareViewAsImage.d.ts +21 -0
- package/dist/utils/helpers/shareViewAsImage.d.ts.map +1 -0
- package/dist/utils/helpers/shareViewAsImage.js +32 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/package.json +38 -6
package/README.md
CHANGED
|
@@ -198,6 +198,8 @@ Recognizes French by default (`fr-FR`, or `en-US` with `locale="en"`); pass `lan
|
|
|
198
198
|
|
|
199
199
|
Photo picker (camera or library) with a preview and a remove link. `savePhoto` is injected rather than hard-coded, so where/how the picked image gets persisted is entirely up to the caller.
|
|
200
200
|
|
|
201
|
+
Only the camera asks for a permission: the library opens the system photo picker, which needs none — the app can block `READ_EXTERNAL_STORAGE` / `WRITE_EXTERNAL_STORAGE` / `READ_MEDIA_IMAGES` (e.g. `android.blockedPermissions` in `app.json`).
|
|
202
|
+
|
|
201
203
|
```tsx
|
|
202
204
|
import { PhotoPicker } from "@forthtilliath/react-native-kit/components/picker/PhotoPicker";
|
|
203
205
|
|
|
@@ -319,6 +321,45 @@ import { ThemeOptionList } from "@forthtilliath/react-native-kit/components/them
|
|
|
319
321
|
<ThemeOptionList value={themePreference} onChange={setThemePreference} />;
|
|
320
322
|
```
|
|
321
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
|
+
|
|
322
363
|
> `useUpdateCheck`, `<UpdateAvailableBanner>` and `<ChangelogNotes>` moved to
|
|
323
364
|
> [`@forthtilliath/expo-release-updates-ui`](../expo-release-updates-ui#readme) —
|
|
324
365
|
> install that package alongside this one if you need them.
|
|
@@ -592,7 +633,68 @@ For all 7 components above, styling works the same way as `ChangelogNotes`: an o
|
|
|
592
633
|
### Utils (`utils/helpers/`)
|
|
593
634
|
|
|
594
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`.
|
|
595
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.
|
|
596
698
|
|
|
597
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:
|
|
598
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 @@
|
|
|
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 +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"}
|
package/dist/components/index.js
CHANGED
|
@@ -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
|
|
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
|
|
25
|
-
*
|
|
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;
|
|
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: {
|
|
22
|
-
|
|
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
|
|
26
|
-
*
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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 +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"}
|