@forthtilliath/react-native-kit 0.11.0 → 0.12.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 CHANGED
@@ -381,6 +381,37 @@ Two independent style knobs, both usable on their own:
381
381
  />
382
382
  ```
383
383
 
384
+ Pass `groups` instead of `items` to render rows under optional section headers (e.g. separating app-specific settings from the standard ones) — the two are mutually exclusive:
385
+
386
+ ```tsx
387
+ <SettingsMenu
388
+ groups={[
389
+ {
390
+ title: "Réglages de calcul",
391
+ items: [
392
+ {
393
+ key: "ratio",
394
+ emoji: "🧮",
395
+ title: "Ratio glucidique",
396
+ onPress: () => router.push("/settings/ratio"),
397
+ },
398
+ ],
399
+ },
400
+ {
401
+ // No `title` renders the group with no header at all, e.g. for a
402
+ // leading group of app-specific items above the standard ones.
403
+ items: [
404
+ {
405
+ key: "backup",
406
+ title: "Sauvegarde",
407
+ onPress: () => router.push("/settings/backup"),
408
+ },
409
+ ],
410
+ },
411
+ ]}
412
+ />
413
+ ```
414
+
384
415
  #### `<ThemeSettingsScreen value onChange />`
385
416
 
386
417
  Same light/dark/system data contract as `ThemeToggle`/`ThemeOptionList`. Two visuals, picked via `variant`: `"emoji"` (default) — emoji + label + a plain-text `✓` on the active row — or `"icon"` — `Ionicons` glyphs in one shared color (`styles.iconColor`/`iconColorActive`) + an `Ionicons` checkmark, matching `SettingsMenu`'s `defaultIconKind="icon"` look. `showHint={false}` hides the hint above the list.
@@ -441,9 +472,21 @@ import { BackupSettingsScreen } from "@forthtilliath/react-native-kit/components
441
472
 
442
473
  Omit `reminder` entirely for an app with no backup-reminder notifications.
443
474
 
444
- #### `<AboutSettingsScreen appName version description developerName />`
475
+ Three more knobs for apps whose backup section doesn't fit the compact 2-button shell: an `info` box above everything (e.g. "last auto-backup: ..."), `layout="sections"` for a titled, full-width, stacked layout instead of side-by-side buttons, and `icons` for a leading `Ionicons` glyph on either button.
476
+
477
+ ```tsx
478
+ <BackupSettingsScreen
479
+ onExport={shareBackup}
480
+ onImport={pickAndImportBackup}
481
+ info={{ label: "Dernière sauvegarde auto", value: lastAutoBackupLabel }}
482
+ layout="sections"
483
+ icons={{ export: "cloud-upload-outline", import: "cloud-download-outline" }}
484
+ />
485
+ ```
486
+
487
+ #### `<AboutSettingsScreen appName version description developerName? sections? />`
445
488
 
446
- App name, version, one or more description paragraphs, and a "developed by" credit. `appName`/`version` are required rather than defaulted internally (keeps this package framework-agnostic) — pass `Constants.expoConfig?.name`/`.version` from the caller.
489
+ App name, version, one or more description paragraphs, and an optional "developed by" credit. `appName`/`version` are required rather than defaulted internally (keeps this package framework-agnostic) — pass `Constants.expoConfig?.name`/`.version` from the caller.
447
490
 
448
491
  ```tsx
