@pi-spi/app 1.5.1 → 2.0.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 (180) hide show
  1. package/README.md +64 -30
  2. package/example/App.tsx +27 -24
  3. package/lib/module/core/assets.js +0 -7
  4. package/lib/module/core/assets.js.map +1 -1
  5. package/lib/module/core/di.js +17 -8
  6. package/lib/module/core/di.js.map +1 -1
  7. package/lib/module/core/pi_spi_custom_theme.js +22 -10
  8. package/lib/module/core/pi_spi_custom_theme.js.map +1 -1
  9. package/lib/module/core/pi_spi_sdk_config.js +22 -8
  10. package/lib/module/core/pi_spi_sdk_config.js.map +1 -1
  11. package/lib/module/core/theme.js +345 -225
  12. package/lib/module/core/theme.js.map +1 -1
  13. package/lib/module/index.js +1 -1
  14. package/lib/module/index.js.map +1 -1
  15. package/lib/module/l10n/app_localizations.js.map +1 -1
  16. package/lib/module/l10n/app_localizations_en.js +26 -1
  17. package/lib/module/l10n/app_localizations_en.js.map +1 -1
  18. package/lib/module/l10n/app_localizations_fr.js +26 -1
  19. package/lib/module/l10n/app_localizations_fr.js.map +1 -1
  20. package/lib/module/l10n/app_localizations_pt.js +26 -1
  21. package/lib/module/l10n/app_localizations_pt.js.map +1 -1
  22. package/lib/module/modules/alias/presentation/pages/alias_delete_btn_widget.js +15 -19
  23. package/lib/module/modules/alias/presentation/pages/alias_delete_btn_widget.js.map +1 -1
  24. package/lib/module/modules/alias/presentation/pages/alias_page_claim.js +4 -11
  25. package/lib/module/modules/alias/presentation/pages/alias_page_claim.js.map +1 -1
  26. package/lib/module/modules/compte/presentation/pages/compte_details_widget.js +6 -7
  27. package/lib/module/modules/compte/presentation/pages/compte_details_widget.js.map +1 -1
  28. package/lib/module/modules/config/domain/models/config_keys.js +3 -1
  29. package/lib/module/modules/config/domain/models/config_keys.js.map +1 -1
  30. package/lib/module/modules/config/domain/services/config_service.js +31 -2
  31. package/lib/module/modules/config/domain/services/config_service.js.map +1 -1
  32. package/lib/module/modules/home/presentation/pages/home_tab_compte.js +0 -1
  33. package/lib/module/modules/home/presentation/pages/home_tab_compte.js.map +1 -1
  34. package/lib/module/modules/home/presentation/pages/home_tab_compte_more_menu.js +38 -37
  35. package/lib/module/modules/home/presentation/pages/home_tab_compte_more_menu.js.map +1 -1
  36. package/lib/module/modules/profile/presentation/pages/compte/profile_compte_page.js +25 -11
  37. package/lib/module/modules/profile/presentation/pages/compte/profile_compte_page.js.map +1 -1
  38. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js +14 -20
  39. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page.js.map +1 -1
  40. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js +109 -103
  41. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.js.map +1 -1
  42. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.js +55 -72
  43. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.js.map +1 -1
  44. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.js +31 -45
  45. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.js.map +1 -1
  46. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js +175 -203
  47. package/lib/module/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.js.map +1 -1
  48. package/lib/module/modules/profile/presentation/pages/profile_app_bar.js +26 -0
  49. package/lib/module/modules/profile/presentation/pages/profile_app_bar.js.map +1 -0
  50. package/lib/module/modules/profile/presentation/pages/profile_menu.js +144 -34
  51. package/lib/module/modules/profile/presentation/pages/profile_menu.js.map +1 -1
  52. package/lib/module/modules/profile/presentation/pages/profile_page.js +95 -63
  53. package/lib/module/modules/profile/presentation/pages/profile_page.js.map +1 -1
  54. package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js +97 -45
  55. package/lib/module/modules/profile/presentation/pages/profile_page_avatar.js.map +1 -1
  56. package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js +9 -8
  57. package/lib/module/modules/profile/presentation/pages/profile_page_contacts.js.map +1 -1
  58. package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js +26 -2
  59. package/lib/module/modules/search/presentation/pages/search_fonctionnalites.js.map +1 -1
  60. package/lib/module/modules/search/presentation/pages/search_page.js +0 -1
  61. package/lib/module/modules/search/presentation/pages/search_page.js.map +1 -1
  62. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_recu.js +3 -1
  63. package/lib/module/modules/transactions/presentation/pages/transaction_details/transaction_details_page_recu.js.map +1 -1
  64. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.js +4 -11
  65. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.js.map +1 -1
  66. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js +13 -10
  67. package/lib/module/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.js.map +1 -1
  68. package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.js +5 -4
  69. package/lib/module/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.js.map +1 -1
  70. package/lib/module/modules/transactions/presentation/pages/transaction_split/transaction_split_page_repartition.js +2 -2
  71. package/lib/module/modules/transactions/presentation/pages/transaction_split/transaction_split_page_repartition.js.map +1 -1
  72. package/lib/module/pi_spi_sdk_entry.js +7 -2
  73. package/lib/module/pi_spi_sdk_entry.js.map +1 -1
  74. package/lib/module/shared/utils/color.js +15 -0
  75. package/lib/module/shared/utils/color.js.map +1 -0
  76. package/lib/module/shared/widgets/custom_progressbar.js +3 -2
  77. package/lib/module/shared/widgets/custom_progressbar.js.map +1 -1
  78. package/lib/module/shared/widgets/error_card_widget.js +70 -0
  79. package/lib/module/shared/widgets/error_card_widget.js.map +1 -0
  80. package/lib/module/shared/widgets/selection.js +136 -0
  81. package/lib/module/shared/widgets/selection.js.map +1 -0
  82. package/lib/typescript/src/core/assets.d.ts +0 -7
  83. package/lib/typescript/src/core/assets.d.ts.map +1 -1
  84. package/lib/typescript/src/core/di.d.ts.map +1 -1
  85. package/lib/typescript/src/core/pi_spi_custom_theme.d.ts +17 -8
  86. package/lib/typescript/src/core/pi_spi_custom_theme.d.ts.map +1 -1
  87. package/lib/typescript/src/core/pi_spi_sdk_config.d.ts +22 -8
  88. package/lib/typescript/src/core/pi_spi_sdk_config.d.ts.map +1 -1
  89. package/lib/typescript/src/core/theme.d.ts +123 -53
  90. package/lib/typescript/src/core/theme.d.ts.map +1 -1
  91. package/lib/typescript/src/index.d.ts +1 -1
  92. package/lib/typescript/src/index.d.ts.map +1 -1
  93. package/lib/typescript/src/l10n/app_localizations.d.ts +50 -0
  94. package/lib/typescript/src/l10n/app_localizations.d.ts.map +1 -1
  95. package/lib/typescript/src/l10n/app_localizations_en.d.ts.map +1 -1
  96. package/lib/typescript/src/l10n/app_localizations_fr.d.ts.map +1 -1
  97. package/lib/typescript/src/l10n/app_localizations_pt.d.ts.map +1 -1
  98. package/lib/typescript/src/modules/alias/presentation/pages/alias_delete_btn_widget.d.ts.map +1 -1
  99. package/lib/typescript/src/modules/alias/presentation/pages/alias_page_claim.d.ts.map +1 -1
  100. package/lib/typescript/src/modules/compte/presentation/pages/compte_details_widget.d.ts.map +1 -1
  101. package/lib/typescript/src/modules/config/domain/models/config_keys.d.ts +3 -1
  102. package/lib/typescript/src/modules/config/domain/models/config_keys.d.ts.map +1 -1
  103. package/lib/typescript/src/modules/config/domain/services/config_service.d.ts +7 -0
  104. package/lib/typescript/src/modules/config/domain/services/config_service.d.ts.map +1 -1
  105. package/lib/typescript/src/modules/home/presentation/pages/home_tab_compte_more_menu.d.ts +2 -0
  106. package/lib/typescript/src/modules/home/presentation/pages/home_tab_compte_more_menu.d.ts.map +1 -1
  107. package/lib/typescript/src/modules/profile/presentation/pages/compte/profile_compte_page.d.ts.map +1 -1
  108. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page.d.ts.map +1 -1
  109. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.d.ts.map +1 -1
  110. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.d.ts.map +1 -1
  111. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.d.ts.map +1 -1
  112. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.d.ts +1 -1
  113. package/lib/typescript/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.d.ts.map +1 -1
  114. package/lib/typescript/src/modules/profile/presentation/pages/profile_app_bar.d.ts +5 -0
  115. package/lib/typescript/src/modules/profile/presentation/pages/profile_app_bar.d.ts.map +1 -0
  116. package/lib/typescript/src/modules/profile/presentation/pages/profile_menu.d.ts +31 -4
  117. package/lib/typescript/src/modules/profile/presentation/pages/profile_menu.d.ts.map +1 -1
  118. package/lib/typescript/src/modules/profile/presentation/pages/profile_page.d.ts +4 -1
  119. package/lib/typescript/src/modules/profile/presentation/pages/profile_page.d.ts.map +1 -1
  120. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts +1 -1
  121. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_avatar.d.ts.map +1 -1
  122. package/lib/typescript/src/modules/profile/presentation/pages/profile_page_contacts.d.ts.map +1 -1
  123. package/lib/typescript/src/modules/search/presentation/pages/search_fonctionnalites.d.ts.map +1 -1
  124. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.d.ts.map +1 -1
  125. package/lib/typescript/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.d.ts.map +1 -1
  126. package/lib/typescript/src/shared/utils/color.d.ts +7 -0
  127. package/lib/typescript/src/shared/utils/color.d.ts.map +1 -0
  128. package/lib/typescript/src/shared/widgets/custom_progressbar.d.ts.map +1 -1
  129. package/lib/typescript/src/shared/widgets/error_card_widget.d.ts +11 -0
  130. package/lib/typescript/src/shared/widgets/error_card_widget.d.ts.map +1 -0
  131. package/lib/typescript/src/shared/widgets/selection.d.ts +21 -0
  132. package/lib/typescript/src/shared/widgets/selection.d.ts.map +1 -0
  133. package/package.json +1 -1
  134. package/src/core/assets.ts +0 -7
  135. package/src/core/di.ts +13 -8
  136. package/src/core/pi_spi_custom_theme.ts +22 -15
  137. package/src/core/pi_spi_sdk_config.ts +27 -9
  138. package/src/core/theme.ts +381 -269
  139. package/src/index.ts +1 -1
  140. package/src/l10n/app_localizations.ts +50 -0
  141. package/src/l10n/app_localizations_en.ts +25 -0
  142. package/src/l10n/app_localizations_fr.ts +25 -0
  143. package/src/l10n/app_localizations_pt.ts +25 -0
  144. package/src/modules/alias/presentation/pages/alias_delete_btn_widget.tsx +13 -12
  145. package/src/modules/alias/presentation/pages/alias_page_claim.tsx +5 -4
  146. package/src/modules/compte/presentation/pages/compte_details_widget.tsx +3 -4
  147. package/src/modules/config/domain/models/config_keys.ts +3 -1
  148. package/src/modules/config/domain/services/config_service.ts +31 -2
  149. package/src/modules/home/presentation/pages/home_tab_compte.tsx +1 -1
  150. package/src/modules/home/presentation/pages/home_tab_compte_more_menu.tsx +34 -42
  151. package/src/modules/profile/presentation/pages/compte/profile_compte_page.tsx +15 -7
  152. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page.tsx +12 -17
  153. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications.tsx +89 -68
  154. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_notifications_son.tsx +84 -54
  155. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_qrcode.tsx +28 -24
  156. package/src/modules/profile/presentation/pages/parametre/profile_parametre_page_theme.tsx +121 -131
  157. package/src/modules/profile/presentation/pages/profile_app_bar.tsx +21 -0
  158. package/src/modules/profile/presentation/pages/profile_menu.tsx +126 -33
  159. package/src/modules/profile/presentation/pages/profile_page.tsx +90 -66
  160. package/src/modules/profile/presentation/pages/profile_page_avatar.tsx +69 -28
  161. package/src/modules/profile/presentation/pages/profile_page_contacts.tsx +5 -4
  162. package/src/modules/search/presentation/pages/search_fonctionnalites.ts +40 -7
  163. package/src/modules/search/presentation/pages/search_page.tsx +1 -1
  164. package/src/modules/transactions/presentation/pages/transaction_details/transaction_details_page_recu.tsx +3 -1
  165. package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page.tsx +2 -4
  166. package/src/modules/transactions/presentation/pages/transaction_rtp/transaction_rtp_page_actions.tsx +13 -6
  167. package/src/modules/transactions/presentation/pages/transaction_search/transaction_search_page_filters.tsx +4 -4
  168. package/src/modules/transactions/presentation/pages/transaction_split/transaction_split_page_repartition.tsx +2 -2
  169. package/src/pi_spi_sdk_entry.tsx +9 -2
  170. package/src/shared/utils/color.ts +14 -0
  171. package/src/shared/widgets/custom_progressbar.tsx +3 -2
  172. package/src/shared/widgets/error_card_widget.tsx +58 -0
  173. package/src/shared/widgets/selection.tsx +98 -0
  174. package/assets/images/theme_light_blue.jpeg +0 -0
  175. package/assets/images/theme_light_green.jpeg +0 -0
  176. package/assets/images/theme_light_orange.jpeg +0 -0
  177. package/assets/images/theme_light_pink.jpeg +0 -0
  178. package/assets/images/theme_light_purple.jpeg +0 -0
  179. package/assets/images/theme_light_red.jpeg +0 -0
  180. package/assets/images/theme_light_yellow.jpeg +0 -0
