@forthtilliath/react-native-kit 0.10.0 → 0.11.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 +42 -7
- package/dist/components/settings/SettingsMenu.d.ts +34 -3
- package/dist/components/settings/SettingsMenu.d.ts.map +1 -1
- package/dist/components/settings/SettingsMenu.js +32 -6
- package/dist/components/settings/ThemeSettingsScreen.d.ts +20 -1
- package/dist/components/settings/ThemeSettingsScreen.d.ts.map +1 -1
- package/dist/components/settings/ThemeSettingsScreen.js +22 -7
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -307,44 +307,48 @@ export default function SettingsMenuScreen() {
|
|
|
307
307
|
return (
|
|
308
308
|
<SettingsMenu
|
|
309
309
|
items={[
|
|
310
|
+
// `key` doubles as the lookup into a built-in default icon/label
|
|
311
|
+
// pairing for the 6 sections above — no `emoji`/`icon` needed here.
|
|
310
312
|
{
|
|
311
313
|
key: "backup",
|
|
312
|
-
emoji: "💾",
|
|
313
314
|
title: "Sauvegarde",
|
|
314
315
|
hint: "Exporter / importer tes données",
|
|
315
316
|
onPress: () => router.push("/settings/backup"),
|
|
316
317
|
},
|
|
317
318
|
{
|
|
318
319
|
key: "theme",
|
|
319
|
-
emoji: "🎨",
|
|
320
320
|
title: "Thème",
|
|
321
321
|
hint: "Clair, sombre, ou automatique",
|
|
322
322
|
onPress: () => router.push("/settings/theme"),
|
|
323
323
|
},
|
|
324
324
|
{
|
|
325
325
|
key: "update",
|
|
326
|
-
emoji: "⬇️",
|
|
327
326
|
title: "Mise à jour",
|
|
328
327
|
onPress: () => router.push("/settings/update"),
|
|
329
328
|
},
|
|
330
329
|
{
|
|
331
330
|
key: "about",
|
|
332
|
-
emoji: "ℹ️",
|
|
333
331
|
title: "À propos",
|
|
334
332
|
onPress: () => router.push("/settings/about"),
|
|
335
333
|
},
|
|
336
334
|
{
|
|
337
335
|
key: "contact",
|
|
338
|
-
emoji: "✉️",
|
|
339
336
|
title: "Contact",
|
|
340
337
|
onPress: () => router.push("/settings/contact"),
|
|
341
338
|
},
|
|
342
339
|
{
|
|
343
340
|
key: "privacy",
|
|
344
|
-
emoji: "🔒",
|
|
345
341
|
title: "Confidentialité",
|
|
346
342
|
onPress: () => router.push("/settings/privacy"),
|
|
347
343
|
},
|
|
344
|
+
// An app-specific section: no default exists for an arbitrary key,
|
|
345
|
+
// so `emoji`/`icon` (and `hint`, if you want one) are yours to set.
|
|
346
|
+
{
|
|
347
|
+
key: "profile",
|
|
348
|
+
emoji: "🙋",
|
|
349
|
+
title: "Mon nom",
|
|
350
|
+
onPress: () => router.push("/settings/profile"),
|
|
351
|
+
},
|
|
348
352
|
]}
|
|
349
353
|
/>
|
|
350
354
|
);
|
|
@@ -353,14 +357,45 @@ export default function SettingsMenuScreen() {
|
|
|
353
357
|
|
|
354
358
|
`SettingsMenu` doesn't depend on expo-router (or any other navigator) itself — each item carries its own `onPress`, so it works the same with any navigation setup.
|
|
355
359
|
|
|
360
|
+
Two independent style knobs, both usable on their own:
|
|
361
|
+
|
|
362
|
+
- **Icon flavor** — each item can set its own `emoji` (naturally multicolor) or `icon` (an `Ionicons` glyph, name in `styles.iconColor`) to override its default; `defaultIconKind` (`"emoji"` by default, or `"icon"`) picks which flavor of the _default_ table applies to items that don't set either.
|
|
363
|
+
- **Hints** — set `showHints={false}` to hide every item's `hint` at once, for a denser, icon-only-style menu.
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
<SettingsMenu
|
|
367
|
+
items={[
|
|
368
|
+
{
|
|
369
|
+
key: "backup",
|
|
370
|
+
title: "Sauvegarde",
|
|
371
|
+
onPress: () => router.push("/settings/backup"),
|
|
372
|
+
},
|
|
373
|
+
]}
|
|
374
|
+
defaultIconKind="icon"
|
|
375
|
+
showHints={false}
|
|
376
|
+
styles={{
|
|
377
|
+
row: { backgroundColor: colors.primary, borderWidth: 0 },
|
|
378
|
+
iconColor: "#fff",
|
|
379
|
+
title: { color: "#fff" },
|
|
380
|
+
}}
|
|
381
|
+
/>
|
|
382
|
+
```
|
|
383
|
+
|
|
356
384
|
#### `<ThemeSettingsScreen value onChange />`
|
|
357
385
|
|
|
358
|
-
Same light/dark/system data contract as `ThemeToggle`/`ThemeOptionList
|
|
386
|
+
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.
|
|
359
387
|
|
|
360
388
|
```tsx
|
|
361
389
|
import { ThemeSettingsScreen } from "@forthtilliath/react-native-kit/components/settings/ThemeSettingsScreen";
|
|
362
390
|
|
|
363
391
|
<ThemeSettingsScreen value={themePreference} onChange={setThemePreference} />;
|
|
392
|
+
|
|
393
|
+
// Or, matching an icon-flavored SettingsMenu:
|
|
394
|
+
<ThemeSettingsScreen
|
|
395
|
+
value={themePreference}
|
|
396
|
+
onChange={setThemePreference}
|
|
397
|
+
variant="icon"
|
|
398
|
+
/>;
|
|
364
399
|
```
|
|
365
400
|
|
|
366
401
|
#### `<UpdateSettingsScreen currentVersion checkForUpdate compareVersions downloadAndInstallApk />`
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
3
|
+
/**
|
|
4
|
+
* Well-known settings sections this package itself ships a screen for
|
|
5
|
+
* (`BackupSettingsScreen`, `ThemeSettingsScreen`, etc.) — matching `key`
|
|
6
|
+
* against one of these gets an item a default icon for free. Any other
|
|
7
|
+
* `key` (e.g. an app-specific "profile"/"notifications" section) needs its
|
|
8
|
+
* own `emoji`/`icon`, since there's no sensible universal default for it.
|
|
9
|
+
*/
|
|
10
|
+
export type SettingsMenuSection = "backup" | "theme" | "update" | "about" | "contact" | "privacy";
|
|
2
11
|
export interface SettingsMenuItem {
|
|
3
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Stable React key. Doubles as the lookup into the default icon table
|
|
14
|
+
* above — use `"backup"`/`"theme"`/`"update"`/`"about"`/`"contact"`/
|
|
15
|
+
* `"privacy"` to get a sensible icon for free, any other string for a
|
|
16
|
+
* custom (app-specific) section, which then needs its own `emoji`/`icon`.
|
|
17
|
+
*/
|
|
4
18
|
key: string;
|
|
5
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Overrides the default icon for this item (whether that default came
|
|
21
|
+
* from `key` or there simply isn't one). At most one of the two —
|
|
22
|
+
* `emoji` renders as-is (naturally multicolor, varies per item); `icon`
|
|
23
|
+
* renders as an `Ionicons` glyph in one shared color (`styles.iconColor`),
|
|
24
|
+
* for a flatter, more uniform look.
|
|
25
|
+
*/
|
|
26
|
+
emoji?: string;
|
|
27
|
+
icon?: keyof typeof Ionicons.glyphMap;
|
|
6
28
|
title: string;
|
|
7
29
|
hint?: string;
|
|
8
30
|
onPress: () => void;
|
|
@@ -12,13 +34,22 @@ export interface SettingsMenuStyles {
|
|
|
12
34
|
row?: StyleProp<ViewStyle>;
|
|
13
35
|
titleColumn?: StyleProp<ViewStyle>;
|
|
14
36
|
emoji?: StyleProp<TextStyle>;
|
|
37
|
+
iconColor?: string;
|
|
15
38
|
title?: StyleProp<TextStyle>;
|
|
16
39
|
hint?: StyleProp<TextStyle>;
|
|
17
40
|
chevronColor?: string;
|
|
18
41
|
}
|
|
19
42
|
export interface SettingsMenuProps {
|
|
20
43
|
items: SettingsMenuItem[];
|
|
44
|
+
/**
|
|
45
|
+
* Which flavor of the default icon table to use for items that don't set
|
|
46
|
+
* their own `emoji`/`icon`. Doesn't affect items that do. Defaults to
|
|
47
|
+
* `"emoji"`.
|
|
48
|
+
*/
|
|
49
|
+
defaultIconKind?: "emoji" | "icon";
|
|
50
|
+
/** Set to `false` to never render any item's `hint`, for a denser, icon-only-style menu. Defaults to `true`. */
|
|
51
|
+
showHints?: boolean;
|
|
21
52
|
styles?: SettingsMenuStyles;
|
|
22
53
|
}
|
|
23
|
-
export declare function SettingsMenu({ items, styles }: SettingsMenuProps): import("react").JSX.Element;
|
|
54
|
+
export declare function SettingsMenu({ items, defaultIconKind, showHints, styles, }: SettingsMenuProps): import("react").JSX.Element;
|
|
24
55
|
//# 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;
|
|
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,6 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Pressable, Text, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
|
+
const defaultSectionIcons = {
|
|
5
|
+
backup: { emoji: "💾", icon: "save-outline" },
|
|
6
|
+
theme: { emoji: "🎨", icon: "contrast-outline" },
|
|
7
|
+
update: { emoji: "⬇️", icon: "download-outline" },
|
|
8
|
+
about: { emoji: "ℹ️", icon: "information-circle-outline" },
|
|
9
|
+
contact: { emoji: "✉️", icon: "chatbubble-ellipses-outline" },
|
|
10
|
+
privacy: { emoji: "🔒", icon: "lock-closed-outline" },
|
|
11
|
+
};
|
|
4
12
|
const defaultStyles = {
|
|
5
13
|
container: { gap: 10 },
|
|
6
14
|
row: {
|
|
@@ -14,6 +22,7 @@ const defaultStyles = {
|
|
|
14
22
|
},
|
|
15
23
|
titleColumn: { flex: 1 },
|
|
16
24
|
emoji: { fontSize: 24 },
|
|
25
|
+
iconColor: "#111827",
|
|
17
26
|
title: {
|
|
18
27
|
fontSize: 16,
|
|
19
28
|
fontWeight: "600",
|
|
@@ -22,11 +31,24 @@ const defaultStyles = {
|
|
|
22
31
|
hint: { fontSize: 12, color: "#6b7280", marginTop: 2 },
|
|
23
32
|
chevronColor: "#9ca3af",
|
|
24
33
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
function resolveIcon(item, defaultIconKind) {
|
|
35
|
+
if (item.icon)
|
|
36
|
+
return { icon: item.icon, emoji: undefined };
|
|
37
|
+
if (item.emoji)
|
|
38
|
+
return { icon: undefined, emoji: item.emoji };
|
|
39
|
+
const fallback = defaultSectionIcons[item.key];
|
|
40
|
+
if (!fallback)
|
|
41
|
+
return { icon: undefined, emoji: undefined };
|
|
42
|
+
return defaultIconKind === "icon"
|
|
43
|
+
? { icon: fallback.icon, emoji: undefined }
|
|
44
|
+
: { icon: undefined, emoji: fallback.emoji };
|
|
45
|
+
}
|
|
46
|
+
// 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
|
|
48
|
+
// navigation works — each item carries its own `onPress` (e.g.
|
|
49
|
+
// `() => router.push("/settings/xxx")`), so this doesn't depend on
|
|
50
|
+
// expo-router or any other specific navigator.
|
|
51
|
+
export function SettingsMenu({ items, defaultIconKind = "emoji", showHints = true, styles, }) {
|
|
30
52
|
const merged = {
|
|
31
53
|
container: [defaultStyles.container, styles?.container],
|
|
32
54
|
row: [defaultStyles.row, styles?.row],
|
|
@@ -36,5 +58,9 @@ export function SettingsMenu({ items, styles }) {
|
|
|
36
58
|
hint: [defaultStyles.hint, styles?.hint],
|
|
37
59
|
};
|
|
38
60
|
const chevronColor = styles?.chevronColor ?? defaultStyles.chevronColor;
|
|
39
|
-
|
|
61
|
+
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
|
+
}) }));
|
|
40
66
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
2
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
2
3
|
import type { ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
|
|
3
4
|
export interface ThemeSettingsScreenEmojis {
|
|
4
5
|
light?: string;
|
|
5
6
|
dark?: string;
|
|
6
7
|
system?: string;
|
|
7
8
|
}
|
|
9
|
+
export interface ThemeSettingsScreenIcons {
|
|
10
|
+
light?: keyof typeof Ionicons.glyphMap;
|
|
11
|
+
dark?: keyof typeof Ionicons.glyphMap;
|
|
12
|
+
system?: keyof typeof Ionicons.glyphMap;
|
|
13
|
+
}
|
|
8
14
|
export interface ThemeSettingsScreenLabels {
|
|
9
15
|
light?: string;
|
|
10
16
|
dark?: string;
|
|
@@ -17,16 +23,29 @@ export interface ThemeSettingsScreenStyles {
|
|
|
17
23
|
row?: StyleProp<ViewStyle>;
|
|
18
24
|
rowActive?: StyleProp<ViewStyle>;
|
|
19
25
|
emoji?: StyleProp<TextStyle>;
|
|
26
|
+
iconColor?: string;
|
|
27
|
+
iconColorActive?: string;
|
|
20
28
|
label?: StyleProp<TextStyle>;
|
|
21
29
|
labelActive?: StyleProp<TextStyle>;
|
|
22
30
|
check?: StyleProp<TextStyle>;
|
|
31
|
+
checkColor?: string;
|
|
23
32
|
}
|
|
24
33
|
export interface ThemeSettingsScreenProps {
|
|
25
34
|
value: ThemePreference;
|
|
26
35
|
onChange: (preference: ThemePreference) => void;
|
|
36
|
+
/**
|
|
37
|
+
* `"emoji"` (default): colorful emoji + a plain-text `✓`. `"icon"`:
|
|
38
|
+
* `Ionicons` glyphs in one shared color (`styles.iconColor`) + an
|
|
39
|
+
* `Ionicons` checkmark, for a flatter, more uniform look — matches
|
|
40
|
+
* `SettingsMenu`'s `defaultIconKind`.
|
|
41
|
+
*/
|
|
42
|
+
variant?: "emoji" | "icon";
|
|
27
43
|
emojis?: ThemeSettingsScreenEmojis;
|
|
44
|
+
icons?: ThemeSettingsScreenIcons;
|
|
28
45
|
labels?: ThemeSettingsScreenLabels;
|
|
46
|
+
/** Set to `false` to hide the hint above the list. Defaults to `true`. */
|
|
47
|
+
showHint?: boolean;
|
|
29
48
|
styles?: ThemeSettingsScreenStyles;
|
|
30
49
|
}
|
|
31
|
-
export declare function ThemeSettingsScreen({ value, onChange, emojis, labels, styles, }: ThemeSettingsScreenProps): import("react").JSX.Element;
|
|
50
|
+
export declare function ThemeSettingsScreen({ value, onChange, variant, emojis, icons, labels, showHint, styles, }: ThemeSettingsScreenProps): import("react").JSX.Element;
|
|
32
51
|
//# sourceMappingURL=ThemeSettingsScreen.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ThemeSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ThemeSettingsScreen.d.ts","sourceRoot":"","sources":["../../../src/components/settings/ThemeSettingsScreen.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,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAE9E,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACvC,IAAI,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,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,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC;AA8DD,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,OAAiB,EACjB,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAe,EACf,MAAM,GACP,EAAE,wBAAwB,+BAiE1B"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Pressable, Text, View } from "react-native";
|
|
3
|
+
import { Ionicons } from "@expo/vector-icons";
|
|
3
4
|
const OPTIONS = ["light", "dark", "system"];
|
|
4
5
|
const defaultEmojis = {
|
|
5
6
|
light: "☀️",
|
|
6
7
|
dark: "🌙",
|
|
7
8
|
system: "⚙️",
|
|
8
9
|
};
|
|
10
|
+
const defaultIcons = {
|
|
11
|
+
light: "sunny-outline",
|
|
12
|
+
dark: "moon-outline",
|
|
13
|
+
system: "phone-portrait-outline",
|
|
14
|
+
};
|
|
9
15
|
const defaultLabels = {
|
|
10
16
|
light: "Clair",
|
|
11
17
|
dark: "Sombre",
|
|
@@ -29,6 +35,8 @@ const defaultStyles = {
|
|
|
29
35
|
backgroundColor: "#2563eb1a",
|
|
30
36
|
},
|
|
31
37
|
emoji: { fontSize: 20 },
|
|
38
|
+
iconColor: "#111827",
|
|
39
|
+
iconColorActive: "#2563eb",
|
|
32
40
|
label: {
|
|
33
41
|
flex: 1,
|
|
34
42
|
fontSize: 15,
|
|
@@ -41,12 +49,14 @@ const defaultStyles = {
|
|
|
41
49
|
fontWeight: "700",
|
|
42
50
|
color: "#2563eb",
|
|
43
51
|
},
|
|
52
|
+
checkColor: "#2563eb",
|
|
44
53
|
};
|
|
45
|
-
// Selectable list of rows (
|
|
46
|
-
// theme preference — same data contract as `ThemeOptionList`, but
|
|
47
|
-
//
|
|
48
|
-
// your app; both cover the exact
|
|
49
|
-
|
|
54
|
+
// Selectable list of rows (icon + label + checkmark) for a light/dark/system
|
|
55
|
+
// theme preference — same data contract as `ThemeOptionList`, but with a
|
|
56
|
+
// second visual (`variant="emoji"`, the default) alongside the vector-icon
|
|
57
|
+
// one (`variant="icon"`). Pick whichever fits your app; both cover the exact
|
|
58
|
+
// same `light`/`dark`/`system` preference.
|
|
59
|
+
export function ThemeSettingsScreen({ value, onChange, variant = "emoji", emojis, icons, labels, showHint = true, styles, }) {
|
|
50
60
|
// Style fields are merged as arrays (default, then override) so a partial
|
|
51
61
|
// override only changes the properties it specifies instead of replacing
|
|
52
62
|
// the whole default style object.
|
|
@@ -61,11 +71,16 @@ export function ThemeSettingsScreen({ value, onChange, emojis, labels, styles, }
|
|
|
61
71
|
check: [defaultStyles.check, styles?.check],
|
|
62
72
|
};
|
|
63
73
|
const e = { ...defaultEmojis, ...emojis };
|
|
74
|
+
const i = { ...defaultIcons, ...icons };
|
|
64
75
|
const t = { ...defaultLabels, ...labels };
|
|
65
|
-
|
|
76
|
+
const iconColor = styles?.iconColor ?? defaultStyles.iconColor;
|
|
77
|
+
const iconColorActive = styles?.iconColorActive ?? defaultStyles.iconColorActive;
|
|
78
|
+
const checkColor = styles?.checkColor ?? defaultStyles.checkColor;
|
|
79
|
+
return (_jsxs(View, { style: merged.container, children: [showHint && _jsx(Text, { style: merged.hint, children: t.hint }), OPTIONS.map((option) => {
|
|
66
80
|
const active = option === value;
|
|
67
81
|
return (_jsxs(Pressable, { style: [merged.row, active && merged.rowActive], onPress: () => {
|
|
68
82
|
onChange(option);
|
|
69
|
-
}, accessibilityRole: "radio", accessibilityState: { checked: active }, accessibilityLabel: t[option], children: [_jsx(Text, { style: merged.emoji, children: e[option] }), _jsx(Text, { style: [merged.label, active && merged.labelActive], children: t[option] }), active &&
|
|
83
|
+
}, accessibilityRole: "radio", accessibilityState: { checked: active }, accessibilityLabel: t[option], children: [variant === "icon" ? (_jsx(Ionicons, { name: i[option], size: 20, color: active ? iconColorActive : iconColor })) : (_jsx(Text, { style: merged.emoji, children: e[option] })), _jsx(Text, { style: [merged.label, active && merged.labelActive], children: t[option] }), active &&
|
|
84
|
+
(variant === "icon" ? (_jsx(Ionicons, { name: "checkmark-circle", size: 20, color: checkColor })) : (_jsx(Text, { style: merged.check, children: "\u2713" })))] }, option));
|
|
70
85
|
})] }));
|
|
71
86
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forthtilliath/react-native-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"react-test-renderer": "19.2.3",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"vitest": "^4.1.10",
|
|
47
|
-
"@forthtilliath/
|
|
48
|
-
"@forthtilliath/
|
|
47
|
+
"@forthtilliath/eslint-config": "0.0.0",
|
|
48
|
+
"@forthtilliath/typescript-config": "0.0.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"lint": "eslint . --max-warnings 0",
|