449
492
  import { AboutSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/AboutSettingsScreen";
@@ -456,7 +499,23 @@ import { AboutSettingsScreen } from "@forthtilliath/react-native-kit/components/
456
499
  />;
457
500
  ```
458
501
 
459
- #### `<ContactSettingsScreen email />`
502
+ Omit `developerName` for an app with no credit line to show. Pass `sections` for extra titled blocks after the description — e.g. a medical/legal disclaimer:
503
+
504
+ ```tsx
505
+ <AboutSettingsScreen
506
+ appName="GlucoDose"
507
+ version="1.2.0"
508
+ description="Un outil personnel de calcul de dose."
509
+ sections={[
510
+ {
511
+ title: "Avertissement médical",
512
+ paragraphs: ["Cet outil ne remplace pas un avis médical professionnel."],
513
+ },
514
+ ]}
515
+ />
516
+ ```
517
+
518
+ #### `<ContactSettingsScreen email actions? emailIcon? />`
460
519
 
461
520
  A `mailto:` button plus a short "how to report a bug" hint.
462
521
 
@@ -466,6 +525,18 @@ import { ContactSettingsScreen } from "@forthtilliath/react-native-kit/component
466
525
  <ContactSettingsScreen email="you@example.com" />;
467
526
  ```
468
527
 
528
+ Pass `actions` for extra rows below the email one (share the app, a donation link...) — switches the whole section from the single bordered button to a rows layout, email included:
529
+
530
+ ```tsx
531
+ <ContactSettingsScreen
532
+ email="you@example.com"
533
+ actions={[
534
+ { icon: "share-outline", label: "Partager l'app", onPress: shareApp },
535
+ { icon: "star-outline", label: "Noter l'app", onPress: openStoreListing },
536
+ ]}
537
+ />
538
+ ```
539
+
469
540
  #### `<PrivacySettingsScreen sections? />`
470
541
 
471
542
  Ships with the standard "everything stays on this device" three sections (no data collected, local storage only, sharing is opt-in) as the default — the right fit for most of these small personal-data apps. Pass your own `sections` to fully replace it for an app with different data handling (e.g. one that syncs to a server).
@@ -1,10 +1,15 @@
1
1
  import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ export interface AboutSettingsScreenSection {
3
+ title: string;
4
+ paragraphs: string[];
5
+ }
2
6
  export interface AboutSettingsScreenStyles {
3
7
  container?: StyleProp<ViewStyle>;
4
8
  appName?: StyleProp<TextStyle>;
5
9
  version?: StyleProp<TextStyle>;
6
10
  separator?: StyleProp<ViewStyle>;
7
11
  paragraph?: StyleProp<TextStyle>;
12
+ sectionTitle?: StyleProp<TextStyle>;
8
13
  hint?: StyleProp<TextStyle>;
9
14
  }
10
15
  export interface AboutSettingsScreenLabels {
@@ -16,9 +21,12 @@ export interface AboutSettingsScreenProps {
16
21
  version: string;
17
22
  /** One or more paragraphs describing the app, rendered in order. */
18
23
  description: string | string[];
19
- developerName: string;
24
+ /** Extra titled sections after the description, e.g. a medical/legal disclaimer. */
25
+ sections?: AboutSettingsScreenSection[];
26
+ /** Omit to not show a "developed by" credit line at all. */
27
+ developerName?: string;
20
28
  labels?: AboutSettingsScreenLabels;
21
29
  styles?: AboutSettingsScreenStyles;
22
30
  }
23
- export declare function AboutSettingsScreen({ appName, version, description, developerName, labels, styles, }: AboutSettingsScreenProps): import("react").JSX.Element;
31
+ export declare function AboutSettingsScreen({ appName, version, description, sections, developerName, labels, styles, }: AboutSettingsScreenProps): import("react").JSX.Element;
24
32
  //# sourceMappingURL=AboutSettingsScreen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AboutSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AboutSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC;AA8BD,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,EACP,WAAW,EACX,aAAa,EACb,MAAM,EACN,MAAM,GACP,EAAE,wBAAwB,+BA8B1B"}
1
+ {"version":3,"file":"AboutSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/AboutSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,0BAA0B,EAAE,CAAC;IACxC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC;AAqCD,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,EACP,WAAW,EACX,QAAQ,EACR,aAAa,EACb,MAAM,EACN,MAAM,GACP,EAAE,wBAAwB,+BA8C1B"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Text, View } from "react-native";
3
3
  const defaultLabels = {
4
4
  version: (version) => `Version ${version}`,
@@ -19,23 +19,31 @@ const defaultStyles = {
19
19
  color: "#111827",
20
20
  marginBottom: 12,
21
21
  },
22
+ sectionTitle: {
23
+ fontSize: 15,
24
+ fontWeight: "700",
25
+ color: "#111827",
26
+ marginBottom: 8,
27
+ },
22
28
  hint: { fontSize: 13, color: "#6b7280" },
23
29
  };
24
- // App name, version, a short description, and a "developed by" credit — the
25
- // standard "About" section of a settings tab. `appName`/`version` are
30
+ // App name, version, a short description, optional extra titled sections
31
+ // (e.g. a medical/legal disclaimer), and an optional "developed by" credit —
32
+ // the standard "About" section of a settings tab. `appName`/`version` are
26
33
  // required rather than defaulted from `expo-constants` so this stays
27
34
  // framework-agnostic; pass `Constants.expoConfig?.name`/`.version` from the
28
35
  // caller.
29
- export function AboutSettingsScreen({ appName, version, description, developerName, labels, styles, }) {
36
+ export function AboutSettingsScreen({ appName, version, description, sections, developerName, labels, styles, }) {
30
37
  const merged = {
31
38
  container: [defaultStyles.container, styles?.container],
32
39
  appName: [defaultStyles.appName, styles?.appName],
33
40
  version: [defaultStyles.version, styles?.version],
34
41
  separator: [defaultStyles.separator, styles?.separator],
35
42
  paragraph: [defaultStyles.paragraph, styles?.paragraph],
43
+ sectionTitle: [defaultStyles.sectionTitle, styles?.sectionTitle],
36
44
  hint: [defaultStyles.hint, styles?.hint],
37
45
  };
38
46
  const t = { ...defaultLabels, ...labels };
39
47
  const paragraphs = Array.isArray(description) ? description : [description];
40
- return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.appName, children: appName }), _jsx(Text, { style: merged.version, children: t.version(version) }), _jsx(View, { style: merged.separator }), paragraphs.map((paragraph) => (_jsx(Text, { style: merged.paragraph, children: paragraph }, paragraph))), _jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.hint, children: t.developedBy(developerName) })] }));
48
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.appName, children: appName }), _jsx(Text, { style: merged.version, children: t.version(version) }), _jsx(View, { style: merged.separator }), paragraphs.map((paragraph) => (_jsx(Text, { style: merged.paragraph, children: paragraph }, paragraph))), sections?.map((section) => (_jsxs(View, { children: [_jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.sectionTitle, children: section.title }), section.paragraphs.map((paragraph) => (_jsx(Text, { style: merged.paragraph, children: paragraph }, paragraph)))] }, section.title))), developerName && (_jsxs(_Fragment, { children: [_jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.hint, children: t.developedBy(developerName) })] }))] }));
41
49
  }