package/src/core/theme.ts CHANGED
@@ -3,18 +3,11 @@ import { Appearance, type ColorSchemeName, type TextStyle } from 'react-native';
3
3
 
4
4
  import type { AppLocalizations } from '../l10n/app_localizations';
5
5
 
6
- /** Thèmes du SDK. La valeur de chaque membre est sa codification (persistée dans les préférences). */
6
+ /** Thème d'affichage du SDK : celui du téléphone, clair ou sombre. La valeur de chaque membre est sa codification (persistée dans les préférences). */
7
7
  export enum Themes {
8
8
  system = 'System',
9
9
  light = 'lightTheme',
10
10
  dark = 'darkTheme',
11
- yellow = 'yellowTheme',
12
- green = 'greenTheme',
13
- blue = 'blueTheme',
14
- red = 'redTheme',
15
- orange = 'orangeTheme',
16
- purple = 'purpleTheme',
17
- pink = 'pinkTheme',
18
11
  }
19
12
 
20
13
  // eslint-disable-next-line @typescript-eslint/no-namespace
@@ -26,18 +19,7 @@ export namespace Themes {
26
19
  }
27
20
 
28
21
  export function list(): Themes[] {
29
- return [
30
- Themes.system,
31
- Themes.light,
32
- Themes.dark,
33
- Themes.yellow,
34
- Themes.green,
35
- Themes.blue,
36
- Themes.red,
37
- Themes.orange,
38
- Themes.purple,
39
- Themes.pink,
40
- ];
22
+ return [Themes.system, Themes.light, Themes.dark];
41
23
  }
