@forthtilliath/react-native-kit 0.15.1 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +103 -3
  2. package/dist/components/feedback/ErrorFallback.d.ts +63 -0
  3. package/dist/components/feedback/ErrorFallback.d.ts.map +1 -0
  4. package/dist/components/feedback/ErrorFallback.js +86 -0
  5. package/dist/components/feedback/index.d.ts +2 -0
  6. package/dist/components/feedback/index.d.ts.map +1 -0
  7. package/dist/components/feedback/index.js +1 -0
  8. package/dist/components/index.d.ts +2 -0
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components/index.js +2 -0
  11. package/dist/components/list/AddButton.d.ts +27 -0
  12. package/dist/components/list/AddButton.d.ts.map +1 -0
  13. package/dist/components/list/AddButton.js +29 -0
  14. package/dist/components/list/DragHandle.d.ts +35 -0
  15. package/dist/components/list/DragHandle.d.ts.map +1 -0
  16. package/dist/components/list/DragHandle.js +31 -0
  17. package/dist/components/list/SwipeableRow.d.ts +18 -4
  18. package/dist/components/list/SwipeableRow.d.ts.map +1 -1
  19. package/dist/components/list/SwipeableRow.js +40 -13
  20. package/dist/components/list/index.d.ts +2 -0
  21. package/dist/components/list/index.d.ts.map +1 -1
  22. package/dist/components/list/index.js +2 -0
  23. package/dist/components/overlay/BottomSheet.d.ts +45 -0
  24. package/dist/components/overlay/BottomSheet.d.ts.map +1 -0
  25. package/dist/components/overlay/BottomSheet.js +48 -0
  26. package/dist/components/overlay/index.d.ts +2 -0
  27. package/dist/components/overlay/index.d.ts.map +1 -0
  28. package/dist/components/overlay/index.js +1 -0
  29. package/dist/components/picker/CheckboxGroup.d.ts +47 -0
  30. package/dist/components/picker/CheckboxGroup.d.ts.map +1 -0
  31. package/dist/components/picker/CheckboxGroup.js +50 -0
  32. package/dist/components/picker/MonthDayGrid.d.ts +58 -0
  33. package/dist/components/picker/MonthDayGrid.d.ts.map +1 -0
  34. package/dist/components/picker/MonthDayGrid.js +73 -0
  35. package/dist/components/picker/OptionsModal.d.ts +45 -0
  36. package/dist/components/picker/OptionsModal.d.ts.map +1 -0
  37. package/dist/components/picker/OptionsModal.js +45 -0
  38. package/dist/components/picker/QuantityStepper.d.ts +63 -0
  39. package/dist/components/picker/QuantityStepper.d.ts.map +1 -0
  40. package/dist/components/picker/QuantityStepper.js +101 -0
  41. package/dist/components/picker/SegmentedToggle.d.ts +52 -0
  42. package/dist/components/picker/SegmentedToggle.d.ts.map +1 -0
  43. package/dist/components/picker/SegmentedToggle.js +48 -0
  44. package/dist/components/picker/SelectField.d.ts +45 -0
  45. package/dist/components/picker/SelectField.d.ts.map +1 -0
  46. package/dist/components/picker/SelectField.js +54 -0
  47. package/dist/components/picker/index.d.ts +6 -0
  48. package/dist/components/picker/index.d.ts.map +1 -1
  49. package/dist/components/picker/index.js +6 -0
  50. package/dist/components/settings/SettingsCard.d.ts +63 -0
  51. package/dist/components/settings/SettingsCard.d.ts.map +1 -0
  52. package/dist/components/settings/SettingsCard.js +68 -0
  53. package/dist/components/settings/SwitchRow.d.ts +48 -0
  54. package/dist/components/settings/SwitchRow.d.ts.map +1 -0
  55. package/dist/components/settings/SwitchRow.js +44 -0
  56. package/dist/components/settings/index.d.ts +2 -0
  57. package/dist/components/settings/index.d.ts.map +1 -1
  58. package/dist/components/settings/index.js +2 -0
  59. package/dist/components/theme/ThemePreferenceProvider.d.ts +39 -0
  60. package/dist/components/theme/ThemePreferenceProvider.d.ts.map +1 -0
  61. package/dist/components/theme/ThemePreferenceProvider.js +53 -0
  62. package/dist/components/theme/index.d.ts +2 -0
  63. package/dist/components/theme/index.d.ts.map +1 -1
  64. package/dist/components/theme/index.js +2 -0
  65. package/dist/components/theme/themePreferenceContext.d.ts +20 -0
  66. package/dist/components/theme/themePreferenceContext.d.ts.map +1 -0
  67. package/dist/components/theme/themePreferenceContext.js +21 -0
  68. package/dist/index.d.ts +20 -0
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +20 -0
  71. package/dist/utils/calendar/index.d.ts +3 -0
  72. package/dist/utils/calendar/index.d.ts.map +1 -0
  73. package/dist/utils/calendar/index.js +2 -0
  74. package/dist/utils/calendar/syncDedicatedCalendar.d.ts +37 -0
  75. package/dist/utils/calendar/syncDedicatedCalendar.d.ts.map +1 -0
  76. package/dist/utils/calendar/syncDedicatedCalendar.js +42 -0
  77. package/dist/utils/calendar/toCalendarEventDetails.d.ts +32 -0
  78. package/dist/utils/calendar/toCalendarEventDetails.d.ts.map +1 -0
  79. package/dist/utils/calendar/toCalendarEventDetails.js +47 -0
  80. package/dist/utils/files/appFiles.d.ts +61 -0
  81. package/dist/utils/files/appFiles.d.ts.map +1 -0
  82. package/dist/utils/files/appFiles.js +126 -0
  83. package/dist/utils/files/imageThumbnail.d.ts +22 -0
  84. package/dist/utils/files/imageThumbnail.d.ts.map +1 -0
  85. package/dist/utils/files/imageThumbnail.js +28 -0
  86. package/dist/utils/files/index.d.ts +3 -0
  87. package/dist/utils/files/index.d.ts.map +1 -0
  88. package/dist/utils/files/index.js +2 -0
  89. package/dist/utils/helpers/haptics.d.ts +9 -0
  90. package/dist/utils/helpers/haptics.d.ts.map +1 -0
  91. package/dist/utils/helpers/haptics.js +28 -0
  92. package/dist/utils/helpers/index.d.ts +5 -0
  93. package/dist/utils/helpers/index.d.ts.map +1 -1
  94. package/dist/utils/helpers/index.js +5 -0
  95. package/dist/utils/helpers/isNativeModuleAvailable.d.ts +12 -0
  96. package/dist/utils/helpers/isNativeModuleAvailable.d.ts.map +1 -0
  97. package/dist/utils/helpers/isNativeModuleAvailable.js +15 -0
  98. package/dist/utils/helpers/shareFile.d.ts +23 -0
  99. package/dist/utils/helpers/shareFile.d.ts.map +1 -0
  100. package/dist/utils/helpers/shareFile.js +26 -0
  101. package/dist/utils/helpers/shareTextFile.d.ts +16 -0
  102. package/dist/utils/helpers/shareTextFile.d.ts.map +1 -0
  103. package/dist/utils/helpers/shareTextFile.js +23 -0
  104. package/dist/utils/helpers/shareViewAsImage.d.ts +21 -0
  105. package/dist/utils/helpers/shareViewAsImage.d.ts.map +1 -0
  106. package/dist/utils/helpers/shareViewAsImage.js +32 -0
  107. package/dist/utils/index.d.ts +2 -0
  108. package/dist/utils/index.d.ts.map +1 -1
  109. package/dist/utils/index.js +2 -0
  110. package/package.json +58 -7