@@ -1,4 +1,5 @@
1
1
  import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import { Ionicons } from "@expo/vector-icons";
2
3
  export interface BackupSettingsScreenReminder {
3
4
  enabled: boolean;
4
5
  /** Set while a permission request or the toggle's own persistence is in flight. */
@@ -7,13 +8,29 @@ export interface BackupSettingsScreenReminder {
7
8
  /** How often the reminder fires, only used to word the hint (e.g. "every 7 days"). */
8
9
  intervalDays: number;
9
10
  }
11
+ export interface BackupSettingsScreenInfo {
12
+ label: string;
13
+ value: string;
14
+ }
15
+ export interface BackupSettingsScreenIcons {
16
+ export?: keyof typeof Ionicons.glyphMap;
17
+ import?: keyof typeof Ionicons.glyphMap;
18
+ }
10
19
  export interface BackupSettingsScreenStyles {
11
20
  container?: StyleProp<ViewStyle>;
21
+ infoBox?: StyleProp<ViewStyle>;
22
+ infoLabel?: StyleProp<TextStyle>;
23
+ infoValue?: StyleProp<TextStyle>;
24
+ sectionTitle?: StyleProp<TextStyle>;
12
25
  hint?: StyleProp<TextStyle>;
13
26
  row?: StyleProp<ViewStyle>;
14
27
  button?: StyleProp<ViewStyle>;
15
28
  buttonDisabled?: StyleProp<ViewStyle>;
16
29
  buttonText?: StyleProp<TextStyle>;
30
+ iconColor?: string;
31
+ /** Merged on top of `button`/`buttonText` — defaults to the same look as the export button. */
32
+ importButton?: StyleProp<ViewStyle>;
33
+ importButtonText?: StyleProp<TextStyle>;
17
34
  reminderRow?: StyleProp<ViewStyle>;
18
35
  reminderTextColumn?: StyleProp<ViewStyle>;
19
36
  reminderLabel?: StyleProp<TextStyle>;
@@ -24,6 +41,11 @@ export interface BackupSettingsScreenLabels {
24
41
  hint?: string;
25
42
  exportButton?: string;
26
43
  importButton?: string;
44
+ /** Only shown in `layout="sections"`. */
45
+ exportSectionTitle?: string;
46
+ exportHelpText?: string;
47
+ importSectionTitle?: string;
48
+ importHelpText?: string;
27
49
  importConfirmTitle?: string;
28
50
  importConfirmMessage?: string;
29
51
  importConfirmLabel?: string;
@@ -45,8 +67,18 @@ export interface BackupSettingsScreenProps {
45
67
  onImport: () => Promise<void>;
46
68
  /** Omit entirely to not show a reminder toggle at all. */
47
69
  reminder?: BackupSettingsScreenReminder;
70
+ /** An info box above everything else, e.g. "Last auto-backup: ...". Omit to not show one. */
71
+ info?: BackupSettingsScreenInfo;
72
+ /**
73
+ * `"compact"` (default): one shared hint, both buttons side by side.
74
+ * `"sections"`: each action gets its own title + help text, buttons
75
+ * stacked full-width.
76
+ */
77
+ layout?: "compact" | "sections";
78
+ /** Leading icon for each button. Omit either (or both) for a text-only button (default). */
79
+ icons?: BackupSettingsScreenIcons;
48
80
  labels?: BackupSettingsScreenLabels;
49
81
  styles?: BackupSettingsScreenStyles;
50
82
  }
51
- export declare function BackupSettingsScreen({ onExport, onImport, reminder, labels, styles, }: BackupSettingsScreenProps): import("react").JSX.Element;
83
+ export declare function BackupSettingsScreen({ onExport, onImport, reminder, info, layout, icons, labels, styles, }: BackupSettingsScreenProps): import("react").JSX.Element;
52
84
  //# sourceMappingURL=BackupSettingsScreen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BackupSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/BackupSettingsScreen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAYpE,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,sFAAsF;IACtF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,kBAAkB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACrC,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,uGAAuG;IACvG,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,4BAA4B,CAAC;IACxC,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,MAAM,CAAC,EAAE,0BAA0B,CAAC;CACrC;AAiED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,GACP,EAAE,yBAAyB,+BA8H3B"}
1
+ {"version":3,"file":"BackupSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/BackupSettingsScreen.tsx"],"names":[],"mappings":"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"}
@@ -1,11 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { ActivityIndicator, Alert, Pressable, Switch, Text, View, } from "react-native";
4
+ import { Ionicons } from "@expo/vector-icons";
4
5
  import { confirmDestructive } from "../../utils/helpers/confirmDestructive.js";
5
6
  const defaultLabels = {
6
7
  hint: "Les données sont stockées uniquement sur cet appareil et sont perdues en cas de réinstallation ou de mise à jour incompatible. Exporte-les régulièrement pour pouvoir les restaurer.",
7
8
  exportButton: "⬆️ Exporter",
8
9
  importButton: "⬇️ Importer",
10
+ exportSectionTitle: "Exporter",
11
+ exportHelpText: "Exporte toutes tes données dans un fichier que tu peux garder précieusement ou transférer vers un autre appareil.",
12
+ importSectionTitle: "Importer",
13
+ importHelpText: "Restaure une sauvegarde exportée précédemment. Remplace entièrement les données actuelles — il n'y a pas de fusion.",
9
14
  importConfirmTitle: "Importer une sauvegarde ?",
10
15
  importConfirmMessage: "Toutes les données actuelles seront remplacées par celles du fichier choisi.",
11
16
  importConfirmLabel: "Choisir un fichier",
@@ -18,9 +23,33 @@ const defaultLabels = {
18
23
  };
19
24
  const defaultStyles = {
20
25
  container: {},
21
- hint: { fontSize: 13, color: "#6b7280", marginBottom: 16 },
26
+ infoBox: {
27
+ backgroundColor: "#f9fafb",
28
+ borderWidth: 1,
29
+ borderColor: "#e5e7eb",
30
+ borderRadius: 10,
31
+ padding: 12,
32
+ marginBottom: 20,
33
+ gap: 2,
34
+ },
35
+ infoLabel: { fontSize: 12, color: "#6b7280" },
36
+ infoValue: { fontSize: 12, color: "#111827", fontWeight: "600" },
37
+ sectionTitle: {
38
+ fontSize: 15,
39
+ fontWeight: "700",
40
+ color: "#111827",
41
+ marginTop: 20,
42
+ },
43
+ hint: {
44
+ fontSize: 13,
45
+ color: "#6b7280",
46
+ marginTop: 6,
47
+ marginBottom: 14,
48
+ lineHeight: 18,
49
+ },
22
50
  row: { flexDirection: "row", gap: 10 },
23
51
  button: {
52
+ flexDirection: "row",
24
53
  flex: 1,
25
54
  paddingVertical: 12,
26
55
  borderRadius: 8,
@@ -28,9 +57,13 @@ const defaultStyles = {
28
57
  borderColor: "#2563eb",
29
58
  alignItems: "center",
30
59
  justifyContent: "center",
60
+ gap: 8,
31
61
  },
32
62
  buttonDisabled: { opacity: 0.5 },
33
63
  buttonText: { color: "#2563eb", fontWeight: "700" },
64
+ iconColor: "#2563eb",
65
+ importButton: undefined,
66
+ importButtonText: undefined,
34
67
  reminderRow: {
35
68
  flexDirection: "row",
36
69
  alignItems: "center",
@@ -55,16 +88,32 @@ function errorMessage(err, fallback) {
55
88
  // what gets exported/imported is entirely app-specific (its own database
56
89
  // tables), so `onExport`/`onImport` are required props, not something this
57
90
  // package could have a sensible default for.
58
- export function BackupSettingsScreen({ onExport, onImport, reminder, labels, styles, }) {
91
+ export function BackupSettingsScreen({ onExport, onImport, reminder, info, layout = "compact", icons, labels, styles, }) {
59
92
  const [exporting, setExporting] = useState(false);
60
93
  const [importing, setImporting] = useState(false);
61
94
  const merged = {
62
95
  container: [defaultStyles.container, styles?.container],
96
+ infoBox: [defaultStyles.infoBox, styles?.infoBox],
97
+ infoLabel: [defaultStyles.infoLabel, styles?.infoLabel],
98
+ infoValue: [defaultStyles.infoValue, styles?.infoValue],
99
+ sectionTitle: [defaultStyles.sectionTitle, styles?.sectionTitle],
63
100
  hint: [defaultStyles.hint, styles?.hint],
64
101
  row: [defaultStyles.row, styles?.row],
65
102
  button: [defaultStyles.button, styles?.button],
66
103
  buttonDisabled: [defaultStyles.buttonDisabled, styles?.buttonDisabled],
67
104
  buttonText: [defaultStyles.buttonText, styles?.buttonText],
105
+ importButton: [
106
+ defaultStyles.button,
107
+ styles?.button,
108
+ defaultStyles.importButton,
109
+ styles?.importButton,
110
+ ],
111
+ importButtonText: [
112
+ defaultStyles.buttonText,
113
+ styles?.buttonText,
114
+ defaultStyles.importButtonText,
115
+ styles?.importButtonText,
116
+ ],
68
117
  reminderRow: [defaultStyles.reminderRow, styles?.reminderRow],
69
118
  reminderTextColumn: [
70
119
  defaultStyles.reminderTextColumn,
@@ -75,6 +124,7 @@ export function BackupSettingsScreen({ onExport, onImport, reminder, labels, sty
75
124
  };
76
125
  const t = { ...defaultLabels, ...labels };
77
126
  const activityIndicatorColor = styles?.activityIndicatorColor ?? defaultStyles.activityIndicatorColor;
127
+ const iconColor = styles?.iconColor ?? defaultStyles.iconColor;
78
128
  async function handleExport() {
79
129
  if (exporting)
80
130
  return;
@@ -111,9 +161,11 @@ export function BackupSettingsScreen({ onExport, onImport, reminder, labels, sty
111
161
  confirmLabel: t.importConfirmLabel,
112
162
  });
113
163
  }
114
- return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsxs(View, { style: merged.row, children: [_jsx(Pressable, { style: [merged.button, exporting && merged.buttonDisabled], disabled: exporting, onPress: () => {
115
- void handleExport();
116
- }, accessibilityRole: "button", accessibilityLabel: t.exportButton, children: exporting ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Text, { style: merged.buttonText, children: t.exportButton })) }), _jsx(Pressable, { style: [merged.button, importing && merged.buttonDisabled], disabled: importing, onPress: handleImportPress, accessibilityRole: "button", accessibilityLabel: t.importButton, children: importing ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Text, { style: merged.buttonText, children: t.importButton })) })] }), reminder && (_jsxs(Pressable, { style: merged.reminderRow, disabled: reminder.busy, onPress: () => {
164
+ const exportButton = (_jsx(Pressable, { style: [merged.button, exporting && merged.buttonDisabled], disabled: exporting, onPress: () => {
165
+ void handleExport();
166
+ }, accessibilityRole: "button", accessibilityLabel: t.exportButton, children: exporting ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsxs(_Fragment, { children: [icons?.export && (_jsx(Ionicons, { name: icons.export, size: 18, color: iconColor })), _jsx(Text, { style: merged.buttonText, children: t.exportButton })] })) }));
167
+ const importButton = (_jsx(Pressable, { style: [merged.importButton, importing && merged.buttonDisabled], disabled: importing, onPress: handleImportPress, accessibilityRole: "button", accessibilityLabel: t.importButton, children: importing ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsxs(_Fragment, { children: [icons?.import && (_jsx(Ionicons, { name: icons.import, size: 18, color: iconColor })), _jsx(Text, { style: merged.importButtonText, children: t.importButton })] })) }));
168
+ return (_jsxs(View, { style: merged.container, children: [info && (_jsxs(View, { style: merged.infoBox, children: [_jsx(Text, { style: merged.infoLabel, children: info.label }), _jsx(Text, { style: merged.infoValue, children: info.value })] })), layout === "sections" ? (_jsxs(_Fragment, { children: [_jsx(Text, { style: merged.sectionTitle, children: t.exportSectionTitle }), _jsx(Text, { style: merged.hint, children: t.exportHelpText }), exportButton, _jsx(Text, { style: merged.sectionTitle, children: t.importSectionTitle }), _jsx(Text, { style: merged.hint, children: t.importHelpText }), importButton] })) : (_jsxs(_Fragment, { children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsxs(View, { style: merged.row, children: [exportButton, importButton] })] })), reminder && (_jsxs(Pressable, { style: merged.reminderRow, disabled: reminder.busy, onPress: () => {
117
169
  reminder.onToggle(!reminder.enabled);
118
170
  }, accessibilityRole: "switch", accessibilityState: { checked: reminder.enabled }, accessibilityLabel: t.reminderLabel, children: [_jsxs(View, { style: merged.reminderTextColumn, children: [_jsx(Text, { style: merged.reminderLabel, children: t.reminderLabel }), _jsx(Text, { style: merged.reminderHint, children: t.reminderHint?.(reminder.intervalDays) ??
119
171
  `Une notification tous les ${String(reminder.intervalDays)} jours pour penser à exporter.` })] }), reminder.busy ? (_jsx(ActivityIndicator, { color: activityIndicatorColor })) : (_jsx(Switch, { value: reminder.enabled, onValueChange: reminder.onToggle }))] }))] }));
@@ -1,19 +1,40 @@
1
1
  import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import { Ionicons } from "@expo/vector-icons";
3
+ export interface ContactSettingsScreenAction {
4
+ icon: keyof typeof Ionicons.glyphMap;
5
+ label: string;
6
+ onPress: () => void;
7
+ accessibilityLabel?: string;
8
+ }
2
9
  export interface ContactSettingsScreenStyles {
3
10
  container?: StyleProp<ViewStyle>;
4
11
  hint?: StyleProp<TextStyle>;
5
12
  emailButton?: StyleProp<ViewStyle>;
6
13
  emailButtonText?: StyleProp<TextStyle>;
7
14
  separator?: StyleProp<ViewStyle>;
15
+ /** Only used when `actions` is set (rows layout). */
16
+ row?: StyleProp<ViewStyle>;
17
+ rowText?: StyleProp<TextStyle>;
18
+ rowIconColor?: string;
8
19
  }
9
20
  export interface ContactSettingsScreenLabels {
10
21
  hint?: string;
11
22
  footer?: string;
23
+ /** Only used when `actions` is set — the email row's label (the button itself shows the address in the default single-button layout). */
24
+ emailRowLabel?: string;
12
25
  }
13
26
  export interface ContactSettingsScreenProps {
14
27
  email: string;
28
+ /**
29
+ * Extra rows below the email one (icon + label). Setting this switches
30
+ * the whole section from a single bordered `mailto:` button to a list of
31
+ * icon rows — the email becomes the first row (icon defaults to
32
+ * `emailIcon`, label to `labels.emailRowLabel`) instead of the button.
33
+ */
34
+ actions?: ContactSettingsScreenAction[];
35
+ emailIcon?: keyof typeof Ionicons.glyphMap;
15
36
  labels?: ContactSettingsScreenLabels;
16
37
  styles?: ContactSettingsScreenStyles;
17
38
  }
18
- export declare function ContactSettingsScreen({ email, labels, styles, }: ContactSettingsScreenProps): import("react").JSX.Element;
39
+ export declare function ContactSettingsScreen({ email, actions, emailIcon, labels, styles, }: ContactSettingsScreenProps): import("react").JSX.Element;
19
40
  //# sourceMappingURL=ContactSettingsScreen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContactSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ContactSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGpE,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,MAAM,CAAC,EAAE,2BAA2B,CAAC;CACtC;AA6BD,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,MAAM,EACN,MAAM,GACP,EAAE,0BAA0B,+BA8B5B"}
1
+ {"version":3,"file":"ContactSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ContactSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,qDAAqD;IACrD,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yIAAyI;IACzI,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,2BAA2B,EAAE,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAC3C,MAAM,CAAC,EAAE,2BAA2B,CAAC;IACrC,MAAM,CAAC,EAAE,2BAA2B,CAAC;CACtC;AA+CD,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,OAAO,EACP,SAA0B,EAC1B,MAAM,EACN,MAAM,GACP,EAAE,0BAA0B,+BAkE5B"}
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Linking, Pressable, Text, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
3
4
  const defaultLabels = {
4
5
  hint: "Une question, un bug, une suggestion ?",
5
6
  footer: "Si tu remontes un bug, précise si possible ce que tu faisais et ce que tu attendais — ça aide à le reproduire.",
7
+ emailRowLabel: "Me contacter",
6
8
  };
7
9
  const defaultStyles = {
8
10
  container: {},
@@ -21,19 +23,40 @@ const defaultStyles = {
21
23
  marginVertical: 16,
22
24
  backgroundColor: "#e5e7eb",
23
25
  },
26
+ row: {
27
+ flexDirection: "row",
28
+ alignItems: "center",
29
+ gap: 10,
30
+ backgroundColor: "#ffffff",
31
+ borderWidth: 1,
32
+ borderColor: "#e5e7eb",
33
+ borderRadius: 10,
34
+ padding: 14,
35
+ marginTop: 8,
36
+ },
37
+ rowText: { flex: 1, fontSize: 15, fontWeight: "600", color: "#111827" },
38
+ rowIconColor: "#111827",
24
39
  };
25
40
  // A mailto: button plus a short "how to report a bug" hint — the standard
26
- // "Contact" section of a settings tab.
27
- export function ContactSettingsScreen({ email, labels, styles, }) {
41
+ // "Contact" section of a settings tab. Pass `actions` for extra rows (share
42
+ // the app, a donation link...), switching email + those to a rows layout.
43
+ export function ContactSettingsScreen({ email, actions, emailIcon = "mail-outline", labels, styles, }) {
28
44
  const merged = {
29
45
  container: [defaultStyles.container, styles?.container],
30
46
  hint: [defaultStyles.hint, styles?.hint],
31
47
  emailButton: [defaultStyles.emailButton, styles?.emailButton],
32
48
  emailButtonText: [defaultStyles.emailButtonText, styles?.emailButtonText],
33
49
  separator: [defaultStyles.separator, styles?.separator],
50
+ row: [defaultStyles.row, styles?.row],
51
+ rowText: [defaultStyles.rowText, styles?.rowText],
34
52
  };
35
53
  const t = { ...defaultLabels, ...labels };
36
- return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsx(Pressable, { style: merged.emailButton, onPress: () => {
37
- void Linking.openURL(`mailto:${email}`);
38
- }, accessibilityRole: "button", accessibilityLabel: `Envoyer un email à ${email}`, children: _jsx(Text, { style: merged.emailButtonText, children: `✉️ ${email}` }) }), _jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.hint, children: t.footer })] }));
54
+ const rowIconColor = styles?.rowIconColor ?? defaultStyles.rowIconColor;
55
+ function openEmail() {
56
+ void Linking.openURL(`mailto:${email}`);
57
+ }
58
+ if (actions) {
59
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsxs(Pressable, { style: merged.row, onPress: openEmail, accessibilityRole: "button", accessibilityLabel: `Envoyer un email à ${email}`, children: [_jsx(Ionicons, { name: emailIcon, size: 20, color: rowIconColor }), _jsx(Text, { style: merged.rowText, children: t.emailRowLabel })] }), actions.map((action) => (_jsxs(Pressable, { style: merged.row, onPress: action.onPress, accessibilityRole: "button", accessibilityLabel: action.accessibilityLabel ?? action.label, children: [_jsx(Ionicons, { name: action.icon, size: 20, color: rowIconColor }), _jsx(Text, { style: merged.rowText, children: action.label })] }, action.label)))] }));
60
+ }
61
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.hint, children: t.hint }), _jsx(Pressable, { style: merged.emailButton, onPress: openEmail, accessibilityRole: "button", accessibilityLabel: `Envoyer un email à ${email}`, children: _jsx(Text, { style: merged.emailButtonText, children: `✉️ ${email}` }) }), _jsx(View, { style: merged.separator }), _jsx(Text, { style: merged.hint, children: t.footer })] }));
39
62
  }