42
24
 
43
25
  /** Libellé traduit du thème. */
@@ -47,26 +29,173 @@ export namespace Themes {
47
29
  return traductions.appSettingPageMenuThemeLight;
48
30
  case Themes.dark:
49
31
  return traductions.appSettingPageMenuThemeDark;
50
- case Themes.yellow:
51
- return traductions.appSettingPageMenuThemeYellow;
52
- case Themes.green:
53
- return traductions.appSettingPageMenuThemeGreen;
54
- case Themes.blue:
55
- return traductions.appSettingPageMenuThemeBlue;
56
- case Themes.red:
57
- return traductions.appSettingPageMenuThemeRed;
58
- case Themes.orange:
59
- return traductions.appSettingPageMenuThemeOrange;
60
- case Themes.purple:
61
- return traductions.appSettingPageMenuThemePurple;
62
- case Themes.pink:
63
- return traductions.appSettingPageMenuThemePink;
64
32
  default:
65
33
  return traductions.appSettingPageMenuThemeDefault;
66
34
  }
67
35
  }
68
36
  }
69
37
 
38
+ /** Teintes d'une couleur d'accentuation pour un thème clair ou sombre. */
39
+ export type AccentTeintes = {
40
+ /** Boutons, interrupteurs, champ actif */
41
+ accent: string;
42
+ /** Texte et icônes posés sur [accent] (noir ou blanc selon le contraste) */
43
+ surAccent: string;
44
+ /** [accent] quand le bouton est pressé */
45
+ appui: string;
46
+ /** Liens et textes en couleur sur le fond de page */
47
+ texte: string;
48
+ /** Fonds teintés : pastilles, puces choisies */
49
+ conteneur: string;
50
+ /** Texte et icônes posés sur [conteneur] */
51
+ surConteneur: string;
52
+ /** Boutons secondaires (feuilles d'erreur, de succès…) */
53
+ tertiaire: string;
54
+ };
55
+
56
+ /**
57
+ * Couleur d'accentuation, indépendante du thème clair ou sombre. Chaque
58
+ * couleur a une teinte pour le clair et une, plus lumineuse, pour le
59
+ * sombre ; contrastes vérifiés (texte ≥ 4,5:1, bouton ≥ 3:1).
60
+ */
61
+ export enum Accents {
62
+ pispi = 'pispi',
63
+ vert = 'vert',
64
+ bleu = 'bleu',
65
+ rouge = 'rouge',
66
+ orange = 'orange',
67
+ violet = 'violet',
68
+ rose = 'rose',
69
+ turquoise = 'turquoise',
70
+ terracotta = 'terracotta',
71
+ bordeaux = 'bordeaux',
72
+ ardoise = 'ardoise',
73
+ }
74
+
75
+ const teintes = (
76
+ accent: string,
77
+ surAccent: string,
78
+ appui: string,
79
+ texte: string,
80
+ conteneur: string,
81
+ surConteneur: string,
82
+ tertiaire: string,
83
+ ): AccentTeintes => ({ accent, surAccent, appui, texte, conteneur, surConteneur, tertiaire });
84
+
85
+ const ACCENTS: Record<Accents, { clair: AccentTeintes; sombre: AccentTeintes }> = {
86
+ [Accents.pispi]: {
87
+ clair: teintes('#F2A900', '#000000', '#E1A009', '#9A6B00', '#F9EBC2', '#000000', '#FFD573'),
88
+ sombre: teintes('#F2A900', '#1B1916', '#D99A06', '#F2A900', '#4A3B1C', '#FFDE9A', '#4A3B1C'),
89
+ },
90
+ [Accents.vert]: {
91
+ clair: teintes('#43A047', '#000000', '#388E3C', '#2E7D32', '#D9F2D6', '#1B4D1E', '#A5D6A7'),
92
+ sombre: teintes('#66BB6A', '#1B1916', '#57A85B', '#66BB6A', '#1F3A20', '#B4E6B1', '#1F3A20'),
93
+ },
94
+ [Accents.bleu]: {
95
+ clair: teintes('#3B63DB', '#FFFFFF', '#3254BA', '#3B63DB', '#DCE4FB', '#1C3378', '#7390E6'),
96
+ sombre: teintes('#7B9BFF', '#1B1916', '#6A88E6', '#7B9BFF', '#1F2A4D', '#C7D3FF', '#1F2A4D'),
97
+ },
98
+ [Accents.rouge]: {
99
+ clair: teintes('#C62828', '#FFFFFF', '#A82222', '#C62828', '#F9D3D3', '#6E1414', '#E57373'),
100
+ sombre: teintes('#EF6F6C', '#1B1916', '#D96260', '#EF6F6C', '#4A1F1E', '#FFC9C7', '#4A1F1E'),
101
+ },
102
+ [Accents.orange]: {
103
+ clair: teintes('#EF6C00', '#000000', '#D45F00', '#B85300', '#FFE0B2', '#6B3300', '#FFB74D'),
104
+ sombre: teintes('#FF9A3D', '#1B1916', '#E6893A', '#FF9A3D', '#4A2E14', '#FFD3A8', '#4A2E14'),
105
+ },
106
+ [Accents.violet]: {
107
+ clair: teintes('#6A1B9A', '#FFFFFF', '#5A1783', '#6A1B9A', '#EBD7F3', '#3D0E5A', '#BA68C8'),
108
+ sombre: teintes('#C08BE0', '#1B1916', '#AD7BCB', '#C08BE0', '#37224A', '#EAD2FA', '#37224A'),
109
+ },
110
+ [Accents.rose]: {
111
+ clair: teintes('#AD1457', '#FFFFFF', '#93114A', '#AD1457', '#F8D3E1', '#5E0B2F', '#F06292'),
112
+ sombre: teintes('#F06C9B', '#1B1916', '#D85F8A', '#F06C9B', '#4A1E30', '#FFC8DB', '#4A1E30'),
113
+ },
114
+ [Accents.turquoise]: {
115
+ clair: teintes('#00897B', '#000000', '#00796B', '#00796B', '#D2EEEB', '#004D45', '#80CBC4'),
116
+ sombre: teintes('#4DB6AC', '#1B1916', '#42A39A', '#4DB6AC', '#173A37', '#B2E8E2', '#173A37'),
117
+ },
118
+ [Accents.terracotta]: {
119
+ clair: teintes('#C2562E', '#000000', '#A8461F', '#A8461F', '#F8DDD2', '#6B2A12', '#F4A585'),
120
+ sombre: teintes('#F08A62', '#1B1916', '#D97A55', '#F08A62', '#4A2A1E', '#FFD2C0', '#4A2A1E'),
121
+ },
122
+ [Accents.bordeaux]: {
123
+ clair: teintes('#8E2440', '#FFFFFF', '#761E35', '#8E2440', '#F3D3DC', '#4F1022', '#D98EA2'),
124
+ sombre: teintes('#E57A95', '#1B1916', '#CE6D85', '#E57A95', '#47212B', '#FFCCD9', '#47212B'),
125
+ },
126
+ [Accents.ardoise]: {
127
+ clair: teintes('#546E7A', '#FFFFFF', '#455A64', '#546E7A', '#DDE5E9', '#263238', '#B0BEC5'),
128
+ sombre: teintes('#90A4AE', '#1B1916', '#7F929B', '#90A4AE', '#2C3438', '#D6E2E8', '#2C3438'),
129
+ },
130
+ };
131
+
132
+ // eslint-disable-next-line @typescript-eslint/no-namespace
133
+ export namespace Accents {
134
+ /** Couleur de base de PI-SPI */
135
+ export const parDefaut = Accents.pispi;
136
+
137
+ /** Couleur correspondant à [code] (`Accents.pispi` si inconnu ou absent). */
138
+ export function fromCode(code: string | null | undefined): Accents {
139
+ if (code == null) return parDefaut;
140
+ return (list() as string[]).includes(code) ? (code as Accents) : parDefaut;
141
+ }
142
+
143
+ export function list(): Accents[] {
144
+ return [
145
+ Accents.pispi,
146
+ Accents.vert,
147
+ Accents.bleu,
148
+ Accents.rouge,
149
+ Accents.orange,
150
+ Accents.violet,
151
+ Accents.rose,
152
+ Accents.turquoise,
153
+ Accents.terracotta,
154
+ Accents.bordeaux,
155
+ Accents.ardoise,
156
+ ];
157
+ }
158
+
159
+ /** Teintes en thème clair */
160
+ export function clair(accent: Accents): AccentTeintes {
161
+ return ACCENTS[accent].clair;
162
+ }
163
+
164
+ /** Teintes en thème sombre */
165
+ export function sombre(accent: Accents): AccentTeintes {
166
+ return ACCENTS[accent].sombre;
167
+ }
168
+
169
+ /** Libellé traduit de la couleur. */
170
+ export function label(accent: Accents, traductions: AppLocalizations): string {
171
+ switch (accent) {
172
+ case Accents.vert:
173
+ return traductions.appSettingPageAccentVert;
174
+ case Accents.bleu:
175
+ return traductions.appSettingPageAccentBleu;
176
+ case Accents.rouge:
177
+ return traductions.appSettingPageAccentRouge;
178
+ case Accents.orange:
179
+ return traductions.appSettingPageAccentOrange;
180
+ case Accents.violet:
181
+ return traductions.appSettingPageAccentViolet;
182
+ case Accents.rose:
183
+ return traductions.appSettingPageAccentRose;
184
+ case Accents.turquoise:
185
+ return traductions.appSettingPageAccentTurquoise;
186
+ case Accents.terracotta:
187
+ return traductions.appSettingPageAccentTerracotta;
188
+ case Accents.bordeaux:
189
+ return traductions.appSettingPageAccentBordeaux;
190
+ case Accents.ardoise:
191
+ return traductions.appSettingPageAccentArdoise;
192
+ default:
193
+ // Couleur de base de PI-SPI, proposée comme « Défaut »
194
+ return traductions.appSettingPageAccentDefaut;
195
+ }
196
+ }
197
+ }
198
+
70
199
  export type Brightness = 'light' | 'dark';
71
200
 
72
201
  export interface ColorScheme {
@@ -172,26 +301,6 @@ export interface ThemeData {
172
301
  chipTheme: { borderRadius: number; labelStyle: TextStyle };
173
302
  }
174
303
 
175
- /** Valeurs qui varient d'un thème à l'autre (le reste est commun, comme dans theme.dart). */
176
- type ThemeSpec = {
177
- dark: boolean;
178
- primaryColor: string;
179
- primaryColorDark: string;
180
- secondaryHeaderColor: string;
181
- schemePrimary: string;
182
- schemeOnPrimary: string;
183
- schemeSecondary: string;
184
- schemeTertiary: string;
185
- iconColor: string;
186
- focusedBorderColor: string;
187
- buttonPressed: string;
188
- buttonBackground: string;
189
- buttonForeground: string;
190
- textButtonColor: string;
191
- textButtonOverlay: string;
192
- switchActiveTrack: string;
193
- };
194
-
195
304
  const fontSize = (size: number, weight: TextStyle['fontWeight'], height?: number): TextStyle => ({
196
305
  fontSize: size,
197
306
  fontWeight: weight,
@@ -214,20 +323,53 @@ export class Themer {
214
323
  /** Couleur jaune */
215
324
  static readonly amberColor = '#F2A900';
216
325
 
217
- /** Couleur neural 01 */
218
- static readonly neural01Color = '#E6E4E2';
326
+ // Nuances neutres du thème clair (du plus clair au plus foncé)
327
+ private static readonly neural01Light = '#E6E4E2';
328
+ private static readonly neural02Light = '#BDBDBB';
329
+ private static readonly neural03Light = '#8B887E';
330
+ private static readonly neural04Light = '#615D52';
331
+ private static readonly neural05Light = '#5C5451';
332
+
333
+ // Les mêmes nuances en thème sombre, inversées : 01 reste la nuance
334
+ // discrète (traits, fonds), 03 et 04 les textes secondaires
335
+ private static readonly neural01Dark = '#4A453E';
336
+ private static readonly neural02Dark = '#6F6A60';
337
+ private static readonly neural03Dark = '#A8A297';
338
+ private static readonly neural04Dark = '#BDB7AC';
339
+ private static readonly neural05Dark = '#C4BDB2';
340
+
341
+ /** Luminosité du thème affiché, mise à jour par {@link suivreLuminosite} */
342
+ private static luminosite: Brightness = 'light';
343
+
344
+ /** À appeler avec la luminosité du thème affiché : adapte les couleurs neutres ci-dessous. */
345
+ static suivreLuminosite(luminosite: Brightness): void {
346
+ Themer.luminosite = luminosite;
347
+ }
348
+
349
+ /** Couleur neural 01 : traits et fonds discrets */
350
+ static get neural01Color(): string {
351
+ return Themer.luminosite === 'dark' ? Themer.neural01Dark : Themer.neural01Light;
352
+ }
219
353
 
220
- /** Couleur neural 02 */
221
- static readonly neural02Color = '#BDBDBB';
354
+ /** Couleur neural 02 : textes désactivés, indications */
355
+ static get neural02Color(): string {
356
+ return Themer.luminosite === 'dark' ? Themer.neural02Dark : Themer.neural02Light;
357
+ }
222
358
 
223
- /** Couleur neural 03 */
224
- static readonly neural03Color = '#8B887E';
359
+ /** Couleur neural 03 : textes secondaires */
360
+ static get neural03Color(): string {
361
+ return Themer.luminosite === 'dark' ? Themer.neural03Dark : Themer.neural03Light;
362
+ }
225
363
 
226
- /** Couleur neural 04 */
227
- static readonly neural04Color = '#615D52';
364
+ /** Couleur neural 04 : textes secondaires plus marqués */
365
+ static get neural04Color(): string {
366
+ return Themer.luminosite === 'dark' ? Themer.neural04Dark : Themer.neural04Light;
367
+ }
228
368
 
229
369
  /** Couleur neural 05 */
230
- static readonly neural05Color = '#5C5451';
370
+ static get neural05Color(): string {
371
+ return Themer.luminosite === 'dark' ? Themer.neural05Dark : Themer.neural05Light;
372
+ }
231
373
 
232
374
  /** Primary */
233
375
  static readonly primary = '#F2A900';
@@ -257,41 +399,52 @@ export class Themer {
257
399
  /** primary small btn height */
258
400
  static readonly btnSmallHeight = 38;
259
401
 
260
- private static build(spec: ThemeSpec): ThemeData {
402
+ // Thème sombre « soft » : fonds brun-gris chauds qui s'éclaircissent avec
403
+ // l'élévation (page < feuille < carte < champ), texte blanc cassé
404
+ private static readonly darkBackground = '#1B1916';
405
+ private static readonly darkSurface = '#24211E';
406
+ private static readonly darkCard = '#2C2925';
407
+ private static readonly darkInput = '#35312C';
408
+ private static readonly darkOutline = '#4A453E';
409
+ private static readonly darkText = '#ECE8E1';
410
+ private static readonly darkTextSecondary = '#A8A297';
411
+ private static readonly darkTextDisabled = '#6F6A60';
412
+
413
+ /** Thème clair ou sombre teinté par la couleur d'accentuation [a]. */
414
+ private static build(dark: boolean, a: AccentTeintes): ThemeData {
261
415
  const T = Themer;
262
- const dark = spec.dark;
263
- const textColor = dark ? T.whiteColor : T.blackColor;
264
- const onColor = dark ? T.whiteColor : T.blackColor;
265
- const surface = dark ? '#181612' : T.whiteColor;
266
- const card = dark ? '#23211D' : '#FAF8F6';
416
+ const textColor = dark ? T.darkText : T.blackColor;
417
+ const surface = dark ? T.darkSurface : T.whiteColor;
418
+ const card = dark ? T.darkCard : '#FAF8F6';
419
+ const background = dark ? T.darkBackground : T.whiteColor;
267
420
  const text = (style: TextStyle): TextStyle => ({ color: textColor, ...style });
268
421
 
269
422
  return {
270
423
  brightness: dark ? 'dark' : 'light',
271
- primaryColor: spec.primaryColor,
272
- primaryColorDark: spec.primaryColorDark,
273
- secondaryHeaderColor: spec.secondaryHeaderColor,
424
+ primaryColor: a.accent,
425
+ primaryColorDark: a.texte,
426
+ secondaryHeaderColor: a.conteneur,
274
427
  cardColor: card,
275
- scaffoldBackgroundColor: dark ? '#2E2B26' : T.whiteColor,
276
- dialogSurfaceTintColor: dark ? '#2E2B26' : T.whiteColor,
428
+ scaffoldBackgroundColor: background,
429
+ dialogSurfaceTintColor: dark ? T.darkSurface : T.whiteColor,
277
430
  colorScheme: {
278
431
  brightness: dark ? 'dark' : 'light',
279
- primary: spec.schemePrimary,
280
- onPrimary: spec.schemeOnPrimary,
281
- secondary: spec.schemeSecondary,
282
- onSecondary: onColor,
283
- tertiary: spec.schemeTertiary,
284
- onTertiary: onColor,
432
+ primary: a.accent,
433
+ onPrimary: a.surAccent,
434
+ secondary: a.conteneur,
435
+ onSecondary: a.surConteneur,
436
+ tertiary: a.tertiaire,
437
+ onTertiary: dark ? a.surConteneur : T.blackColor,
285
438
  error: T.systemErrorColor,
286
- onError: onColor,
439
+ onError: dark ? T.darkBackground : T.blackColor,
287
440
  surface,
288
- onSurface: onColor,
441
+ onSurface: textColor,
289
442
  },
290
443
  appBarTheme: {
291
- backgroundColor: dark ? '#23211D' : T.whiteColor,
444
+ backgroundColor: background,
292
445
  foregroundColor: textColor,
293
446
  },
294
- primaryIconColor: spec.iconColor,
447
+ primaryIconColor: a.accent,
295
448
  textTheme: {
296
449
  titleLarge: text(fontSize(32, '600')),
297
450
  titleMedium: text(fontSize(32, '400')),
@@ -304,7 +457,7 @@ export class Themer {
304
457
  bodySmall: text(fontSize(15, '500')),
305
458
  displayLarge: text(fontSize(15, '400', 1.4)),
306
459
  displayMedium: text(fontSize(13, '500')),
307
- displaySmall: { ...text(fontSize(13, '400', 1.4)), color: T.neural04Color },
460
+ displaySmall: { ...text(fontSize(13, '400', 1.4)), color: dark ? T.darkTextSecondary : T.neural04Light },
308
461
  labelLarge: text(fontSize(12, '400')),
309
462
  labelMedium: text(fontSize(11, '400')),
310
463
  },
@@ -313,232 +466,184 @@ export class Themer {
313
466
  inputDecorationTheme: {
314
467
  paddingHorizontal: 16,
315
468
  paddingVertical: 5,
316
- fillColor: dark ? '#333333' : '#FAF8F6',
469
+ fillColor: dark ? T.darkInput : '#FAF8F6',
317
470
  borderRadius: 10,
318
- focusedBorderColor: spec.focusedBorderColor,
471
+ focusedBorderColor: a.accent,
319
472
  errorBorderColor: T.systemErrorColor,
320
- floatingLabelStyle: { color: dark ? '#9E9E9E' : T.neural04Color, fontSize: 13, fontWeight: '400' },
321
- labelStyle: { color: dark ? '#9E9E9E' : T.neural03Color, fontSize: 17 },
322
- hintStyle: { color: dark ? '#EEEEEE' : T.neural02Color, fontSize: 17 },
473
+ floatingLabelStyle: { color: dark ? T.darkTextSecondary : T.neural04Light, fontSize: 13, fontWeight: '400' },
474
+ labelStyle: { color: dark ? T.darkTextSecondary : T.neural03Light, fontSize: 17 },
475
+ hintStyle: { color: dark ? T.darkTextDisabled : T.neural02Light, fontSize: 17 },
323
476
  errorStyle: { color: T.systemErrorColor, fontSize: 12 },
324
477
  errorMaxLines: 3,
325
- suffixIconColor: dark ? '#B1ADA0' : '#8B887E',
478
+ suffixIconColor: dark ? T.darkTextSecondary : '#8B887E',
326
479
  },
327
480
  elevatedButtonTheme: {
328
- backgroundColor: spec.buttonBackground,
329
- pressedBackgroundColor: spec.buttonPressed,
330
- disabledBackgroundColor: dark ? '#2E2E2E' : '#F5F5F1',
331
- foregroundColor: spec.buttonForeground,
332
- disabledForegroundColor: dark ? '#B1ADA0' : T.neural03Color,
481
+ backgroundColor: a.accent,
482
+ pressedBackgroundColor: a.appui,
483
+ disabledBackgroundColor: dark ? T.darkCard : '#F5F5F1',
484
+ foregroundColor: a.surAccent,
485
+ disabledForegroundColor: dark ? T.darkTextDisabled : T.neural03Light,
333
486
  minHeight: T.btnNormalHeight,
334
487
  borderRadius: 20,
335
- textStyle: { color: spec.buttonForeground, fontSize: 17, fontWeight: '600' },
488
+ textStyle: { color: a.surAccent, fontSize: 17, fontWeight: '600' },
336
489
  },
337
490
  textButtonTheme: {
338
- foregroundColor: spec.textButtonColor,
339
- overlayColor: spec.textButtonOverlay,
340
- textStyle: { color: spec.textButtonColor, fontSize: 17, fontWeight: '400' },
491
+ foregroundColor: a.texte,
492
+ overlayColor: dark ? `${a.accent}1F` : a.conteneur,
493
+ textStyle: { color: a.texte, fontSize: 17, fontWeight: '400' },
341
494
  minWidth: 55,
342
495
  minHeight: 26,
343
496
  },
344
497
  switchTheme: {
345
- thumbColor: dark ? '#23211D' : T.whiteColor,
346
- activeTrackColor: spec.switchActiveTrack,
347
- inactiveTrackColor: dark ? T.neural04Color : T.neural01Color,
498
+ thumbColor: dark ? T.darkTextSecondary : T.whiteColor,
499
+ activeTrackColor: a.accent,
500
+ inactiveTrackColor: dark ? T.darkOutline : T.neural01Light,
348
501
  },
349
502
  filledButtonTheme: {
350
- foregroundColor: textColor,
351
- disabledForegroundColor: dark ? '#B1ADA0' : T.neural03Color,
503
+ foregroundColor: a.surAccent,
504
+ disabledForegroundColor: dark ? T.darkTextDisabled : T.neural03Light,
352
505
  minHeight: T.btnNormalHeight,
353
506
  borderRadius: 20,
354
- textStyle: { color: textColor, fontSize: 17, fontWeight: '600' },
507
+ textStyle: { color: a.surAccent, fontSize: 17, fontWeight: '600' },
355
508
  },
356
509
  snackBarTheme: { backgroundColor: T.systemErrorColor, margin: 10 },
357
510
  checkboxTheme: {
358
511
  borderRadius: 6,
359
- checkColor: dark ? '#181612' : T.whiteColor,
360
- borderColor: T.neural02Color,
512
+ checkColor: a.surAccent,
513
+ borderColor: dark ? T.darkTextDisabled : T.neural02Light,
361
514
  borderWidth: 2,
362
515
  },
363
516
  chipTheme: { borderRadius: 16, labelStyle: { color: textColor, fontSize: 14, fontWeight: '500' } },
364
517
  };
365
518
  }
366
519
 
367
- /** Thème par défaut : jaune PI-SPI sur fond blanc */
368
- private static readonly defaultSpec: ThemeSpec = {
369
- dark: false,
370
- primaryColor: Themer.primary,
371
- primaryColorDark: Themer.primaryDark,
372
- secondaryHeaderColor: Themer.primaryLight,
373
- schemePrimary: Themer.primary,
374
- schemeOnPrimary: Themer.blackColor,
375
- schemeSecondary: Themer.primaryLight,
376
- schemeTertiary: Themer.primaryStroke,
377
- iconColor: Themer.primary,
378
- focusedBorderColor: Themer.primary,
379
- buttonPressed: '#E1A009',
380
- buttonBackground: Themer.primary,
381
- buttonForeground: Themer.blackColor,
382
- textButtonColor: Themer.primaryDark,
383
- textButtonOverlay: Themer.primaryLight,
384
- switchActiveTrack: Themer.primary,
385
- };
520
+ /** Thèmes déjà construits, par luminosité et couleur d'accentuation */
521
+ private static readonly themes = new Map<string, ThemeData>();
386
522
 
387
- /** Code enregistré comme thème préféré quand l'hôte fournit un thème personnalisé (`PiSpiAppConfig.customTheme`). */
388
- static readonly customThemeCode = 'customTheme';
523
+ /** Thème clair teinté par la couleur d'accentuation [accent] */
524
+ static clair(accent?: string | null): ThemeData {
525
+ const a = Accents.fromCode(accent);
526
+ const cle = `clair-${a}`;
527
+ if (!Themer.themes.has(cle)) Themer.themes.set(cle, Themer.build(false, Accents.clair(a)));
528
+ return Themer.themes.get(cle)!;
529
+ }
389
530
 
390
- /** Thème personnalisé de l'hôte, construit à l'ouverture du SDK (voir `Di.init`) ; `null` sans thème personnalisé. */
391
- static customTheme: ThemeData | null = null;
531
+ /** Thème sombre teinté par la couleur d'accentuation [accent] */
532
+ static sombre(accent?: string | null): ThemeData {
533
+ const a = Accents.fromCode(accent);
534
+ const cle = `sombre-${a}`;
535
+ if (!Themer.themes.has(cle)) Themer.themes.set(cle, Themer.build(true, Accents.sombre(a)));
536
+ return Themer.themes.get(cle)!;
537
+ }
538
+
539
+ /** Code enregistré comme couleur d'accentuation quand l'hôte fournit un thème personnalisé (`PiSpiAppConfig.customTheme`). */
540
+ static readonly customAccentCode = 'application';
541
+
542
+ /** Teintes de la couleur de l'hôte (claire puis sombre), définies à l'ouverture du SDK (voir `Di.init`) ; `null` sans thème personnalisé. */
543
+ static customAccent: { clair: AccentTeintes; sombre: AccentTeintes } | null = null;
544
+
545
+ /** Thème complet fourni par l'hôte (`PiSpiCustomTheme.themeData`) : il remplace alors les thèmes clair et sombre du SDK. */
546
+ static customThemeData: ThemeData | null = null;
547
+
548
+ /** L'hôte a fourni un thème personnalisé */
549
+ static get hasCustomTheme(): boolean {
550
+ return Themer.customAccent != null || Themer.customThemeData != null;
551
+ }
392
552
 
393
553
  /**
394
- * Thème clair construit à partir de couleurs (thème personnalisé de
395
- * l'hôte, voir `PiSpiCustomTheme`) : même modèle que les thèmes rouge,
396
- * violet ou rose, avec le texte des boutons en blanc ou en noir selon la
397
- * luminosité de [primary].
554
+ * Teintes claire et sombre dérivées des couleurs de l'hôte : texte posé
555
+ * sur la couleur en noir ou blanc selon son contraste, teinte plus
556
+ * lumineuse en sombre.
398
557
  */
399
- static colored(primary: string, secondary: string, tertiary: string, pressed: string): ThemeData {
400
- const onPrimary = relativeLuminance(primary) > 0.5 ? Themer.blackColor : Themer.whiteColor;
401
- return Themer.build(
402
- Themer.coloredSpec(primary, secondary, tertiary, {
403
- schemeOnPrimary: onPrimary,
404
- buttonPressed: pressed,
405
- buttonForeground: onPrimary,
406
- }),
407
- );
558
+ static teintesDepuis(
559
+ primary: string,
560
+ secondary?: string,
561
+ tertiary?: string,
562
+ pressed?: string,
563
+ ): { clair: AccentTeintes; sombre: AccentTeintes } {
564
+ const sur = (fond: string, fonce: string) =>
565
+ contrast(fonce, fond) >= contrast(Themer.whiteColor, fond) ? fonce : Themer.whiteColor;
566
+ // Lien lisible sur fond blanc (4,5:1), sinon couleur assombrie
567
+ let texte = primary;
568
+ for (let i = 1; i <= 6 && contrast(texte, Themer.whiteColor) < 4.5; i++) {
569
+ texte = lerpColor(primary, Themer.blackColor, i * 0.1);
570
+ }
571
+ const clair: AccentTeintes = {
572
+ accent: primary,
573
+ surAccent: sur(primary, Themer.blackColor),
574
+ appui: pressed ?? lerpColor(primary, Themer.blackColor, 0.2),
575
+ texte,
576
+ conteneur: secondary ?? lerpColor(primary, Themer.whiteColor, 0.75),
577
+ surConteneur: Themer.blackColor,
578
+ tertiaire: tertiary ?? lerpColor(primary, Themer.whiteColor, 0.4),
579
+ };
580
+ // En sombre : couleur éclaircie jusqu'à 3:1 sur le fond
581
+ let fonce = primary;
582
+ for (let i = 1; i <= 6 && contrast(fonce, Themer.darkBackground) < 3; i++) {
583
+ fonce = lerpColor(primary, Themer.whiteColor, i * 0.1);
584
+ }
585
+ const conteneur = lerpColor(Themer.darkBackground, primary, 0.3);
586
+ const sombre: AccentTeintes = {
587
+ accent: fonce,
588
+ surAccent: sur(fonce, Themer.darkBackground),
589
+ appui: lerpColor(fonce, Themer.blackColor, 0.1),
590
+ texte: fonce,
591
+ conteneur,
592
+ surConteneur: lerpColor(fonce, Themer.whiteColor, 0.6),
593
+ tertiaire: conteneur,
594
+ };
595
+ return { clair, sombre };
408
596
  }
409
597
 
410
- /** Variante claire monochrome (même structure pour jaune, bleu, vert, rouge, orange, violet). */
411
- private static coloredSpec(
412
- main: string,
413
- secondary: string,
414
- tertiary: string,
415
- options: Partial<ThemeSpec> = {},
416
- ): ThemeSpec {
417
- return {
418
- ...Themer.defaultSpec,
419
- primaryColor: main,
420
- primaryColorDark: main,
421
- secondaryHeaderColor: secondary,
422
- schemePrimary: main,
423
- schemeSecondary: secondary,
424
- schemeTertiary: tertiary,
425
- iconColor: main,
426
- focusedBorderColor: main,
427
- buttonPressed: main,
428
- buttonBackground: main,
429
- textButtonColor: main,
430
- textButtonOverlay: main,
431
- switchActiveTrack: main,
432
- ...options,
433
- };
598
+ /** Thème construit sur les teintes [a] (clair, ou sombre si [dark]) */
599
+ static depuisTeintes(a: AccentTeintes, dark = false): ThemeData {
600
+ return Themer.build(dark, a);
434
601
  }
435
602
 
436
- /** Light theme data */
437
- static readonly lightTheme: ThemeData = Themer.build(Themer.defaultSpec);
438
-
439
- /** Dark theme */
440
- static readonly darkTheme: ThemeData = Themer.build({
441
- ...Themer.defaultSpec,
442
- dark: true,
443
- secondaryHeaderColor: Themer.primaryStroke,
444
- schemeOnPrimary: Themer.whiteColor,
445
- schemeSecondary: Themer.primaryStroke,
446
- buttonForeground: Themer.whiteColor,
447
- });
603
+ /** Libellé de la couleur d'accentuation [code], y compris celle de l'hôte */
604
+ static accentLabel(traductions: AppLocalizations, code: string | null | undefined): string {
605
+ if (code === Themer.customAccentCode && Themer.hasCustomTheme) return traductions.appSettingPageMenuThemeCustom;
606
+ return Accents.label(Accents.fromCode(code), traductions);
607
+ }
608
+
609
+ /** Thème clair avec la couleur PI-SPI */
610
+ static get lightTheme(): ThemeData {
611
+ return Themer.clair();
612
+ }
448
613
 
449
- /** Light Yellow theme data */
450
- static readonly lightYellowTheme: ThemeData = Themer.build(
451
- Themer.coloredSpec('#F3DC88', '#FAF0C8', '#F6E5A8', { textButtonOverlay: Themer.primaryLight }),
452
- );
453
-
454
- /** Light Blue theme data */
455
- static readonly lightBlueTheme: ThemeData = Themer.build(
456
- Themer.coloredSpec('#3B63DB', '#A5B9F7', '#7390E6', { schemeOnPrimary: Themer.whiteColor }),
457
- );
458
-
459
- /** Light Green theme data */
460
- static readonly lightGreenTheme: ThemeData = Themer.build(
461
- Themer.coloredSpec('#9FF796', '#D9FDD6', '#BAFAB6', {
462
- primaryColor: '#7FC077',
463
- primaryColorDark: '#7FC077',
464
- buttonPressed: '#E1A009',
465
- textButtonColor: '#7FC077',
466
- textButtonOverlay: '#7FC077',
467
- }),
468
- );
469
-
470
- /** Light Red theme data */
471
- static readonly lightRedTheme: ThemeData = Themer.build(
472
- Themer.coloredSpec('#C62828', '#F9CFCF', '#E57373', {
473
- schemeOnPrimary: Themer.whiteColor,
474
- buttonPressed: '#B71C1C',
475
- buttonForeground: Themer.whiteColor,
476
- }),
477
- );
478
-
479
- /** Light Orange theme data */
480
- static readonly lightOrangeTheme: ThemeData = Themer.build(
481
- Themer.coloredSpec('#EF6C00', '#FFE0B2', '#FFB74D', {
482
- schemeOnPrimary: Themer.whiteColor,
483
- buttonPressed: '#E65100',
484
- buttonForeground: Themer.whiteColor,
485
- }),
486
- );
487
-
488
- /** Light Purple theme data */
489
- static readonly lightPurpleTheme: ThemeData = Themer.build(
490
- Themer.coloredSpec('#6A1B9A', '#E1BEE7', '#BA68C8', {
491
- schemeOnPrimary: Themer.whiteColor,
492
- buttonPressed: '#4A148C',
493
- buttonForeground: Themer.whiteColor,
494
- }),
495
- );
496
-
497
- /** Light Pink theme data */
498
- static readonly lightPinkTheme: ThemeData = Themer.build(
499
- Themer.coloredSpec('#AD1457', '#F8BBD0', '#F06292', {
500
- schemeOnPrimary: Themer.whiteColor,
501
- buttonPressed: '#880E4F',
502
- buttonForeground: Themer.whiteColor,
503
- }),
504
- );
614
+ /** Thème sombre avec la couleur PI-SPI */
615
+ static get darkTheme(): ThemeData {
616
+ return Themer.sombre();
617
+ }
505
618
 
506
619
  /**
507
- * Méthode qui retourne un thème demandé. Pour `Themes.system`,
508
- * [systemScheme] (ex. `useColorScheme()`) prime sur la valeur lue
509
- * ponctuellement via `Appearance`.
620
+ * Thème à afficher : [code] (système, clair ou sombre) teinté par la
621
+ * couleur d'accentuation [accent]. Pour `Themes.system`, [systemScheme]
622
+ * (ex. `useColorScheme()`) prime sur la valeur lue ponctuellement via
623
+ * `Appearance`.
510
624
  */
511
- static get(code: string | null | undefined, systemScheme?: ColorSchemeName | null): ThemeData {
512
- // Thème personnalisé de l'hôte
513
- if (code === Themer.customThemeCode && Themer.customTheme != null) return Themer.customTheme;
514
- switch (code) {
515
- case Themes.light:
516
- return Themer.lightTheme;
517
- case Themes.dark:
518
- return Themer.darkTheme;
519
- case Themes.yellow:
520
- return Themer.lightYellowTheme;
521
- case Themes.green:
522
- return Themer.lightGreenTheme;
523
- case Themes.blue:
524
- return Themer.lightBlueTheme;
525
- case Themes.red:
526
- return Themer.lightRedTheme;
527
- case Themes.orange:
528
- return Themer.lightOrangeTheme;
529
- case Themes.purple:
530
- return Themer.lightPurpleTheme;
531
- case Themes.pink:
532
- return Themer.lightPinkTheme;
533
- // Système
534
- case Themes.system: {
535
- const brightness = systemScheme === 'light' || systemScheme === 'dark' ? systemScheme : Appearance.getColorScheme();
536
- return brightness === 'dark' ? Themer.darkTheme : Themer.lightTheme;
537
- }
538
- // Default theme
539
- default:
540
- return Themer.lightTheme;
625
+ static get(code: string | null | undefined, accent?: string | null, systemScheme?: ColorSchemeName | null): ThemeData {
626
+ const theme = Themes.fromCode(code);
627
+ let dark = theme === Themes.dark;
628
+ if (theme === Themes.system) {
629
+ const brightness = systemScheme === 'light' || systemScheme === 'dark' ? systemScheme : Appearance.getColorScheme();
630
+ dark = brightness === 'dark';
631
+ }
632
+ // Thème complet ou couleur du thème personnalisé de l'hôte
633
+ if (accent === Themer.customAccentCode && Themer.customThemeData != null) return Themer.customThemeData;
634
+ const perso = Themer.customAccent;
635
+ if (accent === Themer.customAccentCode && perso != null) {
636
+ const cle = `${dark ? 'sombre' : 'clair'}-${Themer.customAccentCode}`;
637
+ if (!Themer.themes.has(cle)) Themer.themes.set(cle, Themer.build(dark, dark ? perso.sombre : perso.clair));
638
+ return Themer.themes.get(cle)!;
541
639
  }
640
+ return dark ? Themer.sombre(accent) : Themer.clair(accent);
641
+ }
642
+
643
+ /** Oublie les thèmes construits avec la couleur de l'hôte (nouvelle ouverture du SDK) */
644
+ static resetCustomThemes(): void {
645
+ Themer.themes.delete(`clair-${Themer.customAccentCode}`);
646
+ Themer.themes.delete(`sombre-${Themer.customAccentCode}`);
542
647
  }
543
648
  }
544
649
 
@@ -576,3 +681,10 @@ export function relativeLuminance(hex: string): number {
576
681
  });
577
682
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
578
683
  }
684
+
685
+ /** Ratio de contraste WCAG entre deux couleurs */
686
+ export function contrast(a: string, b: string): number {
687
+ const la = relativeLuminance(a);
688
+ const lb = relativeLuminance(b);
689
+ return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
690
+ }