@@ -0,0 +1,52 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ /** Per-slot style overrides for {@link SegmentedToggle}, each merged after its default. */
3
+ export interface SegmentedToggleStyles {
4
+ /** The row holding the buttons. */
5
+ row?: StyleProp<ViewStyle>;
6
+ /** Every button (equal widths through `flex: 1`). */
7
+ button?: StyleProp<ViewStyle>;
8
+ /** Added on top of `button` for the active one. */
9
+ buttonActive?: StyleProp<ViewStyle>;
10
+ /** Every button's label. */
11
+ text?: StyleProp<TextStyle>;
12
+ /** Added on top of `text` for the active one. */
13
+ textActive?: StyleProp<TextStyle>;
14
+ /** Every option's `hint` line. */
15
+ hint?: StyleProp<TextStyle>;
16
+ /** Added on top of `hint` for the active one. */
17
+ hintActive?: StyleProp<TextStyle>;
18
+ }
19
+ export interface SegmentedToggleOption<T extends string> {
20
+ value: T;
21
+ label: string;
22
+ /** A smaller line under the label (e.g. "2 × 2 joueurs"). */
23
+ hint?: string;
24
+ }
25
+ export interface SegmentedToggleProps<T extends string> {
26
+ options: readonly SegmentedToggleOption<T>[];
27
+ /** The active option's value. */
28
+ value: T;
29
+ onChange: (value: T) => void;
30
+ /** What the choice is about, read by screen readers on the group. */
31
+ label?: string;
32
+ /** Per-slot style overrides — see {@link SegmentedToggleStyles}. */
33
+ styles?: SegmentedToggleStyles;
34
+ }
35
+ /**
36
+ * A row of equal-width buttons of which exactly one is active — a view
37
+ * switch such as Calendar / List. Announced as a radio group.
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * import { SegmentedToggle } from "@forthtilliath/react-native-kit/components/picker/SegmentedToggle";
42
+ *
43
+ * <SegmentedToggle
44
+ * label="Affichage"
45
+ * options={[{ value: "calendar", label: "Calendrier" }, { value: "list", label: "Liste" }]}
46
+ * value={view}
47
+ * onChange={setView}
48
+ * />
49
+ * ```
50
+ */
51
+ export declare function SegmentedToggle<T extends string>({ options, value, onChange, label, styles, }: SegmentedToggleProps<T>): import("react").JSX.Element;
52
+ //# sourceMappingURL=SegmentedToggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentedToggle.d.ts","sourceRoot":"","sources":["../../../src/components/picker/SegmentedToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,2FAA2F;AAC3F,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,qDAAqD;IACrD,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,mDAAmD;IACnD,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,4BAA4B;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,iDAAiD;IACjD,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,kCAAkC;IAClC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,iDAAiD;IACjD,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,SAAS,MAAM;IACrD,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,MAAM;IACpD,OAAO,EAAE,SAAS,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,iCAAiC;IACjC,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAuBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,EAChD,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAK,EACL,MAAM,GACP,EAAE,oBAAoB,CAAC,CAAC,CAAC,+BAqCzB"}
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, Text, View } from "react-native";
3
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
4
+ const defaultStyles = {
5
+ row: { flexDirection: "row", gap: 8, marginBottom: 12 },
6
+ button: {
7
+ flex: 1,
8
+ paddingVertical: 8,
9
+ paddingHorizontal: 14,
10
+ borderRadius: 8,
11
+ borderWidth: 1,
12
+ borderColor: "#d4d4d4",
13
+ alignItems: "center",
14
+ },
15
+ buttonActive: {
16
+ backgroundColor: "#2563eb",
17
+ borderColor: "#2563eb",
18
+ },
19
+ text: { fontWeight: "600", color: "#111111" },
20
+ textActive: { color: "#ffffff" },
21
+ hint: { fontSize: 12, marginTop: 2, color: "#6b7280" },
22
+ hintActive: { color: "#dbeafe" },
23
+ };
24
+ /**
25
+ * A row of equal-width buttons of which exactly one is active — a view
26
+ * switch such as Calendar / List. Announced as a radio group.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * import { SegmentedToggle } from "@forthtilliath/react-native-kit/components/picker/SegmentedToggle";
31
+ *
32
+ * <SegmentedToggle
33
+ * label="Affichage"
34
+ * options={[{ value: "calendar", label: "Calendrier" }, { value: "list", label: "Liste" }]}
35
+ * value={view}
36
+ * onChange={setView}
37
+ * />
38
+ * ```
39
+ */
40
+ export function SegmentedToggle({ options, value, onChange, label, styles, }) {
41
+ const merged = mergeSlotStyles(defaultStyles, styles);
42
+ return (_jsx(View, { style: merged.row, accessibilityRole: "radiogroup", accessibilityLabel: label, children: options.map((option) => {
43
+ const active = option.value === value;
44
+ return (_jsxs(Pressable, { style: [merged.button, active && merged.buttonActive], accessibilityRole: "radio", accessibilityState: { selected: active, checked: active }, accessibilityLabel: option.hint ? `${option.label}, ${option.hint}` : option.label, onPress: () => {
45
+ onChange(option.value);
46
+ }, children: [_jsx(Text, { style: [merged.text, active && merged.textActive], children: option.label }), option.hint !== undefined && (_jsx(Text, { style: [merged.hint, active && merged.hintActive], children: option.hint }))] }, option.value));
47
+ }) }));
48
+ }
@@ -0,0 +1,45 @@
1
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import { type OptionsModalStyles, type SelectOption } from "./OptionsModal.js";
3
+ /** Per-slot style overrides for {@link SelectField}, each merged after its default. */
4
+ export interface SelectFieldStyles {
5
+ /** The outer wrapper (`flex: 1`, so two fields share a row evenly). */
6
+ container?: StyleProp<ViewStyle>;
7
+ /** The small caption above the button. */
8
+ label?: StyleProp<TextStyle>;
9
+ /** The bordered button showing the current value. */
10
+ button?: StyleProp<ViewStyle>;
11
+ /** The current value's text. */
12
+ buttonText?: StyleProp<TextStyle>;
13
+ /** The "▾" on the right. */
14
+ chevron?: StyleProp<TextStyle>;
15
+ /** Forwarded to the {@link OptionsModal} opened by the button. */
16
+ optionsModal?: OptionsModalStyles;
17
+ }
18
+ export interface SelectFieldProps<T extends string | number> {
19
+ /** Caption above the button, e.g. "Mois". */
20
+ label: string;
21
+ /** Text of the current value, shown in the button. */
22
+ valueLabel: string;
23
+ options: readonly SelectOption<T>[];
24
+ onSelect: (value: T) => void;
25
+ /** Per-slot style overrides — see {@link SelectFieldStyles}. */
26
+ styles?: SelectFieldStyles;
27
+ }
28
+ /**
29
+ * A native-looking "select": a captioned button showing the current value,
30
+ * opening an {@link OptionsModal} to pick another one.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * import { SelectField } from "@forthtilliath/react-native-kit/components/picker/SelectField";
35
+ *
36
+ * <SelectField
37
+ * label="Mois"
38
+ * valueLabel={MONTH_NAMES[month - 1]}
39
+ * options={MONTH_NAMES.map((name, i) => ({ value: i + 1, label: name }))}
40
+ * onSelect={setMonth}
41
+ * />
42
+ * ```
43
+ */
44
+ export declare function SelectField<T extends string | number>({ label, valueLabel, options, onSelect, styles, }: SelectFieldProps<T>): import("react").JSX.Element;
45
+ //# sourceMappingURL=SelectField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectField.d.ts","sourceRoot":"","sources":["../../../src/components/picker/SelectField.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKpE,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,uFAAuF;AACvF,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,qDAAqD;IACrD,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,gCAAgC;IAChC,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,4BAA4B;IAC5B,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC/B,kEAAkE;IAClE,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACzD,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,gEAAgE;IAChE,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AA2BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,EACrD,KAAK,EACL,UAAU,EACV,OAAO,EACP,QAAQ,EACR,MAAM,GACP,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BA6BrB"}
@@ -0,0 +1,54 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { Pressable, Text, View } from "react-native";
4
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
5
+ import { OptionsModal, } from "./OptionsModal.js";
6
+ const defaultStyles = {
7
+ container: { flex: 1 },
8
+ label: {
9
+ fontSize: 12,
10
+ opacity: 0.7,
11
+ marginBottom: 4,
12
+ color: "#111111",
13
+ },
14
+ button: {
15
+ flexDirection: "row",
16
+ alignItems: "center",
17
+ justifyContent: "space-between",
18
+ borderWidth: 1,
19
+ borderColor: "#d4d4d4",
20
+ borderRadius: 8,
21
+ padding: 10,
22
+ },
23
+ buttonText: {
24
+ fontSize: 15,
25
+ fontWeight: "600",
26
+ color: "#111111",
27
+ },
28
+ chevron: { opacity: 0.5, color: "#111111" },
29
+ };
30
+ /**
31
+ * A native-looking "select": a captioned button showing the current value,
32
+ * opening an {@link OptionsModal} to pick another one.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * import { SelectField } from "@forthtilliath/react-native-kit/components/picker/SelectField";
37
+ *
38
+ * <SelectField
39
+ * label="Mois"
40
+ * valueLabel={MONTH_NAMES[month - 1]}
41
+ * options={MONTH_NAMES.map((name, i) => ({ value: i + 1, label: name }))}
42
+ * onSelect={setMonth}
43
+ * />
44
+ * ```
45
+ */
46
+ export function SelectField({ label, valueLabel, options, onSelect, styles, }) {
47
+ const [open, setOpen] = useState(false);
48
+ const merged = mergeSlotStyles(defaultStyles, styles);
49
+ return (_jsxs(View, { style: merged.container, children: [_jsx(Text, { style: merged.label, children: label }), _jsxs(Pressable, { style: merged.button, accessibilityRole: "button", accessibilityLabel: `${label} : ${valueLabel}`, onPress: () => {
50
+ setOpen(true);
51
+ }, children: [_jsx(Text, { style: merged.buttonText, children: valueLabel }), _jsx(Text, { style: merged.chevron, children: "\u25BE" })] }), _jsx(OptionsModal, { visible: open, onClose: () => {
52
+ setOpen(false);
53
+ }, options: options, onSelect: onSelect, styles: styles?.optionsModal })] }));
54
+ }
@@ -1,4 +1,10 @@
1
+ export * from "./CheckboxGroup.js";
2
+ export * from "./MonthDayGrid.js";
3
+ export * from "./OptionsModal.js";
1
4
  export * from "./PhotoPicker.js";
2
5
  export * from "./PickerModal.js";
6
+ export * from "./QuantityStepper.js";
7
+ export * from "./SegmentedToggle.js";
8
+ export * from "./SelectField.js";
3
9
  export * from "./VoiceSearchButton.js";
4
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/picker/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/picker/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,wBAAwB,CAAC"}
@@ -1,3 +1,9 @@
1
+ export * from "./CheckboxGroup.js";
2
+ export * from "./MonthDayGrid.js";
3
+ export * from "./OptionsModal.js";
1
4
  export * from "./PhotoPicker.js";
2
5
  export * from "./PickerModal.js";
6
+ export * from "./QuantityStepper.js";
7
+ export * from "./SegmentedToggle.js";
8
+ export * from "./SelectField.js";
3
9
  export * from "./VoiceSearchButton.js";
@@ -0,0 +1,63 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ColorValue, StyleProp, TextStyle, ViewStyle } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ type IconName = keyof typeof Ionicons.glyphMap;
5
+ /** Per-slot style overrides for {@link SettingsCard}, each merged after its default. */
6
+ export interface SettingsCardStyles {
7
+ card?: StyleProp<ViewStyle>;
8
+ /** The row holding the icon and title. */
9
+ header?: StyleProp<ViewStyle>;
10
+ title?: StyleProp<TextStyle>;
11
+ }
12
+ export interface SettingsCardProps {
13
+ title: string;
14
+ /** `Ionicons` glyph before the title. */
15
+ icon?: IconName;
16
+ /** @defaultValue "#2563eb" */
17
+ iconColor?: ColorValue;
18
+ /** Free content: `SettingsCardStatus` lines, `SettingsCardHint`, buttons… */
19
+ children?: ReactNode;
20
+ styles?: SettingsCardStyles;
21
+ }
22
+ /**
23
+ * A settings card: a header (icon + title), then free content — typically
24
+ * status lines, a hint and action buttons (backup, sync, account…).
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * import {
29
+ * SettingsCard,
30
+ * SettingsCardHint,
31
+ * SettingsCardStatus,
32
+ * } from "@forthtilliath/react-native-kit/components/settings/SettingsCard";
33
+ *
34
+ * <SettingsCard icon="cloud-outline" title="Google Drive">
35
+ * <SettingsCardStatus icon="person-circle-outline">{email}</SettingsCardStatus>
36
+ * <SettingsCardStatus icon="time-outline">Dernier envoi : hier à 21:42</SettingsCardStatus>
37
+ * <SettingsCardHint>Copie privée, invisible dans ton Drive.</SettingsCardHint>
38
+ * </SettingsCard>
39
+ * ```
40
+ */
41
+ export declare function SettingsCard({ title, icon, iconColor, children, styles, }: SettingsCardProps): import("react").JSX.Element;
42
+ /** Per-slot style overrides for {@link SettingsCardStatus}. */
43
+ export interface SettingsCardStatusStyles {
44
+ row?: StyleProp<ViewStyle>;
45
+ text?: StyleProp<TextStyle>;
46
+ }
47
+ export interface SettingsCardStatusProps {
48
+ icon: IconName;
49
+ /** E.g. green for "connected", red for an error. @defaultValue "#6b7280" */
50
+ iconColor?: ColorValue;
51
+ children: ReactNode;
52
+ styles?: SettingsCardStatusStyles;
53
+ }
54
+ /** A status line of a card: small icon, then text (linked account, last backup…). */
55
+ export declare function SettingsCardStatus({ icon, iconColor, children, styles, }: SettingsCardStatusProps): import("react").JSX.Element;
56
+ export interface SettingsCardHintProps {
57
+ children: ReactNode;
58
+ style?: StyleProp<TextStyle>;
59
+ }
60
+ /** Explanatory text of a card, quieter than its status lines. */
61
+ export declare function SettingsCardHint({ children, style }: SettingsCardHintProps): import("react").JSX.Element;
62
+ export {};
63
+ //# sourceMappingURL=SettingsCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/SettingsCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAI9C,KAAK,QAAQ,GAAG,MAAM,OAAO,QAAQ,CAAC,QAAQ,CAAC;AAE/C,wFAAwF;AACxF,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,8BAA8B;IAC9B,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAsBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,IAAI,EACJ,SAAqB,EACrB,QAAQ,EACR,MAAM,GACP,EAAE,iBAAiB,+BAgBnB;AAED,+DAA+D;AAC/D,MAAM,WAAW,wBAAwB;IACvC,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,IAAI,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,QAAQ,CAAC;IACf,4EAA4E;IAC5E,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC;AAWD,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,SAAqB,EACrB,QAAQ,EACR,MAAM,GACP,EAAE,uBAAuB,+BASzB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAQD,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,qBAAqB,+BAE1E"}
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text, View } from "react-native";
3
+ import { Ionicons } from "@expo/vector-icons";
4
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
5
+ const cardStyles = {
6
+ card: {
7
+ backgroundColor: "#f3f4f6",
8
+ borderRadius: 12,
9
+ padding: 16,
10
+ gap: 8,
11
+ },
12
+ header: {
13
+ flexDirection: "row",
14
+ alignItems: "center",
15
+ gap: 8,
16
+ },
17
+ title: {
18
+ flex: 1,
19
+ fontSize: 16,
20
+ fontWeight: "700",
21
+ color: "#111111",
22
+ },
23
+ };
24
+ /**
25
+ * A settings card: a header (icon + title), then free content — typically
26
+ * status lines, a hint and action buttons (backup, sync, account…).
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * import {
31
+ * SettingsCard,
32
+ * SettingsCardHint,
33
+ * SettingsCardStatus,
34
+ * } from "@forthtilliath/react-native-kit/components/settings/SettingsCard";
35
+ *
36
+ * <SettingsCard icon="cloud-outline" title="Google Drive">
37
+ * <SettingsCardStatus icon="person-circle-outline">{email}</SettingsCardStatus>
38
+ * <SettingsCardStatus icon="time-outline">Dernier envoi : hier à 21:42</SettingsCardStatus>
39
+ * <SettingsCardHint>Copie privée, invisible dans ton Drive.</SettingsCardHint>
40
+ * </SettingsCard>
41
+ * ```
42
+ */
43
+ export function SettingsCard({ title, icon, iconColor = "#2563eb", children, styles, }) {
44
+ const merged = mergeSlotStyles(cardStyles, styles);
45
+ return (_jsxs(View, { style: merged.card, children: [_jsxs(View, { style: merged.header, children: [icon !== undefined && (_jsx(Ionicons, { name: icon, size: 20, color: iconColor })), _jsx(Text, { style: merged.title, accessibilityRole: "header", children: title })] }), children] }));
46
+ }
47
+ const statusStyles = {
48
+ row: {
49
+ flexDirection: "row",
50
+ alignItems: "center",
51
+ gap: 8,
52
+ },
53
+ text: { flex: 1, fontSize: 14, color: "#111111" },
54
+ };
55
+ /** A status line of a card: small icon, then text (linked account, last backup…). */
56
+ export function SettingsCardStatus({ icon, iconColor = "#6b7280", children, styles, }) {
57
+ const merged = mergeSlotStyles(statusStyles, styles);
58
+ return (_jsxs(View, { style: merged.row, children: [_jsx(Ionicons, { name: icon, size: 16, color: iconColor }), _jsx(Text, { style: merged.text, children: children })] }));
59
+ }
60
+ const hintStyle = {
61
+ fontSize: 13,
62
+ color: "#6b7280",
63
+ marginBottom: 4,
64
+ };
65
+ /** Explanatory text of a card, quieter than its status lines. */
66
+ export function SettingsCardHint({ children, style }) {
67
+ return _jsx(Text, { style: [hintStyle, style], children: children });
68
+ }
@@ -0,0 +1,48 @@
1
+ import type { ColorValue, StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ /** Per-slot style overrides for {@link SwitchRow}, each merged after its default. */
3
+ export interface SwitchRowStyles {
4
+ /** The pressable row (border and background included). */
5
+ row?: StyleProp<ViewStyle>;
6
+ /** The column holding the label and description. */
7
+ texts?: StyleProp<ViewStyle>;
8
+ label?: StyleProp<TextStyle>;
9
+ description?: StyleProp<TextStyle>;
10
+ }
11
+ /** Colors of the switch itself — React Native's `Switch` takes no `style` for them. */
12
+ export interface SwitchRowColors {
13
+ trackOff?: ColorValue;
14
+ trackOn?: ColorValue;
15
+ thumbOff?: ColorValue;
16
+ thumbOn?: ColorValue;
17
+ }
18
+ export interface SwitchRowProps {
19
+ label: string;
20
+ /** A line under the label, also read as the screen-reader hint. */
21
+ description?: string;
22
+ value: boolean;
23
+ /** Receives the new value — from the switch or from a tap anywhere on the row. */
24
+ onChange: (value: boolean) => void;
25
+ disabled?: boolean;
26
+ /** Switch colors; unset ones keep the platform default. */
27
+ colors?: SwitchRowColors;
28
+ /** Per-slot style overrides — see {@link SwitchRowStyles}. */
29
+ styles?: SwitchRowStyles;
30
+ }
31
+ /**
32
+ * A settings line: label, optional description, and a switch — the whole
33
+ * row is pressable and announced as one switch by screen readers.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * import { SwitchRow } from "@forthtilliath/react-native-kit/components/settings/SwitchRow";
38
+ *
39
+ * <SwitchRow
40
+ * label="Équipes aléatoires"
41
+ * description="Mélange les joueurs avant de former les équipes"
42
+ * value={randomTeams}
43
+ * onChange={setRandomTeams}
44
+ * />
45
+ * ```
46
+ */
47
+ export declare function SwitchRow({ label, description, value, onChange, disabled, colors, styles, }: SwitchRowProps): import("react").JSX.Element;
48
+ //# sourceMappingURL=SwitchRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwitchRow.d.ts","sourceRoot":"","sources":["../../../src/components/settings/SwitchRow.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKhF,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,GAAG,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3B,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CACpC;AAED,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,kFAAkF;IAClF,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAmBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAW,EACX,KAAK,EACL,QAAQ,EACR,QAAgB,EAChB,MAAM,EACN,MAAM,GACP,EAAE,cAAc,+BAkChB"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Pressable, Switch, Text, View } from "react-native";
3
+ import { mergeSlotStyles } from "../../utils/helpers/mergeSlotStyles.js";
4
+ const defaultStyles = {
5
+ row: {
6
+ flexDirection: "row",
7
+ alignItems: "center",
8
+ gap: 16,
9
+ paddingVertical: 12,
10
+ paddingHorizontal: 16,
11
+ },
12
+ texts: { flex: 1, gap: 2 },
13
+ label: {
14
+ fontSize: 15,
15
+ fontWeight: "600",
16
+ color: "#111111",
17
+ },
18
+ description: { fontSize: 13, color: "#6b7280" },
19
+ };
20
+ /**
21
+ * A settings line: label, optional description, and a switch — the whole
22
+ * row is pressable and announced as one switch by screen readers.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * import { SwitchRow } from "@forthtilliath/react-native-kit/components/settings/SwitchRow";
27
+ *
28
+ * <SwitchRow
29
+ * label="Équipes aléatoires"
30
+ * description="Mélange les joueurs avant de former les équipes"
31
+ * value={randomTeams}
32
+ * onChange={setRandomTeams}
33
+ * />
34
+ * ```
35
+ */
36
+ export function SwitchRow({ label, description, value, onChange, disabled = false, colors, styles, }) {
37
+ const merged = mergeSlotStyles(defaultStyles, styles);
38
+ return (_jsxs(Pressable, { style: merged.row, onPress: () => {
39
+ onChange(!value);
40
+ }, disabled: disabled, accessibilityRole: "switch", accessibilityState: { checked: value, disabled }, accessibilityLabel: label, accessibilityHint: description, children: [_jsxs(View, { style: merged.texts, children: [_jsx(Text, { style: merged.label, children: label }), description !== undefined && (_jsx(Text, { style: merged.description, children: description }))] }), _jsx(Switch, { value: value, onValueChange: onChange, disabled: disabled, trackColor: { false: colors?.trackOff, true: colors?.trackOn }, thumbColor: value ? colors?.thumbOn : colors?.thumbOff,
41
+ // The row already announces the switch: hide this one from screen
42
+ // readers so it isn't read twice.
43
+ importantForAccessibility: "no-hide-descendants", accessibilityElementsHidden: true })] }));
44
+ }
@@ -2,7 +2,9 @@ export * from "./AboutSettingsScreen.js";
2
2
  export * from "./BackupSettingsScreen.js";
3
3
  export * from "./ContactSettingsScreen.js";
4
4
  export * from "./PrivacySettingsScreen.js";
5
+ export * from "./SettingsCard.js";
5
6
  export * from "./SettingsMenu.js";
7
+ export * from "./SwitchRow.js";
6
8
  export * from "./ThemeSettingsScreen.js";
7
9
  export * from "./UpdateSettingsScreen.js";
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/settings/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/settings/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC"}
@@ -2,6 +2,8 @@ export * from "./AboutSettingsScreen.js";
2
2
  export * from "./BackupSettingsScreen.js";
3
3
  export * from "./ContactSettingsScreen.js";
4
4
  export * from "./PrivacySettingsScreen.js";
5
+ export * from "./SettingsCard.js";
5
6
  export * from "./SettingsMenu.js";
7
+ export * from "./SwitchRow.js";
6
8
  export * from "./ThemeSettingsScreen.js";
7
9
  export * from "./UpdateSettingsScreen.js";
@@ -0,0 +1,39 @@
1
+ import { type ReactNode } from "react";
2
+ import type { ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
3
+ export interface ThemePreferenceProviderProps {
4
+ children: ReactNode;
5
+ /**
6
+ * Reads the saved preference once, on mount. Resolve to `null` /
7
+ * `undefined` when nothing is saved yet: `initialPreference` then stays.
8
+ */
9
+ load: () => Promise<ThemePreference | null | undefined>;
10
+ /** Persists a new preference (AsyncStorage, SQLite, a settings row…). */
11
+ save: (preference: ThemePreference) => void | Promise<void>;
12
+ /**
13
+ * Used until `load` resolves, and when nothing is saved.
14
+ *
15
+ * @defaultValue "system"
16
+ */
17
+ initialPreference?: ThemePreference;
18
+ }
19
+ /**
20
+ * Holds the app's light / dark / system theme preference in a context:
21
+ * loaded from storage on mount, changeable from anywhere through
22
+ * `useThemePreference`, persisted on change. Storage-agnostic — the app
23
+ * passes its own `load` / `save`. Pair it with `ThemeSettingsScreen` for the
24
+ * settings screen and `useResolvedScheme` to pick the palette.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * import { ThemePreferenceProvider } from "@forthtilliath/react-native-kit/components/theme/ThemePreferenceProvider";
29
+ *
30
+ * <ThemePreferenceProvider
31
+ * load={async () => (await getSettings()).theme}
32
+ * save={async (theme) => saveSettings({ ...(await getSettings()), theme })}
33
+ * >
34
+ * <App />
35
+ * </ThemePreferenceProvider>
36
+ * ```
37
+ */
38
+ export declare function ThemePreferenceProvider({ children, load, save, initialPreference, }: ThemePreferenceProviderProps): import("react").JSX.Element;
39
+ //# sourceMappingURL=ThemePreferenceProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThemePreferenceProvider.d.ts","sourceRoot":"","sources":["../../../src/components/theme/ThemePreferenceProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAI9E,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,IAAI,EAAE,MAAM,OAAO,CAAC,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACxD,yEAAyE;IACzE,IAAI,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,eAAe,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,iBAA4B,GAC7B,EAAE,4BAA4B,+BAwC9B"}
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
3
+ import { ThemePreferenceContext } from "./themePreferenceContext.js";
4
+ /**
5
+ * Holds the app's light / dark / system theme preference in a context:
6
+ * loaded from storage on mount, changeable from anywhere through
7
+ * `useThemePreference`, persisted on change. Storage-agnostic — the app
8
+ * passes its own `load` / `save`. Pair it with `ThemeSettingsScreen` for the
9
+ * settings screen and `useResolvedScheme` to pick the palette.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { ThemePreferenceProvider } from "@forthtilliath/react-native-kit/components/theme/ThemePreferenceProvider";
14
+ *
15
+ * <ThemePreferenceProvider
16
+ * load={async () => (await getSettings()).theme}
17
+ * save={async (theme) => saveSettings({ ...(await getSettings()), theme })}
18
+ * >
19
+ * <App />
20
+ * </ThemePreferenceProvider>
21
+ * ```
22
+ */
23
+ export function ThemePreferenceProvider({ children, load, save, initialPreference = "system", }) {
24
+ const [preference, setPreference] = useState(initialPreference);
25
+ // Latest callbacks, so inline `load` / `save` props neither re-run the
26
+ // initial load nor go stale.
27
+ const loadRef = useRef(load);
28
+ const saveRef = useRef(save);
29
+ // Set by a user choice made before `load` resolved: the slower stored
30
+ // value must not overwrite it.
31
+ const chosenRef = useRef(false);
32
+ useEffect(() => {
33
+ loadRef.current = load;
34
+ saveRef.current = save;
35
+ });
36
+ useEffect(() => {
37
+ let active = true;
38
+ void loadRef.current().then((stored) => {
39
+ if (active && stored && !chosenRef.current)
40
+ setPreference(stored);
41
+ });
42
+ return () => {
43
+ active = false;
44
+ };
45
+ }, []);
46
+ const choosePreference = useCallback((next) => {
47
+ chosenRef.current = true;
48
+ setPreference(next);
49
+ void saveRef.current(next);
50
+ }, []);
51
+ const value = useMemo(() => ({ preference, setPreference: choosePreference }), [preference, choosePreference]);
52
+ return (_jsx(ThemePreferenceContext, { value: value, children: children }));
53
+ }
@@ -1,3 +1,5 @@
1
1
  export * from "./ThemeOptionList.js";
2
+ export * from "./themePreferenceContext.js";
3
+ export * from "./ThemePreferenceProvider.js";
2
4
  export * from "./ThemeToggle.js";
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kBAAkB,CAAC"}
@@ -1,2 +1,4 @@
1
1
  export * from "./ThemeOptionList.js";
2
+ export * from "./themePreferenceContext.js";
3
+ export * from "./ThemePreferenceProvider.js";
2
4
  export * from "./ThemeToggle.js";
@@ -0,0 +1,20 @@
1
+ import { type ThemePreference } from "../../hooks/useEffectiveColorScheme.js";
2
+ export interface ThemePreferenceContextValue {
3
+ /** The user's choice: light, dark, or follow the system. */
4
+ preference: ThemePreference;
5
+ /** Applies a new choice right away, then persists it through `save`. */
6
+ setPreference: (preference: ThemePreference) => void;
7
+ }
8
+ /**
9
+ * Provided by `ThemePreferenceProvider`, read through
10
+ * {@link useThemePreference}; `"system"` outside a provider.
11
+ */
12
+ export declare const ThemePreferenceContext: import("react").Context<ThemePreferenceContextValue>;
13
+ /** The theme preference and its setter, from the nearest `ThemePreferenceProvider`. */
14
+ export declare function useThemePreference(): ThemePreferenceContextValue;
15
+ /**
16
+ * The scheme actually applied: the chosen one, or the device's when the
17
+ * preference is `"system"`.
18
+ */
19
+ export declare function useResolvedScheme(): "light" | "dark";
20
+ //# sourceMappingURL=themePreferenceContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themePreferenceContext.d.ts","sourceRoot":"","sources":["../../../src/components/theme/themePreferenceContext.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,wCAAwC,CAAC;AAEhD,MAAM,WAAW,2BAA2B;IAC1C,4DAA4D;IAC5D,UAAU,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,aAAa,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;CACtD;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,sDAI/B,CAAC;AAEL,uFAAuF;AACvF,wBAAgB,kBAAkB,IAAI,2BAA2B,CAEhE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,GAAG,MAAM,CAEpD"}