@@ -29,8 +29,14 @@ export interface SettingsMenuItem {
29
29
  hint?: string;
30
30
  onPress: () => void;
31
31
  }
32
+ export interface SettingsMenuGroup {
33
+ /** Omit for a group with no visible header (e.g. the first one). */
34
+ title?: string;
35
+ items: SettingsMenuItem[];
36
+ }
32
37
  export interface SettingsMenuStyles {
33
38
  container?: StyleProp<ViewStyle>;
39
+ groupTitle?: StyleProp<TextStyle>;
34
40
  row?: StyleProp<ViewStyle>;
35
41
  titleColumn?: StyleProp<ViewStyle>;
36
42
  emoji?: StyleProp<TextStyle>;
@@ -40,7 +46,10 @@ export interface SettingsMenuStyles {
40
46
  chevronColor?: string;
41
47
  }
42
48
  export interface SettingsMenuProps {
43
- items: SettingsMenuItem[];
49
+ /** A flat list of rows. Mutually exclusive with `groups`. */
50
+ items?: SettingsMenuItem[];
51
+ /** Rows grouped under optional section headers. Mutually exclusive with `items`. */
52
+ groups?: SettingsMenuGroup[];
44
53
  /**
45
54
  * Which flavor of the default icon table to use for items that don't set
46
55
  * their own `emoji`/`icon`. Doesn't affect items that do. Defaults to
@@ -51,5 +60,5 @@ export interface SettingsMenuProps {
51
60
  showHints?: boolean;
52
61
  styles?: SettingsMenuStyles;
53
62
  }
54
- export declare function SettingsMenu({ items, defaultIconKind, showHints, styles, }: SettingsMenuProps): import("react").JSX.Element;
63
+ export declare function SettingsMenu({ items, groups, defaultIconKind, showHints, styles, }: SettingsMenuProps): import("react").JSX.Element;
55
64
  //# sourceMappingURL=SettingsMenu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SettingsMenu.d.ts","sourceRoot":"","sources":["../../../src/components/settings/SettingsMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAC7B,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAclE,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,gHAAgH;IAChH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AA4CD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,eAAyB,EACzB,SAAgB,EAChB,MAAM,GACP,EAAE,iBAAiB,+BAyCnB"}
1
+ {"version":3,"file":"SettingsMenu.d.ts","sourceRoot":"","sources":["../../../src/components/settings/SettingsMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAC7B,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAclE,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,oFAAoF;IACpF,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC7B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,gHAAgH;IAChH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAmDD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,eAAyB,EACzB,SAAgB,EAChB,MAAM,GACP,EAAE,iBAAiB,+BAsDnB"}
@@ -11,6 +11,12 @@ const defaultSectionIcons = {
11
11
  };
12
12
  const defaultStyles = {
13
13
  container: { gap: 10 },
14
+ groupTitle: {
15
+ fontSize: 13,
16
+ fontWeight: "700",
17
+ color: "#6b7280",
18
+ textTransform: "uppercase",
19
+ },
14
20
  row: {
15
21
  flexDirection: "row",
16
22
  alignItems: "center",
@@ -44,13 +50,15 @@ function resolveIcon(item, defaultIconKind) {
44
50
  : { icon: undefined, emoji: fallback.emoji };
45
51
  }
46
52
  // Menu screen for a settings tab: one row per section (icon + title +
47
- // optional hint), navigating to it on press. Has no opinion on how
53
+ // optional hint), navigating to it on press, optionally grouped under
54
+ // section headers (`groups` instead of `items`). Has no opinion on how
48
55
  // navigation works — each item carries its own `onPress` (e.g.
49
56
  // `() => router.push("/settings/xxx")`), so this doesn't depend on
50
57
  // expo-router or any other specific navigator.
51
- export function SettingsMenu({ items, defaultIconKind = "emoji", showHints = true, styles, }) {
58
+ export function SettingsMenu({ items, groups, defaultIconKind = "emoji", showHints = true, styles, }) {
52
59
  const merged = {
53
60
  container: [defaultStyles.container, styles?.container],
61
+ groupTitle: [defaultStyles.groupTitle, styles?.groupTitle],
54
62
  row: [defaultStyles.row, styles?.row],
55
63
  titleColumn: [defaultStyles.titleColumn, styles?.titleColumn],
56
64
  emoji: [defaultStyles.emoji, styles?.emoji],
@@ -59,8 +67,13 @@ export function SettingsMenu({ items, defaultIconKind = "emoji", showHints = tru
59
67
  };
60
68
  const chevronColor = styles?.chevronColor ?? defaultStyles.chevronColor;
61
69
  const iconColor = styles?.iconColor ?? defaultStyles.iconColor;
62
- return (_jsx(View, { style: merged.container, children: items.map((item) => {
63
- const { icon, emoji } = resolveIcon(item, defaultIconKind);
64
- return (_jsxs(Pressable, { style: merged.row, onPress: item.onPress, accessibilityRole: "button", accessibilityLabel: item.title, children: [icon ? (_jsx(Ionicons, { name: icon, size: 24, color: iconColor })) : (_jsx(Text, { style: merged.emoji, children: emoji })), _jsxs(View, { style: merged.titleColumn, children: [_jsx(Text, { style: merged.title, children: item.title }), showHints && item.hint ? (_jsx(Text, { style: merged.hint, children: item.hint })) : null] }), _jsx(Ionicons, { name: "chevron-forward", size: 20, color: chevronColor })] }, item.key));
65
- }) }));
70
+ function renderItem(item) {
71
+ const { icon, emoji } = resolveIcon(item, defaultIconKind);
72
+ return (_jsxs(Pressable, { style: merged.row, onPress: item.onPress, accessibilityRole: "button", accessibilityLabel: item.title, children: [icon ? (_jsx(Ionicons, { name: icon, size: 24, color: iconColor })) : (_jsx(Text, { style: merged.emoji, children: emoji })), _jsxs(View, { style: merged.titleColumn, children: [_jsx(Text, { style: merged.title, children: item.title }), showHints && item.hint ? (_jsx(Text, { style: merged.hint, children: item.hint })) : null] }), _jsx(Ionicons, { name: "chevron-forward", size: 20, color: chevronColor })] }, item.key));
73
+ }
74
+ return (_jsx(View, { style: merged.container, children: groups
75
+ ? groups.map((group, index) => (
76
+ // eslint-disable-next-line @eslint-react/no-array-index-key -- groups have no other stable identifier, and are a static, author-defined list (not reordered/filtered at runtime)
77
+ _jsxs(View, { style: merged.container, children: [group.title ? (_jsx(Text, { style: merged.groupTitle, children: group.title })) : null, group.items.map(renderItem)] }, group.title ?? index)))
78
+ : items?.map(renderItem) }));
66
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forthtilliath/react-native-kit",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {