@forthtilliath/react-native-kit 0.14.7 → 0.15.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 +37 -17
- package/dist/components/index.d.ts +4 -4
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +4 -4
- package/dist/components/list/SwipeableRow.d.ts +14 -1
- package/dist/components/list/SwipeableRow.d.ts.map +1 -1
- package/dist/components/list/SwipeableRow.js +18 -16
- package/dist/components/list/Thumbnail.d.ts.map +1 -1
- package/dist/components/list/Thumbnail.js +2 -6
- package/dist/components/list/UndoToast.d.ts +10 -4
- package/dist/components/list/UndoToast.d.ts.map +1 -1
- package/dist/components/list/UndoToast.js +11 -8
- package/dist/components/list/index.d.ts +4 -4
- package/dist/components/list/index.d.ts.map +1 -1
- package/dist/components/list/index.js +4 -4
- package/dist/components/picker/PhotoPicker.d.ts +7 -1
- package/dist/components/picker/PhotoPicker.d.ts.map +1 -1
- package/dist/components/picker/PhotoPicker.js +33 -28
- package/dist/components/picker/PickerModal.d.ts +7 -1
- package/dist/components/picker/PickerModal.d.ts.map +1 -1
- package/dist/components/picker/PickerModal.js +8 -28
- package/dist/components/picker/PickerModal.styles.d.ts +18 -1
- package/dist/components/picker/PickerModal.styles.d.ts.map +1 -1
- package/dist/components/picker/PickerModal.styles.js +16 -6
- package/dist/components/picker/VoiceSearchButton.d.ts +13 -2
- package/dist/components/picker/VoiceSearchButton.d.ts.map +1 -1
- package/dist/components/picker/VoiceSearchButton.js +18 -1
- package/dist/components/picker/index.d.ts +3 -3
- package/dist/components/picker/index.d.ts.map +1 -1
- package/dist/components/picker/index.js +3 -3
- package/dist/components/settings/AboutSettingsScreen.d.ts +7 -1
- package/dist/components/settings/AboutSettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/AboutSettingsScreen.js +13 -13
- package/dist/components/settings/BackupSettingsScreen.d.ts +9 -59
- package/dist/components/settings/BackupSettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/BackupSettingsScreen.js +32 -141
- package/dist/components/settings/BackupSettingsScreen.styles.d.ts +173 -0
- package/dist/components/settings/BackupSettingsScreen.styles.d.ts.map +1 -0
- package/dist/components/settings/BackupSettingsScreen.styles.js +98 -0
- package/dist/components/settings/ContactSettingsScreen.d.ts +7 -1
- package/dist/components/settings/ContactSettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/ContactSettingsScreen.js +15 -14
- package/dist/components/settings/PrivacySettingsScreen.d.ts +7 -1
- package/dist/components/settings/PrivacySettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/PrivacySettingsScreen.js +50 -28
- package/dist/components/settings/SettingsMenu.d.ts.map +1 -1
- package/dist/components/settings/SettingsMenu.js +2 -9
- package/dist/components/settings/ThemeSettingsScreen.d.ts +7 -1
- package/dist/components/settings/ThemeSettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/ThemeSettingsScreen.js +17 -19
- package/dist/components/settings/UpdateSettingsScreen.d.ts +11 -2
- package/dist/components/settings/UpdateSettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/UpdateSettingsScreen.js +7 -31
- package/dist/components/settings/UpdateSettingsScreen.styles.d.ts +28 -1
- package/dist/components/settings/UpdateSettingsScreen.styles.d.ts.map +1 -1
- package/dist/components/settings/UpdateSettingsScreen.styles.js +26 -11
- package/dist/components/settings/index.d.ts +7 -7
- package/dist/components/settings/index.d.ts.map +1 -1
- package/dist/components/settings/index.js +7 -7
- package/dist/components/theme/ThemeOptionList.d.ts +7 -1
- package/dist/components/theme/ThemeOptionList.d.ts.map +1 -1
- package/dist/components/theme/ThemeOptionList.js +6 -17
- package/dist/components/theme/ThemeToggle.d.ts +7 -1
- package/dist/components/theme/ThemeToggle.d.ts.map +1 -1
- package/dist/components/theme/ThemeToggle.js +6 -20
- package/dist/components/theme/index.d.ts +2 -2
- package/dist/components/theme/index.d.ts.map +1 -1
- package/dist/components/theme/index.js +2 -2
- package/dist/components/theme/themeLabels.d.ts +14 -0
- package/dist/components/theme/themeLabels.d.ts.map +1 -0
- package/dist/components/theme/themeLabels.js +5 -0
- package/dist/hooks/index.d.ts +3 -3
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -3
- package/dist/hooks/useDebouncedChange.d.ts.map +1 -1
- package/dist/hooks/useDebouncedChange.js +6 -7
- package/dist/hooks/useSubmitGuard.d.ts.map +1 -1
- package/dist/hooks/useSubmitGuard.js +9 -5
- package/dist/i18n/KitLocaleProvider.d.ts +19 -0
- package/dist/i18n/KitLocaleProvider.d.ts.map +1 -0
- package/dist/i18n/KitLocaleProvider.js +16 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/locale.d.ts +14 -0
- package/dist/i18n/locale.d.ts.map +1 -0
- package/dist/i18n/locale.js +18 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/utils/helpers/confirmDestructive.d.ts +6 -0
- package/dist/utils/helpers/confirmDestructive.d.ts.map +1 -1
- package/dist/utils/helpers/confirmDestructive.js +20 -6
- package/dist/utils/helpers/index.d.ts +1 -0
- package/dist/utils/helpers/index.d.ts.map +1 -1
- package/dist/utils/helpers/index.js +1 -0
- package/dist/utils/helpers/mergeSlotStyles.d.ts +20 -0
- package/dist/utils/helpers/mergeSlotStyles.d.ts.map +1 -0
- package/dist/utils/helpers/mergeSlotStyles.js +26 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/package.json +14 -10
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`,
|
|
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).
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
@@ -38,6 +38,23 @@ import { PhotoPicker, useSubmitGuard } from "@forthtilliath/react-native-kit";
|
|
|
38
38
|
|
|
39
39
|
**Avoid the barrel under Jest (or any other CommonJS `require` consumer).** `export * from` re-exports are evaluated eagerly on `require()` — unlike Metro's ESM bundling, there's no tree-shaking to skip the unused ones. Requiring the barrel from _any_ file, even one that only wants a framework-agnostic util like `getMostRecentIds`, pulls in every component's module graph, including native-module imports (`expo-speech-recognition` via `VoiceSearchButton`/`PickerModal`) that don't exist in a Jest environment — this throws `Cannot find native module '...'` at require time, not just at runtime for an unrendered component. Deep imports only ever load the one module you asked for, so they don't have this problem in any environment.
|
|
40
40
|
|
|
41
|
+
### Languages (`locale`)
|
|
42
|
+
|
|
43
|
+
Every component with built-in copy (labels, hints, accessibility labels, default sections…) ships it in **French** (default) and **English**. Pick the language:
|
|
44
|
+
|
|
45
|
+
- per component, with a `locale` prop: `<UndoToast locale="en" … />`;
|
|
46
|
+
- or once for a whole tree — handy for a bilingual app — with `KitLocaleProvider`:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { KitLocaleProvider } from "@forthtilliath/react-native-kit/i18n/KitLocaleProvider";
|
|
50
|
+
|
|
51
|
+
<KitLocaleProvider locale={userLanguage /* "fr" | "en" */}>
|
|
52
|
+
<App />
|
|
53
|
+
</KitLocaleProvider>;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A component's own `locale` prop wins over the provider, and its `labels` prop still overrides individual strings on top of either. The locale also drives `VoiceSearchButton`'s speech-recognition language (`fr-FR` / `en-US`) and `UpdateSettingsScreen`'s date format, unless `lang` / `dateLocale` are passed. `confirmDestructive`, being a plain function, can't read the provider: pass it `{ locale }`.
|
|
57
|
+
|
|
41
58
|
### `<Thumbnail photoUri={...} placeholderIcon="..." />`
|
|
42
59
|
|
|
43
60
|
List-row thumbnail: the photo if there is one, otherwise a placeholder icon.
|
|
@@ -54,7 +71,7 @@ import { Thumbnail } from "@forthtilliath/react-native-kit/components/list/Thumb
|
|
|
54
71
|
|
|
55
72
|
### `<SwipeableRow onDelete={...} deleteLabel="...">`
|
|
56
73
|
|
|
57
|
-
Swipe a list row left to reveal a delete button, on top of a tap to edit it.
|
|
74
|
+
Swipe a list row left to reveal a delete button, on top of a tap to edit it. Built on react-native-gesture-handler's `ReanimatedSwipeable`, so it needs `react-native-reanimated` installed. The text under the trash icon defaults to "Supprimer" ("Delete" in English), overridable with `deleteText`.
|
|
58
75
|
|
|
59
76
|
```tsx
|
|
60
77
|
import { SwipeableRow } from "@forthtilliath/react-native-kit/components/list/SwipeableRow";
|
|
@@ -119,12 +136,13 @@ import { UndoToast } from "@forthtilliath/react-native-kit/components/list/UndoT
|
|
|
119
136
|
}
|
|
120
137
|
```
|
|
121
138
|
|
|
122
|
-
| Prop | Type | Default
|
|
123
|
-
| ------------- | ----------------- |
|
|
124
|
-
| `message` | `string` | —
|
|
125
|
-
| `actionLabel` | `string` | `"Undo"` | The tappable label on the right. |
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
139
|
+
| Prop | Type | Default | Notes |
|
|
140
|
+
| ------------- | ----------------- | --------------------------------- | ----------------------------------------------------------------------------------- |
|
|
141
|
+
| `message` | `string` | — | Past tense; single line, truncated with an ellipsis. |
|
|
142
|
+
| `actionLabel` | `string` | `"Annuler"` (`"Undo"` in English) | The tappable label on the right. |
|
|
143
|
+
| `locale` | `KitLocale` | `"fr"` | Language of the default `actionLabel` (see [Languages](#languages-locale)). |
|
|
144
|
+
| `onAction` | `() => void` | — | Reverse the change here. The host then hides the toast. |
|
|
145
|
+
| `styles` | `UndoToastStyles` | — | Per-slot overrides (`toast` / `message` / `action`), each merged after its default. |
|
|
128
146
|
|
|
129
147
|
Full pattern — commit for good after 5s, localized label, re-themed:
|
|
130
148
|
|
|
@@ -171,9 +189,11 @@ Microphone button to dictate a search instead of typing it. Safe to mount more t
|
|
|
171
189
|
```tsx
|
|
172
190
|
import { VoiceSearchButton } from "@forthtilliath/react-native-kit/components/picker/VoiceSearchButton";
|
|
173
191
|
|
|
174
|
-
<VoiceSearchButton onResult={setQuery}
|
|
192
|
+
<VoiceSearchButton onResult={setQuery} />;
|
|
175
193
|
```
|
|
176
194
|
|
|
195
|
+
Recognizes French by default (`fr-FR`, or `en-US` with `locale="en"`); pass `lang` for any other BCP 47 language.
|
|
196
|
+
|
|
177
197
|
### `<PhotoPicker photoUri={...} onChange={...} savePhoto={...} photoLabel="..." />`
|
|
178
198
|
|
|
179
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.
|
|
@@ -220,7 +240,7 @@ import {
|
|
|
220
240
|
|
|
221
241
|
Groups results into sections via each item's `group` (e.g. food groups, ingredients vs recipes) — ignored while searching, where the best global matches are shown instead. Pass `filterItems` for custom ranking (e.g. `rankByNameMatch` from this same package's `utils/`) instead of the default case-insensitive substring match.
|
|
222
242
|
|
|
223
|
-
For all 5 components above, styling and (where relevant) copy work the same way as `ChangelogNotes`: an optional `styles` prop (all fields optional, neutral defaults) and, for `PhotoPicker`/`PickerModal`, an optional `labels` prop
|
|
243
|
+
For all 5 components above, styling and (where relevant) copy work the same way as `ChangelogNotes`: an optional `styles` prop (all fields optional, neutral defaults) and, for `PhotoPicker`/`PickerModal`, an optional `labels` prop over the built-in French or English copy (see [Languages](#languages-locale)).
|
|
224
244
|
|
|
225
245
|
### `useSubmitGuard()`
|
|
226
246
|
|
|
@@ -257,15 +277,14 @@ useDebouncedChange([settingsData, itemsData], 5 * 60 * 1000, () => {
|
|
|
257
277
|
|
|
258
278
|
### `confirmDestructive(title, onConfirm, options?)`
|
|
259
279
|
|
|
260
|
-
Generic destructive-action confirmation (title + message + Cancel/Confirm), for anything irreversible (delete, reset...). Ships with French defaults (`message`, `cancelLabel`, `confirmLabel` all overridable).
|
|
280
|
+
Generic destructive-action confirmation (title + message + Cancel/Confirm), for anything irreversible (delete, reset...). Ships with French defaults, or English ones with `{ locale: "en" }` (`message`, `cancelLabel`, `confirmLabel` all overridable).
|
|
261
281
|
|
|
262
282
|
```ts
|
|
263
283
|
import { confirmDestructive } from "@forthtilliath/react-native-kit/utils/helpers/confirmDestructive";
|
|
264
284
|
|
|
265
285
|
confirmDestructive("Delete this item?", () => deleteItem(id), {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
confirmLabel: "Delete",
|
|
286
|
+
locale: "en",
|
|
287
|
+
confirmLabel: "Remove",
|
|
269
288
|
});
|
|
270
289
|
```
|
|
271
290
|
|
|
@@ -568,11 +587,12 @@ import { PrivacySettingsScreen } from "@forthtilliath/react-native-kit/component
|
|
|
568
587
|
/>;
|
|
569
588
|
```
|
|
570
589
|
|
|
571
|
-
For all 7 components above, styling works the same way as `ChangelogNotes`: an optional `styles` prop (all fields optional, neutral defaults), and where relevant an optional `labels` prop
|
|
590
|
+
For all 7 components above, styling works the same way as `ChangelogNotes`: an optional `styles` prop (all fields optional, neutral defaults), and where relevant an optional `labels` prop over the built-in French or English copy (see [Languages](#languages-locale)).
|
|
572
591
|
|
|
573
592
|
### Utils (`utils/helpers/`)
|
|
574
593
|
|
|
575
|
-
`confirmDestructive`
|
|
594
|
+
- `confirmDestructive` — wraps React Native's `Alert`, so it isn't framework-agnostic (see its own section above).
|
|
595
|
+
- `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.
|
|
576
596
|
|
|
577
597
|
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:
|
|
578
598
|
|
|
@@ -600,4 +620,4 @@ Built to `dist/` (see the `exports` field in `package.json`), so run `pnpm run b
|
|
|
600
620
|
|
|
601
621
|
### Testing note
|
|
602
622
|
|
|
603
|
-
`react-native`'s package entry uses Flow syntax that `@babel/parser`'s flow plugin can't parse (`as Cast` casts aren't supported there), so Vitest can never load the real package directly. Tests alias `react-native` to a minimal stub (`src/__mocks__/react-native.tsx`) instead — see that file for details. `react-native-gesture-handler` ships the same kind of Flow syntax, and `@expo/vector-icons` has its own unrelated ESM-resolution issue under Vite/Rollup — both are stubbed the same way (`src/__mocks__/react-native-gesture-handler.tsx`, `src/__mocks__/expo-vector-icons.tsx`). `expo-speech-recognition` and `expo-image-picker` wrap native modules unavailable under Vitest regardless, so they're mocked with plain spies (`src/__mocks__/expo-speech-recognition.tsx`, `src/__mocks__/expo-image-picker.ts`).
|
|
623
|
+
`react-native`'s package entry uses Flow syntax that `@babel/parser`'s flow plugin can't parse (`as Cast` casts aren't supported there), so Vitest can never load the real package directly. Tests alias `react-native` to a minimal stub (`src/__mocks__/react-native.tsx`) instead — see that file for details. `react-native-gesture-handler` ships the same kind of Flow syntax, and `@expo/vector-icons` has its own unrelated ESM-resolution issue under Vite/Rollup — both are stubbed the same way (`src/__mocks__/react-native-gesture-handler.tsx`, `src/__mocks__/expo-vector-icons.tsx`). `react-native-gesture-handler/ReanimatedSwipeable` needs Reanimated's native runtime, so it gets its own stub too (`src/__mocks__/react-native-gesture-handler-reanimated-swipeable.tsx`). `expo-speech-recognition` and `expo-image-picker` wrap native modules unavailable under Vitest regardless, so they're mocked with plain spies (`src/__mocks__/expo-speech-recognition.tsx`, `src/__mocks__/expo-image-picker.ts`).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from "./list";
|
|
2
|
-
export * from "./picker";
|
|
3
|
-
export * from "./settings";
|
|
4
|
-
export * from "./theme";
|
|
1
|
+
export * from "./list/index.js";
|
|
2
|
+
export * from "./picker/index.js";
|
|
3
|
+
export * from "./settings/index.js";
|
|
4
|
+
export * from "./theme/index.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
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"}
|
package/dist/components/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from "./list";
|
|
2
|
-
export * from "./picker";
|
|
3
|
-
export * from "./settings";
|
|
4
|
-
export * from "./theme";
|
|
1
|
+
export * from "./list/index.js";
|
|
2
|
+
export * from "./picker/index.js";
|
|
3
|
+
export * from "./settings/index.js";
|
|
4
|
+
export * from "./theme/index.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
3
|
+
import { type KitLocale } from "../../i18n/locale.js";
|
|
3
4
|
export interface SwipeableRowStyles {
|
|
4
5
|
deleteAction?: StyleProp<ViewStyle>;
|
|
5
6
|
deleteActionText?: StyleProp<TextStyle>;
|
|
@@ -8,9 +9,21 @@ export interface SwipeableRowStyles {
|
|
|
8
9
|
export interface SwipeableRowProps {
|
|
9
10
|
children: ReactNode;
|
|
10
11
|
onDelete: () => void;
|
|
12
|
+
/** Accessibility label of the delete button, e.g. `"Delete « Marie »"`. */
|
|
11
13
|
deleteLabel: string;
|
|
14
|
+
/** Text under the trash icon. Defaults to "Supprimer" (`"Delete"` with `locale="en"`). */
|
|
12
15
|
deleteText?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Language of the default `deleteText` — French by default, or the
|
|
18
|
+
* nearest `KitLocaleProvider`'s.
|
|
19
|
+
*/
|
|
20
|
+
locale?: KitLocale;
|
|
13
21
|
styles?: SwipeableRowStyles;
|
|
14
22
|
}
|
|
15
|
-
|
|
23
|
+
/**
|
|
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
|
|
26
|
+
* needs `react-native-reanimated` (a peer dependency).
|
|
27
|
+
*/
|
|
28
|
+
export declare function SwipeableRow({ children, onDelete, deleteLabel, deleteText, locale, styles, }: SwipeableRowProps): import("react").JSX.Element;
|
|
16
29
|
//# sourceMappingURL=SwipeableRow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SwipeableRow.d.ts","sourceRoot":"","sources":["../../../src/components/list/SwipeableRow.tsx"],"names":[],"mappings":"
|
|
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,8 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRef } from "react";
|
|
3
2
|
import { Pressable, Text } from "react-native";
|
|
4
|
-
import
|
|
3
|
+
import ReanimatedSwipeable from "react-native-gesture-handler/ReanimatedSwipeable";
|
|
5
4
|
import { Ionicons } from "@expo/vector-icons";
|
|
5
|
+
import { useKitLocale } from "../../i18n/locale.js";
|
|
6
|
+
import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
|
|
7
|
+
const defaultDeleteTexts = {
|
|
8
|
+
fr: "Supprimer",
|
|
9
|
+
en: "Delete",
|
|
10
|
+
};
|
|
6
11
|
const defaultStyles = {
|
|
7
12
|
deleteAction: {
|
|
8
13
|
backgroundColor: "#dc2626",
|
|
@@ -16,22 +21,19 @@ const defaultStyles = {
|
|
|
16
21
|
deleteActionText: { color: "#ffffff", fontSize: 11, fontWeight: "600" },
|
|
17
22
|
deleteIconColor: "#ffffff",
|
|
18
23
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
/**
|
|
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
|
|
27
|
+
* needs `react-native-reanimated` (a peer dependency).
|
|
28
|
+
*/
|
|
29
|
+
export function SwipeableRow({ children, onDelete, deleteLabel, deleteText, locale, styles, }) {
|
|
30
|
+
const resolvedLocale = useKitLocale(locale);
|
|
24
31
|
const merged = {
|
|
25
|
-
|
|
26
|
-
deleteActionText: [
|
|
27
|
-
defaultStyles.deleteActionText,
|
|
28
|
-
styles?.deleteActionText,
|
|
29
|
-
],
|
|
32
|
+
...mergeSlotStyles(defaultStyles, styles),
|
|
30
33
|
deleteIconColor: styles?.deleteIconColor ?? defaultStyles.deleteIconColor,
|
|
31
34
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
swipeableRef.current?.close();
|
|
35
|
+
return (_jsx(ReanimatedSwipeable, { renderRightActions: (_progress, _translation, swipeable) => (_jsxs(Pressable, { style: merged.deleteAction, onPress: () => {
|
|
36
|
+
swipeable.close();
|
|
35
37
|
onDelete();
|
|
36
|
-
}, accessibilityRole: "button", accessibilityLabel: deleteLabel, children: [_jsx(Ionicons, { name: "trash-outline", size: 22, color: merged.deleteIconColor }), _jsx(Text, { style: merged.deleteActionText, children: deleteText })] })), children: children }));
|
|
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 }));
|
|
37
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Thumbnail.d.ts","sourceRoot":"","sources":["../../../src/components/list/Thumbnail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Thumbnail.d.ts","sourceRoot":"","sources":["../../../src/components/list/Thumbnail.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAI9C,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IAClC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iFAAiF;IACjF,eAAe,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAgBD,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,eAAe,EACf,IAAmB,EACnB,MAAM,GACP,EAAE,cAAc,+BA0BhB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Image, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
|
|
4
5
|
const DEFAULT_SIZE = 48;
|
|
5
6
|
const defaultStyles = {
|
|
6
7
|
thumbnail: { borderRadius: 10, backgroundColor: "#f3f4f6" },
|
|
@@ -14,13 +15,8 @@ const defaultStyles = {
|
|
|
14
15
|
};
|
|
15
16
|
// List-row thumbnail (photo, or a placeholder icon when there is none).
|
|
16
17
|
export function Thumbnail({ photoUri, placeholderIcon, size = DEFAULT_SIZE, styles, }) {
|
|
17
|
-
// Style fields are merged as arrays (default, then override) so a partial
|
|
18
|
-
// override (e.g. just backgroundColor) doesn't drop the default's
|
|
19
|
-
// borderRadius, or the placeholder's alignItems/justifyContent that center
|
|
20
|
-
// the icon.
|
|
21
18
|
const merged = {
|
|
22
|
-
|
|
23
|
-
placeholder: [defaultStyles.placeholder, styles?.placeholder],
|
|
19
|
+
...mergeSlotStyles(defaultStyles, styles),
|
|
24
20
|
iconColor: styles?.iconColor ?? defaultStyles.iconColor,
|
|
25
21
|
};
|
|
26
22
|
const dimensions = { width: size, height: size };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import { type KitLocale } from "../../i18n/locale.js";
|
|
2
3
|
/**
|
|
3
4
|
* Per-slot style overrides for {@link UndoToast}. Every field is optional and
|
|
4
5
|
* is **merged after** the matching default (as a style array), so overriding
|
|
@@ -10,7 +11,7 @@ export interface UndoToastStyles {
|
|
|
10
11
|
toast?: StyleProp<ViewStyle>;
|
|
11
12
|
/** The message text on the left. Truncated to a single line. */
|
|
12
13
|
message?: StyleProp<TextStyle>;
|
|
13
|
-
/** The action label on the right (the tappable "Undo"). */
|
|
14
|
+
/** The action label on the right (the tappable "Annuler" / "Undo"). */
|
|
14
15
|
action?: StyleProp<TextStyle>;
|
|
15
16
|
}
|
|
16
17
|
export interface UndoToastProps {
|
|
@@ -23,9 +24,14 @@ export interface UndoToastProps {
|
|
|
23
24
|
/**
|
|
24
25
|
* Label of the action button.
|
|
25
26
|
*
|
|
26
|
-
* @defaultValue "Undo"
|
|
27
|
+
* @defaultValue "Annuler" (`"Undo"` with `locale="en"`)
|
|
27
28
|
*/
|
|
28
29
|
actionLabel?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Language of the default `actionLabel` — French by default, or the
|
|
32
|
+
* nearest `KitLocaleProvider`'s.
|
|
33
|
+
*/
|
|
34
|
+
locale?: KitLocale;
|
|
29
35
|
/**
|
|
30
36
|
* Called when the action button is tapped. Wire this to your reverse-the-
|
|
31
37
|
* change logic (restore the row, re-insert the item…). The host is
|
|
@@ -38,7 +44,7 @@ export interface UndoToastProps {
|
|
|
38
44
|
/**
|
|
39
45
|
* A bottom-of-screen snackbar for **undoing an action that already
|
|
40
46
|
* happened** — the counterpart to a delete that runs immediately with no
|
|
41
|
-
* confirmation dialog (for instance a {@link
|
|
47
|
+
* confirmation dialog (for instance a {@link SwipeableRow} swipe).
|
|
42
48
|
*
|
|
43
49
|
* `UndoToast` is presentational only: it does not time itself out, animate
|
|
44
50
|
* in/out, or stack. The host owns that — typically a piece of state holding
|
|
@@ -96,5 +102,5 @@ export interface UndoToastProps {
|
|
|
96
102
|
* )}
|
|
97
103
|
* ```
|
|
98
104
|
*/
|
|
99
|
-
export declare function UndoToast({ message, actionLabel, onAction, styles, }: UndoToastProps): import("react").JSX.Element;
|
|
105
|
+
export declare function UndoToast({ message, actionLabel, locale, onAction, styles, }: UndoToastProps): import("react").JSX.Element;
|
|
100
106
|
//# sourceMappingURL=UndoToast.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UndoToast.d.ts","sourceRoot":"","sources":["../../../src/components/list/UndoToast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,gEAAgE;IAChE,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,
|
|
1
|
+
{"version":3,"file":"UndoToast.d.ts","sourceRoot":"","sources":["../../../src/components/list/UndoToast.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;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,gEAAgE;IAChE,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,uEAAuE;IACvE,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,WAAW,EACX,MAAM,EACN,QAAQ,EACR,MAAM,GACP,EAAE,cAAc,+BAkBhB"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Pressable, Text, View } from "react-native";
|
|
3
|
+
import { useKitLocale } from "../../i18n/locale.js";
|
|
4
|
+
import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
|
|
5
|
+
const defaultActionLabels = {
|
|
6
|
+
fr: "Annuler",
|
|
7
|
+
en: "Undo",
|
|
8
|
+
};
|
|
3
9
|
const defaultStyles = {
|
|
4
10
|
toast: {
|
|
5
11
|
position: "absolute",
|
|
@@ -34,7 +40,7 @@ const defaultStyles = {
|
|
|
34
40
|
/**
|
|
35
41
|
* A bottom-of-screen snackbar for **undoing an action that already
|
|
36
42
|
* happened** — the counterpart to a delete that runs immediately with no
|
|
37
|
-
* confirmation dialog (for instance a {@link
|
|
43
|
+
* confirmation dialog (for instance a {@link SwipeableRow} swipe).
|
|
38
44
|
*
|
|
39
45
|
* `UndoToast` is presentational only: it does not time itself out, animate
|
|
40
46
|
* in/out, or stack. The host owns that — typically a piece of state holding
|
|
@@ -92,13 +98,10 @@ const defaultStyles = {
|
|
|
92
98
|
* )}
|
|
93
99
|
* ```
|
|
94
100
|
*/
|
|
95
|
-
export function UndoToast({ message, actionLabel
|
|
101
|
+
export function UndoToast({ message, actionLabel, locale, onAction, styles, }) {
|
|
102
|
+
const resolvedLocale = useKitLocale(locale);
|
|
96
103
|
// Merge each slot as [default, override] so a partial override doesn't drop
|
|
97
104
|
// the rest of that slot's default (positioning, shadow, flex…).
|
|
98
|
-
const merged =
|
|
99
|
-
|
|
100
|
-
message: [defaultStyles.message, styles?.message],
|
|
101
|
-
action: [defaultStyles.action, styles?.action],
|
|
102
|
-
};
|
|
103
|
-
return (_jsxs(View, { style: merged.toast, children: [_jsx(Text, { style: merged.message, numberOfLines: 1, children: message }), _jsx(Pressable, { onPress: onAction, hitSlop: 8, accessibilityRole: "button", children: _jsx(Text, { style: merged.action, children: actionLabel }) })] }));
|
|
105
|
+
const merged = mergeSlotStyles(defaultStyles, styles);
|
|
106
|
+
return (_jsxs(View, { style: merged.toast, children: [_jsx(Text, { style: merged.message, numberOfLines: 1, children: message }), _jsx(Pressable, { onPress: onAction, hitSlop: 8, accessibilityRole: "button", children: _jsx(Text, { style: merged.action, children: actionLabel ?? defaultActionLabels[resolvedLocale] }) })] }));
|
|
104
107
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from "./ColorDot";
|
|
2
|
-
export * from "./SwipeableRow";
|
|
3
|
-
export * from "./Thumbnail";
|
|
4
|
-
export * from "./UndoToast";
|
|
1
|
+
export * from "./ColorDot.js";
|
|
2
|
+
export * from "./SwipeableRow.js";
|
|
3
|
+
export * from "./Thumbnail.js";
|
|
4
|
+
export * from "./UndoToast.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/list/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
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,4 +1,4 @@
|
|
|
1
|
-
export * from "./ColorDot";
|
|
2
|
-
export * from "./SwipeableRow";
|
|
3
|
-
export * from "./Thumbnail";
|
|
4
|
-
export * from "./UndoToast";
|
|
1
|
+
export * from "./ColorDot.js";
|
|
2
|
+
export * from "./SwipeableRow.js";
|
|
3
|
+
export * from "./Thumbnail.js";
|
|
4
|
+
export * from "./UndoToast.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ImageStyle, StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import { type KitLocale } from "../../i18n/locale.js";
|
|
2
3
|
export interface PhotoPickerStyles {
|
|
3
4
|
photoPicker?: StyleProp<ViewStyle>;
|
|
4
5
|
photoPreview?: StyleProp<ImageStyle>;
|
|
@@ -28,7 +29,12 @@ export interface PhotoPickerProps {
|
|
|
28
29
|
/** Used as-is in titles/accessibility labels, e.g. "of the container". */
|
|
29
30
|
photoLabel: string;
|
|
30
31
|
labels?: PhotoPickerLabels;
|
|
32
|
+
/**
|
|
33
|
+
* Language of the built-in labels — French by default, or the nearest
|
|
34
|
+
* `KitLocaleProvider`'s. `labels` still overrides individual strings.
|
|
35
|
+
*/
|
|
36
|
+
locale?: KitLocale;
|
|
31
37
|
styles?: PhotoPickerStyles;
|
|
32
38
|
}
|
|
33
|
-
export declare function PhotoPicker({ photoUri, onChange, savePhoto, photoLabel, labels, styles, }: PhotoPickerProps): import("react").JSX.Element;
|
|
39
|
+
export declare function PhotoPicker({ photoUri, onChange, savePhoto, photoLabel, labels, locale, styles, }: PhotoPickerProps): import("react").JSX.Element;
|
|
34
40
|
//# sourceMappingURL=PhotoPicker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PhotoPicker.d.ts","sourceRoot":"","sources":["../../../src/components/picker/PhotoPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKhF,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,YAAY,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IACrC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,oBAAoB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,sBAAsB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD,qBAAqB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IACvD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;
|
|
1
|
+
{"version":3,"file":"PhotoPicker.d.ts","sourceRoot":"","sources":["../../../src/components/picker/PhotoPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKhF,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AAGpE,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,YAAY,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IACrC,gBAAgB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,oBAAoB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,sBAAsB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD,qBAAqB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IACvD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAuED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,UAAU,EACV,MAAM,EACN,MAAM,EACN,MAAM,GACP,EAAE,gBAAgB,+BAuGlB"}
|
|
@@ -2,6 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Alert, Image, Pressable, Text, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
4
|
import * as ImagePicker from "expo-image-picker";
|
|
5
|
+
import { useKitLocale } from "../../i18n/locale.js";
|
|
6
|
+
import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
|
|
5
7
|
const defaultStyles = {
|
|
6
8
|
photoPicker: { alignSelf: "center", marginTop: 8 },
|
|
7
9
|
photoPreview: {
|
|
@@ -32,39 +34,42 @@ const defaultStyles = {
|
|
|
32
34
|
iconColor: "#6b7280",
|
|
33
35
|
};
|
|
34
36
|
const defaultLabels = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
fr: {
|
|
38
|
+
photoTitle: (photoLabel) => `Photo ${photoLabel}`,
|
|
39
|
+
editAccessibilityLabel: (photoLabel) => `Photo ${photoLabel}. Modifier.`,
|
|
40
|
+
addAccessibilityLabel: (photoLabel) => `Ajouter une photo ${photoLabel}`,
|
|
41
|
+
addPlaceholderText: "Ajouter une photo",
|
|
42
|
+
cameraPermissionTitle: "Accès à l'appareil photo refusé",
|
|
43
|
+
cameraPermissionMessage: "Autorise l'accès dans les réglages du téléphone pour prendre une photo.",
|
|
44
|
+
libraryPermissionTitle: "Accès aux photos refusé",
|
|
45
|
+
libraryPermissionMessage: "Autorise l'accès dans les réglages du téléphone pour choisir une photo.",
|
|
46
|
+
takePhoto: "Prendre une photo",
|
|
47
|
+
chooseFromLibrary: "Choisir dans la galerie",
|
|
48
|
+
cancel: "Annuler",
|
|
49
|
+
removePhoto: "Retirer la photo",
|
|
50
|
+
},
|
|
51
|
+
en: {
|
|
52
|
+
photoTitle: (photoLabel) => `Photo ${photoLabel}`,
|
|
53
|
+
editAccessibilityLabel: (photoLabel) => `Photo ${photoLabel}. Edit.`,
|
|
54
|
+
addAccessibilityLabel: (photoLabel) => `Add a photo ${photoLabel}`,
|
|
55
|
+
addPlaceholderText: "Add a photo",
|
|
56
|
+
cameraPermissionTitle: "Camera access denied",
|
|
57
|
+
cameraPermissionMessage: "Allow access in the phone's settings to take a photo.",
|
|
58
|
+
libraryPermissionTitle: "Photo access denied",
|
|
59
|
+
libraryPermissionMessage: "Allow access in the phone's settings to choose a photo.",
|
|
60
|
+
takePhoto: "Take a photo",
|
|
61
|
+
chooseFromLibrary: "Choose from library",
|
|
62
|
+
cancel: "Cancel",
|
|
63
|
+
removePhoto: "Remove photo",
|
|
64
|
+
},
|
|
47
65
|
};
|
|
48
66
|
// Photo picker (camera or library) with a preview.
|
|
49
|
-
export function PhotoPicker({ photoUri, onChange, savePhoto, photoLabel, labels, styles, }) {
|
|
50
|
-
// Style fields are merged as arrays (default, then override) so a partial
|
|
51
|
-
// override only changes the properties it specifies instead of replacing
|
|
52
|
-
// the whole default style object.
|
|
67
|
+
export function PhotoPicker({ photoUri, onChange, savePhoto, photoLabel, labels, locale, styles, }) {
|
|
53
68
|
const mergedStyles = {
|
|
54
|
-
|
|
55
|
-
photoPreview: [defaultStyles.photoPreview, styles?.photoPreview],
|
|
56
|
-
photoPlaceholder: [
|
|
57
|
-
defaultStyles.photoPlaceholder,
|
|
58
|
-
styles?.photoPlaceholder,
|
|
59
|
-
],
|
|
60
|
-
photoPlaceholderText: [
|
|
61
|
-
defaultStyles.photoPlaceholderText,
|
|
62
|
-
styles?.photoPlaceholderText,
|
|
63
|
-
],
|
|
64
|
-
clearLink: [defaultStyles.clearLink, styles?.clearLink],
|
|
69
|
+
...mergeSlotStyles(defaultStyles, styles),
|
|
65
70
|
iconColor: styles?.iconColor ?? defaultStyles.iconColor,
|
|
66
71
|
};
|
|
67
|
-
const t = { ...defaultLabels, ...labels };
|
|
72
|
+
const t = { ...defaultLabels[useKitLocale(locale)], ...labels };
|
|
68
73
|
async function pickFromCamera() {
|
|
69
74
|
const permission = await ImagePicker.requestCameraPermissionsAsync();
|
|
70
75
|
if (!permission.granted) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type KitLocale } from "../../i18n/locale.js";
|
|
1
2
|
import type { PickerModalLabels, PickerModalStyles } from "./PickerModal.styles.js";
|
|
2
3
|
export type { PickerModalLabels, PickerModalStyles, } from "./PickerModal.styles.js";
|
|
3
4
|
export interface PickerItem {
|
|
@@ -35,7 +36,12 @@ export interface PickerModalProps {
|
|
|
35
36
|
*/
|
|
36
37
|
groupOrder?: string[];
|
|
37
38
|
labels?: PickerModalLabels;
|
|
39
|
+
/**
|
|
40
|
+
* Language of the built-in labels and of voice search — French by default, or the nearest
|
|
41
|
+
* `KitLocaleProvider`'s. `labels` still overrides individual strings.
|
|
42
|
+
*/
|
|
43
|
+
locale?: KitLocale;
|
|
38
44
|
styles?: PickerModalStyles;
|
|
39
45
|
}
|
|
40
|
-
export declare function PickerModal({ visible, title, items, onSelect, onClose, emptyMessage, initialQuery, filterItems, extraActions, groupOrder, labels, styles, }: PickerModalProps): import("react").JSX.Element;
|
|
46
|
+
export declare function PickerModal({ visible, title, items, onSelect, onClose, emptyMessage, initialQuery, filterItems, extraActions, groupOrder, labels, locale, styles, }: PickerModalProps): import("react").JSX.Element;
|
|
41
47
|
//# sourceMappingURL=PickerModal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickerModal.d.ts","sourceRoot":"","sources":["../../../src/components/picker/PickerModal.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAMjC,YAAY,EACV,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,UAAU,EAAE,CAAC;IACnE,qFAAqF;IACrF,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,EAAE,CAAC;IACxD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAID,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,MAAM,GACP,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"PickerModal.d.ts","sourceRoot":"","sources":["../../../src/components/picker/PickerModal.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AAGpE,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AAMjC,YAAY,EACV,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,KAAK,UAAU,EAAE,CAAC;IACnE,qFAAqF;IACrF,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,IAAI,CAAA;KAAE,EAAE,CAAC;IACxD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAID,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,KAAK,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,MAAM,EACN,MAAM,GACP,EAAE,gBAAgB,+BAsIlB"}
|
|
@@ -2,45 +2,25 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo, useState } from "react";
|
|
3
3
|
import { Modal, Pressable, SectionList, Text, TextInput, View, } from "react-native";
|
|
4
4
|
import { Ionicons } from "@expo/vector-icons";
|
|
5
|
+
import { useKitLocale } from "../../i18n/locale.js";
|
|
6
|
+
import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
|
|
5
7
|
import { defaultLabels, defaultStyles } from "./PickerModal.styles.js";
|
|
6
8
|
import { PickerModalRow } from "./PickerModalRow.js";
|
|
7
9
|
import { usePickerModalSections } from "./usePickerModalSections.js";
|
|
8
10
|
import { VoiceSearchButton } from "./VoiceSearchButton.js";
|
|
9
11
|
// Full-screen picker: search (typed or dictated), optional sections, and
|
|
10
12
|
// "add" actions always visible above the results.
|
|
11
|
-
export function PickerModal({ visible, title, items, onSelect, onClose, emptyMessage, initialQuery, filterItems, extraActions, groupOrder, labels, styles, }) {
|
|
12
|
-
//
|
|
13
|
-
// override only changes the properties it specifies instead of replacing
|
|
14
|
-
// the whole default style object (e.g. losing rowThumbnailPlaceholder's
|
|
15
|
-
// alignItems/justifyContent by only overriding its backgroundColor).
|
|
13
|
+
export function PickerModal({ visible, title, items, onSelect, onClose, emptyMessage, initialQuery, filterItems, extraActions, groupOrder, labels, locale, styles, }) {
|
|
14
|
+
// Memoized: the rows (and their thumbnails) re-render on every keystroke.
|
|
16
15
|
const merged = useMemo(() => ({
|
|
17
|
-
|
|
18
|
-
header: [defaultStyles.header, styles?.header],
|
|
19
|
-
title: [defaultStyles.title, styles?.title],
|
|
20
|
-
close: [defaultStyles.close, styles?.close],
|
|
21
|
-
searchRow: [defaultStyles.searchRow, styles?.searchRow],
|
|
22
|
-
search: [defaultStyles.search, styles?.search],
|
|
23
|
-
row: [defaultStyles.row, styles?.row],
|
|
24
|
-
rowThumbnail: [defaultStyles.rowThumbnail, styles?.rowThumbnail],
|
|
25
|
-
rowThumbnailPlaceholder: [
|
|
26
|
-
defaultStyles.rowThumbnailPlaceholder,
|
|
27
|
-
styles?.rowThumbnailPlaceholder,
|
|
28
|
-
],
|
|
29
|
-
rowLabel: [defaultStyles.rowLabel, styles?.rowLabel],
|
|
30
|
-
rowSubtitle: [defaultStyles.rowSubtitle, styles?.rowSubtitle],
|
|
31
|
-
empty: [defaultStyles.empty, styles?.empty],
|
|
32
|
-
sectionHeader: [defaultStyles.sectionHeader, styles?.sectionHeader],
|
|
33
|
-
extraActions: [defaultStyles.extraActions, styles?.extraActions],
|
|
34
|
-
extraActionLabel: [
|
|
35
|
-
defaultStyles.extraActionLabel,
|
|
36
|
-
styles?.extraActionLabel,
|
|
37
|
-
],
|
|
16
|
+
...mergeSlotStyles(defaultStyles, styles),
|
|
38
17
|
extraActionIconColor: styles?.extraActionIconColor ?? defaultStyles.extraActionIconColor,
|
|
39
18
|
rowThumbnailPlaceholderIconColor: styles?.rowThumbnailPlaceholderIconColor ??
|
|
40
19
|
defaultStyles.rowThumbnailPlaceholderIconColor,
|
|
41
20
|
placeholderTextColor: styles?.placeholderTextColor ?? defaultStyles.placeholderTextColor,
|
|
42
21
|
}), [styles]);
|
|
43
|
-
const
|
|
22
|
+
const resolvedLocale = useKitLocale(locale);
|
|
23
|
+
const t = useMemo(() => ({ ...defaultLabels[resolvedLocale], ...labels }), [resolvedLocale, labels]);
|
|
44
24
|
const [query, setQuery] = useState(initialQuery ?? "");
|
|
45
25
|
// Reset the search each time the modal (re-)opens — adjusted during render
|
|
46
26
|
// rather than in an effect, since it only needs to happen once per
|
|
@@ -65,5 +45,5 @@ export function PickerModal({ visible, title, items, onSelect, onClose, emptyMes
|
|
|
65
45
|
onSelect(item);
|
|
66
46
|
setQuery("");
|
|
67
47
|
}
|
|
68
|
-
return (_jsx(Modal, { visible: visible, animationType: "slide", onRequestClose: onClose, children: _jsxs(View, { style: merged.container, children: [_jsxs(View, { style: merged.header, children: [_jsx(Text, { style: merged.title, children: title }), _jsx(Pressable, { onPress: onClose, hitSlop: 12, accessibilityRole: "button", accessibilityLabel: t.close, children: _jsx(Text, { style: merged.close, children: t.close }) })] }), _jsxs(View, { style: merged.searchRow, children: [_jsx(TextInput, { style: merged.search, placeholder: t.searchPlaceholder, placeholderTextColor: merged.placeholderTextColor, value: query, onChangeText: setQuery, autoFocus: true, accessibilityLabel: t.searchAccessibilityLabel }), _jsx(VoiceSearchButton, { onResult: setQuery, accessibilityLabel: t.voiceSearchAccessibilityLabel })] }), _jsx(SectionList, { sections: sections, keyExtractor: (item) => String(item.id), keyboardShouldPersistTaps: "handled", stickySectionHeadersEnabled: false, ListHeaderComponent: extraActions && extraActions.length > 0 ? (_jsx(View, { style: merged.extraActions, children: extraActions.map((action) => (_jsxs(Pressable, { style: merged.row, onPress: action.onPress, accessibilityRole: "button", accessibilityLabel: action.label, children: [_jsx(Ionicons, { name: "add-circle-outline", size: 20, color: merged.extraActionIconColor }), _jsx(Text, { style: [merged.rowLabel, merged.extraActionLabel], children: action.label })] }, action.label))) })) : null, ListEmptyComponent: _jsx(Text, { style: merged.empty, children: emptyMessage ?? t.defaultEmptyMessage }), renderSectionHeader: ({ section: { title: sectionTitle }, }) => sectionTitle ? (_jsx(Text, { style: merged.sectionHeader, children: sectionTitle })) : null, renderItem: ({ item }) => (_jsx(PickerModalRow, { item: item, styles: merged, onSelect: handleSelect })) })] }) }));
|
|
48
|
+
return (_jsx(Modal, { visible: visible, animationType: "slide", onRequestClose: onClose, children: _jsxs(View, { style: merged.container, children: [_jsxs(View, { style: merged.header, children: [_jsx(Text, { style: merged.title, children: title }), _jsx(Pressable, { onPress: onClose, hitSlop: 12, accessibilityRole: "button", accessibilityLabel: t.close, children: _jsx(Text, { style: merged.close, children: t.close }) })] }), _jsxs(View, { style: merged.searchRow, children: [_jsx(TextInput, { style: merged.search, placeholder: t.searchPlaceholder, placeholderTextColor: merged.placeholderTextColor, value: query, onChangeText: setQuery, autoFocus: true, accessibilityLabel: t.searchAccessibilityLabel }), _jsx(VoiceSearchButton, { onResult: setQuery, accessibilityLabel: t.voiceSearchAccessibilityLabel, locale: resolvedLocale })] }), _jsx(SectionList, { sections: sections, keyExtractor: (item) => String(item.id), keyboardShouldPersistTaps: "handled", stickySectionHeadersEnabled: false, ListHeaderComponent: extraActions && extraActions.length > 0 ? (_jsx(View, { style: merged.extraActions, children: extraActions.map((action) => (_jsxs(Pressable, { style: merged.row, onPress: action.onPress, accessibilityRole: "button", accessibilityLabel: action.label, children: [_jsx(Ionicons, { name: "add-circle-outline", size: 20, color: merged.extraActionIconColor }), _jsx(Text, { style: [merged.rowLabel, merged.extraActionLabel], children: action.label })] }, action.label))) })) : null, ListEmptyComponent: _jsx(Text, { style: merged.empty, children: emptyMessage ?? t.defaultEmptyMessage }), renderSectionHeader: ({ section: { title: sectionTitle }, }) => sectionTitle ? (_jsx(Text, { style: merged.sectionHeader, children: sectionTitle })) : null, renderItem: ({ item }) => (_jsx(PickerModalRow, { item: item, styles: merged, onSelect: handleSelect })) })] }) }));
|
|
69
49
|
}
|