@wasto/cms-components 1.5.10 → 1.5.12

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 (101) hide show
  1. package/README.md +47 -47
  2. package/dist/index.d.mts +181 -51
  3. package/dist/index.d.ts +181 -51
  4. package/dist/index.js +3614 -3015
  5. package/dist/index.mjs +3021 -2418
  6. package/package.json +12 -2
  7. package/src/components/announcement-modal/AnnouncementModal.tsx +183 -183
  8. package/src/components/article/Article.tsx +21 -21
  9. package/src/components/article/ArticleCard.tsx +81 -81
  10. package/src/components/article/ArticleExcerpts.tsx +80 -80
  11. package/src/components/article/ArticleList.tsx +114 -114
  12. package/src/components/article/ArticleMarkdown.tsx +170 -170
  13. package/src/components/article/ArticleReaderModal.tsx +93 -93
  14. package/src/components/article/ArticleSingle.tsx +57 -57
  15. package/src/components/bottom-tab/BottomTab.tsx +392 -392
  16. package/src/components/button/Button.tsx +154 -154
  17. package/src/components/challenge/Badges.tsx +84 -84
  18. package/src/components/challenge/BadgesModal.tsx +166 -166
  19. package/src/components/challenge/Challenge.tsx +380 -380
  20. package/src/components/challenge/ChallengeModal.tsx +231 -231
  21. package/src/components/challenge/ChallengeVignette.tsx +249 -249
  22. package/src/components/challenge/ChallengesModal.tsx +243 -243
  23. package/src/components/challenge/QuizModal.tsx +381 -381
  24. package/src/components/challenge/ResultModal.tsx +144 -144
  25. package/src/components/chat/Chat.tsx +928 -928
  26. package/src/components/collection-calendar/CollectionCalendar.tsx +274 -274
  27. package/src/components/columns/Columns.tsx +33 -33
  28. package/src/components/consignes-modal/ConsignesModal.tsx +267 -267
  29. package/src/components/content-inset/ContentInset.tsx +33 -33
  30. package/src/components/header/Header.tsx +415 -391
  31. package/src/components/image/Image.tsx +15 -15
  32. package/src/components/image/ImageFrame.tsx +102 -102
  33. package/src/components/image/ImageMedia.tsx +31 -31
  34. package/src/components/image/ImageUrl.tsx +30 -30
  35. package/src/components/locations-map/LocationSheet.tsx +417 -417
  36. package/src/components/locations-map/LocationsMap.native.tsx +260 -260
  37. package/src/components/locations-map/LocationsMap.tsx +13 -13
  38. package/src/components/locations-map/LocationsMap.web.tsx +10 -10
  39. package/src/components/locations-map/LocationsMapShell.tsx +546 -527
  40. package/src/components/locations-map/LocationsMapView.tsx +108 -108
  41. package/src/components/locations-map/LocationsMapView.web.tsx +331 -331
  42. package/src/components/nav-button/NavButton.tsx +59 -59
  43. package/src/components/next-collection/NextCollection.tsx +636 -621
  44. package/src/components/notifications/NotificationsScreen.tsx +247 -247
  45. package/src/components/page/Page.tsx +102 -64
  46. package/src/components/parapraph/Paragraph.tsx +67 -67
  47. package/src/components/profile/ProfileScreen.tsx +236 -236
  48. package/src/components/push/PushTokenRegistrar.tsx +47 -0
  49. package/src/components/recycling-info/RecyclingInfo.tsx +189 -189
  50. package/src/components/safe-area/OptionalSafeAreaProvider.tsx +17 -17
  51. package/src/components/search/Search.tsx +320 -320
  52. package/src/components/select-street-modal/SelectStreetModal.tsx +475 -475
  53. package/src/components/title/Title.tsx +100 -100
  54. package/src/components/vision/Vision.tsx +447 -447
  55. package/src/components/vision/VisionActionCard.tsx +117 -117
  56. package/src/components/vision/VisionCapture.tsx +30 -30
  57. package/src/components/vision/VisionTapOverlay.tsx +122 -122
  58. package/src/contexts/AnnouncementsContext.tsx +171 -171
  59. package/src/contexts/AppDataContext.tsx +458 -0
  60. package/src/contexts/ChallengesContext.tsx +182 -182
  61. package/src/contexts/HeaderConfigContext.tsx +29 -0
  62. package/src/contexts/LocalContext.tsx +227 -227
  63. package/src/contexts/ThemeContext.tsx +37 -37
  64. package/src/contexts/WastoContext.tsx +132 -127
  65. package/src/helpers/blogPost.ts +72 -72
  66. package/src/helpers/generateQuizQuestions.ts +247 -247
  67. package/src/helpers/getContrastColor.ts +18 -18
  68. package/src/helpers/haptics.ts +20 -20
  69. package/src/helpers/hoursSchedule.ts +230 -230
  70. package/src/helpers/htmlContentToMarkdown.ts +114 -114
  71. package/src/helpers/layout.ts +54 -54
  72. package/src/helpers/loadLinearGradient.ts +22 -22
  73. package/src/helpers/mapLocations.ts +170 -0
  74. package/src/helpers/maquetteTheme.ts +76 -76
  75. package/src/helpers/markdownUnderlinePlugin.ts +32 -32
  76. package/src/helpers/nextCollection.ts +122 -0
  77. package/src/helpers/normalizeWasteTypeRecyclingInfo.ts +80 -80
  78. package/src/helpers/publicMediaUrl.ts +8 -8
  79. package/src/helpers/resolveApiAssetUrl.ts +21 -21
  80. package/src/helpers/systemRoutes.ts +7 -7
  81. package/src/helpers/themeStyles.ts +114 -114
  82. package/src/helpers/ttlCache.ts +41 -0
  83. package/src/helpers/useSafeAreaPadding.ts +14 -14
  84. package/src/hooks/useBadges.ts +100 -100
  85. package/src/hooks/useBlogPosts.ts +109 -105
  86. package/src/hooks/useChallenges.ts +110 -110
  87. package/src/hooks/useCities.ts +56 -56
  88. package/src/hooks/useCityAnnouncements.ts +118 -118
  89. package/src/hooks/useCollectionCalendar.ts +421 -421
  90. package/src/hooks/useColumns.ts +19 -19
  91. package/src/hooks/useConsignes.ts +62 -62
  92. package/src/hooks/useLocations.ts +120 -248
  93. package/src/hooks/useNextCollection.ts +163 -238
  94. package/src/hooks/useRecyclingInfo.ts +90 -90
  95. package/src/hooks/useWastoClient.ts +16 -16
  96. package/src/index.ts +161 -142
  97. package/src/navigation/NavigationContext.tsx +56 -56
  98. package/src/push/registerExpoPushToken.ts +133 -0
  99. package/src/sentry/initSentry.ts +120 -120
  100. package/src/types/Theme.ts +72 -72
  101. package/src/types/css-modules.d.ts +1 -1
package/README.md CHANGED
@@ -1,47 +1,47 @@
1
- # @wasto/cms-components
2
-
3
- Composants CMS pour apps Expo / React Native (New Architecture, Bridgeless).
4
-
5
- Metro doit charger le **source** (`src/`, champ `"react-native"`), pas seulement `dist/`. Ne pas forcer `react-native-safe-area-context/lib/commonjs` dans `metro.config.js` : ça casse le codegen Fabric (`RNCSafeArea*`).
6
-
7
- ## Peers
8
-
9
- | Package | Obligatoire | Notes |
10
- |--------|-------------|--------|
11
- | `react`, `react-native` | oui | RN 0.81+ / Expo 54+ |
12
- | `react-native-safe-area-context` | oui | `~5.6.0` — une seule copie, hoisted |
13
- | `@react-navigation/bottom-tabs` | oui | Types `BottomTab` (tabBar) |
14
- | `expo-image`, `lucide-react-native`, `@react-native-async-storage/async-storage`, `@wasto/sdk` | oui | |
15
- | `expo-router` | **optionnel** | Apps Expo Router : fournir `NavigationProvider` qui délègue à `router` |
16
- | `@maplibre/maplibre-react-native` | **optionnel** | Requis sur iOS/Android pour afficher la carte |
17
- | `maplibre-gl` | **optionnel** | Requis sur **web** (PWA / playground) pour la carte tuiles |
18
-
19
- ### Navigation
20
-
21
- `Header` / `NextCollection` n’importent plus `expo-router`. L’hôte enveloppe l’app avec `NavigationProvider` :
22
-
23
- ```tsx
24
- import { NavigationProvider } from "@wasto/cms-components";
25
- import { router } from "expo-router"; // ou React Navigation
26
-
27
- <NavigationProvider
28
- value={{
29
- push: (href) => router.push(href as never),
30
- canGoBack: () => router.canGoBack(),
31
- goBack: () => router.back(),
32
- }}
33
- >
34
- …
35
- </NavigationProvider>
36
- ```
37
-
38
- ## Carte (`LocationsMap`)
39
-
40
- ```ts
41
- import { LocationsMap } from "@wasto/cms-components"
42
- ```
43
-
44
- - **iOS / Android** : Metro charge `LocationsMap.native.tsx` (MapLibre React Native).
45
- - **Web** : Metro charge `LocationsMapView.web.tsx` (MapLibre GL JS, style OpenFreeMap) si `maplibre-gl` est installé ; sinon fallback pins (`LocationsMapView.tsx`).
46
-
47
- Sur native : dépendance + plugin Expo `@maplibre/maplibre-react-native`, puis rebuild (`expo run:ios`). pnpm : le module doit être résolvable depuis la racine (`shamefully-hoist` ou `extraNodeModules`).
1
+ # @wasto/cms-components
2
+
3
+ Composants CMS pour apps Expo / React Native (New Architecture, Bridgeless).
4
+
5
+ Metro doit charger le **source** (`src/`, champ `"react-native"`), pas seulement `dist/`. Ne pas forcer `react-native-safe-area-context/lib/commonjs` dans `metro.config.js` : ça casse le codegen Fabric (`RNCSafeArea*`).
6
+
7
+ ## Peers
8
+
9
+ | Package | Obligatoire | Notes |
10
+ |--------|-------------|--------|
11
+ | `react`, `react-native` | oui | RN 0.81+ / Expo 54+ |
12
+ | `react-native-safe-area-context` | oui | `~5.6.0` — une seule copie, hoisted |
13
+ | `@react-navigation/bottom-tabs` | oui | Types `BottomTab` (tabBar) |
14
+ | `expo-image`, `lucide-react-native`, `@react-native-async-storage/async-storage`, `@wasto/sdk` | oui | |
15
+ | `expo-router` | **optionnel** | Apps Expo Router : fournir `NavigationProvider` qui délègue à `router` |
16
+ | `@maplibre/maplibre-react-native` | **optionnel** | Requis sur iOS/Android pour afficher la carte |
17
+ | `maplibre-gl` | **optionnel** | Requis sur **web** (PWA / playground) pour la carte tuiles |
18
+
19
+ ### Navigation
20
+
21
+ `Header` / `NextCollection` n’importent plus `expo-router`. L’hôte enveloppe l’app avec `NavigationProvider` :
22
+
23
+ ```tsx
24
+ import { NavigationProvider } from "@wasto/cms-components";
25
+ import { router } from "expo-router"; // ou React Navigation
26
+
27
+ <NavigationProvider
28
+ value={{
29
+ push: (href) => router.push(href as never),
30
+ canGoBack: () => router.canGoBack(),
31
+ goBack: () => router.back(),
32
+ }}
33
+ >
34
+ …
35
+ </NavigationProvider>
36
+ ```
37
+
38
+ ## Carte (`LocationsMap`)
39
+
40
+ ```ts
41
+ import { LocationsMap } from "@wasto/cms-components"
42
+ ```
43
+
44
+ - **iOS / Android** : Metro charge `LocationsMap.native.tsx` (MapLibre React Native).
45
+ - **Web** : Metro charge `LocationsMapView.web.tsx` (MapLibre GL JS, style OpenFreeMap) si `maplibre-gl` est installé ; sinon fallback pins (`LocationsMapView.tsx`).
46
+
47
+ Sur native : dépendance + plugin Expo `@maplibre/maplibre-react-native`, puis rebuild (`expo run:ios`). pnpm : le module doit être résolvable depuis la racine (`shamefully-hoist` ou `extraNodeModules`).
package/dist/index.d.mts CHANGED
@@ -65,12 +65,96 @@ type ImageUrlProps = {
65
65
  type ImageProps = ImageUrlProps | ImageMediaProps;
66
66
  declare function Image(props: ImageProps): ReactNode;
67
67
 
68
+ type HoursDayRow = {
69
+ label: string;
70
+ hours: string;
71
+ closed: boolean;
72
+ };
73
+ type HoursStatus = {
74
+ open: boolean;
75
+ text: string;
76
+ };
77
+
78
+ type LocationCategory = "recycling" | "decheterie" | "collection_place" | "sorting_center";
79
+ type MapLocation = {
80
+ id: string;
81
+ name: string;
82
+ category: LocationCategory;
83
+ address?: string;
84
+ phone?: string;
85
+ hoursDisplay?: string;
86
+ hoursRows?: HoursDayRow[];
87
+ hoursStatus?: HoursStatus | null;
88
+ lat: number;
89
+ lon: number;
90
+ badges?: string[];
91
+ };
92
+
93
+ type AppHeaderSlot = "left" | "center" | "right";
94
+ type AppHeaderLogoElement = {
95
+ id: string;
96
+ type: "logo";
97
+ slot: AppHeaderSlot;
98
+ sourceType: "url" | "media";
99
+ url?: string;
100
+ mediaId?: string;
101
+ };
102
+ type AppHeaderTitleElement = {
103
+ id: string;
104
+ type: "title";
105
+ slot: AppHeaderSlot;
106
+ useScreenTitle?: boolean;
107
+ text?: string;
108
+ };
109
+ type AppHeaderActionElement = {
110
+ id: string;
111
+ type: "action";
112
+ slot: AppHeaderSlot;
113
+ icon: string;
114
+ label?: string;
115
+ action: "navigate" | "back";
116
+ href?: string;
117
+ };
118
+ type AppHeaderElement = AppHeaderLogoElement | AppHeaderTitleElement | AppHeaderActionElement;
119
+ type AppHeaderConfig = {
120
+ elements: AppHeaderElement[];
121
+ };
122
+ /** Actions standard (notifications + profil) pour navbar sans titre. */
123
+ declare const DEFAULT_HEADER_ACTIONS: readonly AppHeaderActionElement[];
124
+ type HeaderProps = {
125
+ readonly options: {
126
+ title?: string;
127
+ headerBackVisible?: boolean;
128
+ };
129
+ readonly route: {
130
+ name: string;
131
+ };
132
+ readonly navigation: {
133
+ canGoBack: () => boolean;
134
+ goBack: () => void;
135
+ };
136
+ /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
137
+ readonly overlay?: boolean;
138
+ /**
139
+ * Config layout (`header.elements`) — logo / actions.
140
+ * Si fournie (même vide), le fallback titre d’écran ne s’applique pas.
141
+ */
142
+ readonly headerConfig?: AppHeaderConfig | null;
143
+ };
144
+ declare function Header({ options, route, navigation, overlay, headerConfig, }: HeaderProps): ReactNode;
145
+
68
146
  type LocationsMapProps = {
69
147
  fullScreen?: boolean;
70
148
  showFilters?: boolean;
71
- /** Header flottant au-dessus de la carte (titre + cloche + profil). */
149
+ /** Header flottant au-dessus de la carte (même navbar que le layout, sans titre). */
72
150
  overlayHeader?: boolean;
151
+ /**
152
+ * @deprecated Ignoré — la navbar n’affiche plus de titre d’écran.
153
+ * Conservé pour compatibilité des configs CMS existantes.
154
+ */
73
155
  overlayTitle?: string;
156
+ /** Override optionnel ; sinon config du layout via `HeaderConfigProvider`. */
157
+ headerConfig?: AppHeaderConfig | null;
74
158
  };
75
159
 
76
160
  /**
@@ -94,13 +178,18 @@ declare function NextCollection({ calendarHref, showCalendarCta, dayOffset, }: N
94
178
  type PageProps = PropsWithChildren & {
95
179
  /** When false, content fills the screen without scrolling (e.g. fullScreen map). Default true. */
96
180
  scroll?: boolean;
181
+ /**
182
+ * Pull-to-refresh : force refresh du cache tri + carte (TTL 1 semaine).
183
+ * Default true when scrollable.
184
+ */
185
+ pullToRefresh?: boolean;
97
186
  };
98
187
  /**
99
188
  * Page shell: top safe area + optional vertical scroll.
100
189
  * No global horizontal padding — each block uses ContentInset or is full-bleed
101
190
  * (map, calendar grid). Bottom inset is handled by BottomTab when using tabs.
102
191
  */
103
- declare function Page({ children, scroll }: PageProps): ReactNode;
192
+ declare function Page({ children, scroll, pullToRefresh, }: PageProps): ReactNode;
104
193
 
105
194
  type ContentInsetProps = PropsWithChildren & {
106
195
  /** Use 12px horizontal instead of 16px */
@@ -144,54 +233,6 @@ type SearchProps = {
144
233
  };
145
234
  declare function Search({ searchType, placeholder, }: SearchProps): ReactNode;
146
235
 
147
- type AppHeaderSlot = "left" | "center" | "right";
148
- type AppHeaderLogoElement = {
149
- id: string;
150
- type: "logo";
151
- slot: AppHeaderSlot;
152
- sourceType: "url" | "media";
153
- url?: string;
154
- mediaId?: string;
155
- };
156
- type AppHeaderTitleElement = {
157
- id: string;
158
- type: "title";
159
- slot: AppHeaderSlot;
160
- useScreenTitle?: boolean;
161
- text?: string;
162
- };
163
- type AppHeaderActionElement = {
164
- id: string;
165
- type: "action";
166
- slot: AppHeaderSlot;
167
- icon: string;
168
- label?: string;
169
- action: "navigate" | "back";
170
- href?: string;
171
- };
172
- type AppHeaderElement = AppHeaderLogoElement | AppHeaderTitleElement | AppHeaderActionElement;
173
- type AppHeaderConfig = {
174
- elements: AppHeaderElement[];
175
- };
176
- type HeaderProps = {
177
- readonly options: {
178
- title?: string;
179
- headerBackVisible?: boolean;
180
- };
181
- readonly route: {
182
- name: string;
183
- };
184
- readonly navigation: {
185
- canGoBack: () => boolean;
186
- goBack: () => void;
187
- };
188
- /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
189
- readonly overlay?: boolean;
190
- /** Config layout (`header.elements`) — logo / titre / actions. */
191
- readonly headerConfig?: AppHeaderConfig | null;
192
- };
193
- declare function Header({ options, route, navigation, overlay, headerConfig, }: HeaderProps): ReactNode;
194
-
195
236
  type BottomTabProps = BottomTabBarProps;
196
237
  declare function BottomTab({ state, descriptors, navigation, }: BottomTabProps): ReactNode;
197
238
 
@@ -360,6 +401,95 @@ type WastoProviderProps = PropsWithChildren & {
360
401
  declare function WastoProvider(props: WastoProviderProps): ReactNode;
361
402
  declare function useWasto(): WastoContextType;
362
403
 
404
+ type HeaderConfigProviderProps = {
405
+ readonly value: AppHeaderConfig | null;
406
+ readonly children: ReactNode;
407
+ };
408
+ declare function HeaderConfigProvider({ value, children, }: HeaderConfigProviderProps): ReactNode;
409
+ /** Config header du layout parent (`null` si hors provider / non défini). */
410
+ declare function useHeaderConfig(): AppHeaderConfig | null;
411
+
412
+ type City = {
413
+ id: string;
414
+ name: string;
415
+ status?: "NON_ACTIF" | "INCOMPLET" | "ACTIF" | "MAINTENANCE";
416
+ [key: string]: unknown;
417
+ };
418
+ type WasteType = {
419
+ id: string;
420
+ name?: string;
421
+ icon?: string;
422
+ imageUrl?: string | null;
423
+ color?: string;
424
+ [key: string]: unknown;
425
+ };
426
+ type WasteCollectionPlace = {
427
+ id: string;
428
+ name?: string;
429
+ collectionDays?: Array<{
430
+ /** Ex: "Lundi", "Mardi", ... (mapping lundi=0 … dimanche=6) */
431
+ name: string;
432
+ wasteTypeId: string;
433
+ biweekly?: boolean;
434
+ startDate?: string;
435
+ }>;
436
+ /** Périodes d’exception (pas de collecte) */
437
+ exceptions?: Array<{
438
+ dateFrom: string;
439
+ dateTo: string;
440
+ }>;
441
+ [key: string]: unknown;
442
+ };
443
+ type CollectionForDay = {
444
+ id: string;
445
+ name: string;
446
+ icon?: string;
447
+ imageUrl?: string | null;
448
+ color?: string;
449
+ contrastColor?: "#000000" | "#FFFFFF";
450
+ };
451
+
452
+ /** Fenêtre préchargée pour le carrousel « prochaines collectes ». */
453
+ declare const NEXT_EVENTS_DAY_RADIUS = 3;
454
+ type AppDataContextType = {
455
+ /** True tant que events ±3j + mois calendrier ne sont pas prêts (loader post-splash). */
456
+ isBootstrapping: boolean;
457
+ city: City | null;
458
+ wasteTypes: WasteType[];
459
+ wasteCollectionPlaces: WasteCollectionPlace[];
460
+ /** Collectes précalculées pour offsets -3 … +3. */
461
+ nextEventsByOffset: Record<number, CollectionForDay[]>;
462
+ locations: MapLocation[];
463
+ mapCenter: {
464
+ lat: number;
465
+ lon: number;
466
+ };
467
+ isMapLoading: boolean;
468
+ isRecyclingLoading: boolean;
469
+ scheduleError: string | null;
470
+ mapError: string | null;
471
+ /**
472
+ * Force-refresh tri + carte (invalide le TTL).
473
+ * Utilisé par le pull-to-refresh.
474
+ */
475
+ refreshCachedData: () => Promise<void>;
476
+ /** Recharge events + calendrier (critique). */
477
+ refreshSchedule: () => Promise<void>;
478
+ };
479
+ declare function useAppData(): AppDataContextType;
480
+ /** Safe variant for hooks that may run outside (preview / tests). */
481
+ declare function useOptionalAppData(): AppDataContextType | null;
482
+
483
+ type RegisterExpoPushTokenOptions = {
484
+ deviceId: string;
485
+ registerPushToken: (deviceId: string, token: string) => Promise<unknown>;
486
+ };
487
+ /**
488
+ * Request OS permission, fetch Expo push token, register it on the API.
489
+ * No-op on web, preview, missing peer, missing EAS projectId, or denied permission.
490
+ */
491
+ declare function registerExpoPushToken(options: RegisterExpoPushTokenOptions): Promise<string | null>;
492
+
363
493
  /** Shared Wasto HTTP client wired with optional API error reporting. */
364
494
  declare function useWastoClient(): WastoClient;
365
495
 
@@ -462,4 +592,4 @@ declare function useAppNavigation(): NavigationApi;
462
592
  /** Soft read — returns null when no provider (e.g. isolated component tests). */
463
593
  declare function useOptionalAppNavigation(): NavigationApi | null;
464
594
 
465
- export { type AppHeaderActionElement, type AppHeaderConfig, type AppHeaderElement, type AppHeaderLogoElement, type AppHeaderSlot, type AppHeaderTitleElement, Article, type ArticleDisplayMode, type ArticleProps, BottomTab, type BottomTabProps, Challenge, type ChallengeProps, ChallengeVignette, type ChallengeVignetteProps, Chat, type ChatProps, CollectionCalendar, type CollectionCalendarProps, Columns, type ColumnsColumnCount, type ColumnsProps, ContentInset, type ContentInsetProps, Header, type HeaderProps, Image, type ImageProps, LocationsMap, type LocationsMapProps, MAQUETTE_COLORS, MAQUETTE_RADII, type NavigationApi, NavigationProvider, type NavigationProviderProps, NextCollection, type NextCollectionProps, NotificationsScreen, Page, type PageProps, Paragraph, type PararaphProps, ProfileScreen, RecyclingInfo, type RecyclingInfoProps, type ResolvedTheme, SPACE, SYSTEM_ROUTES, Search, type SearchProps, type SystemRouteKey, type Theme, type ThemeShadowStyle, Title, type TitleLevel, type TitleProps, Vision, type VisionActionCardProps, type VisionCaptureResult, type VisionProps, type VisionSuggestion, type WastoContextType, type WastoErrorContext, WastoProvider, type WastoProviderProps, type WastoSentryConfig, cardStyle, coloredShadow, createMaquetteTheme, groupedSectionStyle, initWastoSentry, isWastoSentryActive, reportError, resolveTheme, softCardStyle, useAppNavigation, useOptionalAppNavigation, useWasto, useWastoClient, wrapWithSentry };
595
+ export { type AppDataContextType, type AppHeaderActionElement, type AppHeaderConfig, type AppHeaderElement, type AppHeaderLogoElement, type AppHeaderSlot, type AppHeaderTitleElement, Article, type ArticleDisplayMode, type ArticleProps, BottomTab, type BottomTabProps, Challenge, type ChallengeProps, ChallengeVignette, type ChallengeVignetteProps, Chat, type ChatProps, CollectionCalendar, type CollectionCalendarProps, Columns, type ColumnsColumnCount, type ColumnsProps, ContentInset, type ContentInsetProps, DEFAULT_HEADER_ACTIONS, Header, HeaderConfigProvider, type HeaderConfigProviderProps, type HeaderProps, Image, type ImageProps, LocationsMap, type LocationsMapProps, MAQUETTE_COLORS, MAQUETTE_RADII, NEXT_EVENTS_DAY_RADIUS, type NavigationApi, NavigationProvider, type NavigationProviderProps, NextCollection, type NextCollectionProps, NotificationsScreen, Page, type PageProps, Paragraph, type PararaphProps, ProfileScreen, RecyclingInfo, type RecyclingInfoProps, type RegisterExpoPushTokenOptions, type ResolvedTheme, SPACE, SYSTEM_ROUTES, Search, type SearchProps, type SystemRouteKey, type Theme, type ThemeShadowStyle, Title, type TitleLevel, type TitleProps, Vision, type VisionActionCardProps, type VisionCaptureResult, type VisionProps, type VisionSuggestion, type WastoContextType, type WastoErrorContext, WastoProvider, type WastoProviderProps, type WastoSentryConfig, cardStyle, coloredShadow, createMaquetteTheme, groupedSectionStyle, initWastoSentry, isWastoSentryActive, registerExpoPushToken, reportError, resolveTheme, softCardStyle, useAppData, useAppNavigation, useHeaderConfig, useOptionalAppData, useOptionalAppNavigation, useWasto, useWastoClient, wrapWithSentry };
package/dist/index.d.ts CHANGED
@@ -65,12 +65,96 @@ type ImageUrlProps = {
65
65
  type ImageProps = ImageUrlProps | ImageMediaProps;
66
66
  declare function Image(props: ImageProps): ReactNode;
67
67
 
68
+ type HoursDayRow = {
69
+ label: string;
70
+ hours: string;
71
+ closed: boolean;
72
+ };
73
+ type HoursStatus = {
74
+ open: boolean;
75
+ text: string;
76
+ };
77
+
78
+ type LocationCategory = "recycling" | "decheterie" | "collection_place" | "sorting_center";
79
+ type MapLocation = {
80
+ id: string;
81
+ name: string;
82
+ category: LocationCategory;
83
+ address?: string;
84
+ phone?: string;
85
+ hoursDisplay?: string;
86
+ hoursRows?: HoursDayRow[];
87
+ hoursStatus?: HoursStatus | null;
88
+ lat: number;
89
+ lon: number;
90
+ badges?: string[];
91
+ };
92
+
93
+ type AppHeaderSlot = "left" | "center" | "right";
94
+ type AppHeaderLogoElement = {
95
+ id: string;
96
+ type: "logo";
97
+ slot: AppHeaderSlot;
98
+ sourceType: "url" | "media";
99
+ url?: string;
100
+ mediaId?: string;
101
+ };
102
+ type AppHeaderTitleElement = {
103
+ id: string;
104
+ type: "title";
105
+ slot: AppHeaderSlot;
106
+ useScreenTitle?: boolean;
107
+ text?: string;
108
+ };
109
+ type AppHeaderActionElement = {
110
+ id: string;
111
+ type: "action";
112
+ slot: AppHeaderSlot;
113
+ icon: string;
114
+ label?: string;
115
+ action: "navigate" | "back";
116
+ href?: string;
117
+ };
118
+ type AppHeaderElement = AppHeaderLogoElement | AppHeaderTitleElement | AppHeaderActionElement;
119
+ type AppHeaderConfig = {
120
+ elements: AppHeaderElement[];
121
+ };
122
+ /** Actions standard (notifications + profil) pour navbar sans titre. */
123
+ declare const DEFAULT_HEADER_ACTIONS: readonly AppHeaderActionElement[];
124
+ type HeaderProps = {
125
+ readonly options: {
126
+ title?: string;
127
+ headerBackVisible?: boolean;
128
+ };
129
+ readonly route: {
130
+ name: string;
131
+ };
132
+ readonly navigation: {
133
+ canGoBack: () => boolean;
134
+ goBack: () => void;
135
+ };
136
+ /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
137
+ readonly overlay?: boolean;
138
+ /**
139
+ * Config layout (`header.elements`) — logo / actions.
140
+ * Si fournie (même vide), le fallback titre d’écran ne s’applique pas.
141
+ */
142
+ readonly headerConfig?: AppHeaderConfig | null;
143
+ };
144
+ declare function Header({ options, route, navigation, overlay, headerConfig, }: HeaderProps): ReactNode;
145
+
68
146
  type LocationsMapProps = {
69
147
  fullScreen?: boolean;
70
148
  showFilters?: boolean;
71
- /** Header flottant au-dessus de la carte (titre + cloche + profil). */
149
+ /** Header flottant au-dessus de la carte (même navbar que le layout, sans titre). */
72
150
  overlayHeader?: boolean;
151
+ /**
152
+ * @deprecated Ignoré — la navbar n’affiche plus de titre d’écran.
153
+ * Conservé pour compatibilité des configs CMS existantes.
154
+ */
73
155
  overlayTitle?: string;
156
+ /** Override optionnel ; sinon config du layout via `HeaderConfigProvider`. */
157
+ headerConfig?: AppHeaderConfig | null;
74
158
  };
75
159
 
76
160
  /**
@@ -94,13 +178,18 @@ declare function NextCollection({ calendarHref, showCalendarCta, dayOffset, }: N
94
178
  type PageProps = PropsWithChildren & {
95
179
  /** When false, content fills the screen without scrolling (e.g. fullScreen map). Default true. */
96
180
  scroll?: boolean;
181
+ /**
182
+ * Pull-to-refresh : force refresh du cache tri + carte (TTL 1 semaine).
183
+ * Default true when scrollable.
184
+ */
185
+ pullToRefresh?: boolean;
97
186
  };
98
187
  /**
99
188
  * Page shell: top safe area + optional vertical scroll.
100
189
  * No global horizontal padding — each block uses ContentInset or is full-bleed
101
190
  * (map, calendar grid). Bottom inset is handled by BottomTab when using tabs.
102
191
  */
103
- declare function Page({ children, scroll }: PageProps): ReactNode;
192
+ declare function Page({ children, scroll, pullToRefresh, }: PageProps): ReactNode;
104
193
 
105
194
  type ContentInsetProps = PropsWithChildren & {
106
195
  /** Use 12px horizontal instead of 16px */
@@ -144,54 +233,6 @@ type SearchProps = {
144
233
  };
145
234
  declare function Search({ searchType, placeholder, }: SearchProps): ReactNode;
146
235
 
147
- type AppHeaderSlot = "left" | "center" | "right";
148
- type AppHeaderLogoElement = {
149
- id: string;
150
- type: "logo";
151
- slot: AppHeaderSlot;
152
- sourceType: "url" | "media";
153
- url?: string;
154
- mediaId?: string;
155
- };
156
- type AppHeaderTitleElement = {
157
- id: string;
158
- type: "title";
159
- slot: AppHeaderSlot;
160
- useScreenTitle?: boolean;
161
- text?: string;
162
- };
163
- type AppHeaderActionElement = {
164
- id: string;
165
- type: "action";
166
- slot: AppHeaderSlot;
167
- icon: string;
168
- label?: string;
169
- action: "navigate" | "back";
170
- href?: string;
171
- };
172
- type AppHeaderElement = AppHeaderLogoElement | AppHeaderTitleElement | AppHeaderActionElement;
173
- type AppHeaderConfig = {
174
- elements: AppHeaderElement[];
175
- };
176
- type HeaderProps = {
177
- readonly options: {
178
- title?: string;
179
- headerBackVisible?: boolean;
180
- };
181
- readonly route: {
182
- name: string;
183
- };
184
- readonly navigation: {
185
- canGoBack: () => boolean;
186
- goBack: () => void;
187
- };
188
- /** Flotte au-dessus du contenu (ex. carte) : fond transparent. */
189
- readonly overlay?: boolean;
190
- /** Config layout (`header.elements`) — logo / titre / actions. */
191
- readonly headerConfig?: AppHeaderConfig | null;
192
- };
193
- declare function Header({ options, route, navigation, overlay, headerConfig, }: HeaderProps): ReactNode;
194
-
195
236
  type BottomTabProps = BottomTabBarProps;
196
237
  declare function BottomTab({ state, descriptors, navigation, }: BottomTabProps): ReactNode;
197
238
 
@@ -360,6 +401,95 @@ type WastoProviderProps = PropsWithChildren & {
360
401
  declare function WastoProvider(props: WastoProviderProps): ReactNode;
361
402
  declare function useWasto(): WastoContextType;
362
403
 
404
+ type HeaderConfigProviderProps = {
405
+ readonly value: AppHeaderConfig | null;
406
+ readonly children: ReactNode;
407
+ };
408
+ declare function HeaderConfigProvider({ value, children, }: HeaderConfigProviderProps): ReactNode;
409
+ /** Config header du layout parent (`null` si hors provider / non défini). */
410
+ declare function useHeaderConfig(): AppHeaderConfig | null;
411
+
412
+ type City = {
413
+ id: string;
414
+ name: string;
415
+ status?: "NON_ACTIF" | "INCOMPLET" | "ACTIF" | "MAINTENANCE";
416
+ [key: string]: unknown;
417
+ };
418
+ type WasteType = {
419
+ id: string;
420
+ name?: string;
421
+ icon?: string;
422
+ imageUrl?: string | null;
423
+ color?: string;
424
+ [key: string]: unknown;
425
+ };
426
+ type WasteCollectionPlace = {
427
+ id: string;
428
+ name?: string;
429
+ collectionDays?: Array<{
430
+ /** Ex: "Lundi", "Mardi", ... (mapping lundi=0 … dimanche=6) */
431
+ name: string;
432
+ wasteTypeId: string;
433
+ biweekly?: boolean;
434
+ startDate?: string;
435
+ }>;
436
+ /** Périodes d’exception (pas de collecte) */
437
+ exceptions?: Array<{
438
+ dateFrom: string;
439
+ dateTo: string;
440
+ }>;
441
+ [key: string]: unknown;
442
+ };
443
+ type CollectionForDay = {
444
+ id: string;
445
+ name: string;
446
+ icon?: string;
447
+ imageUrl?: string | null;
448
+ color?: string;
449
+ contrastColor?: "#000000" | "#FFFFFF";
450
+ };
451
+
452
+ /** Fenêtre préchargée pour le carrousel « prochaines collectes ». */
453
+ declare const NEXT_EVENTS_DAY_RADIUS = 3;
454
+ type AppDataContextType = {
455
+ /** True tant que events ±3j + mois calendrier ne sont pas prêts (loader post-splash). */
456
+ isBootstrapping: boolean;
457
+ city: City | null;
458
+ wasteTypes: WasteType[];
459
+ wasteCollectionPlaces: WasteCollectionPlace[];
460
+ /** Collectes précalculées pour offsets -3 … +3. */
461
+ nextEventsByOffset: Record<number, CollectionForDay[]>;
462
+ locations: MapLocation[];
463
+ mapCenter: {
464
+ lat: number;
465
+ lon: number;
466
+ };
467
+ isMapLoading: boolean;
468
+ isRecyclingLoading: boolean;
469
+ scheduleError: string | null;
470
+ mapError: string | null;
471
+ /**
472
+ * Force-refresh tri + carte (invalide le TTL).
473
+ * Utilisé par le pull-to-refresh.
474
+ */
475
+ refreshCachedData: () => Promise<void>;
476
+ /** Recharge events + calendrier (critique). */
477
+ refreshSchedule: () => Promise<void>;
478
+ };
479
+ declare function useAppData(): AppDataContextType;
480
+ /** Safe variant for hooks that may run outside (preview / tests). */
481
+ declare function useOptionalAppData(): AppDataContextType | null;
482
+
483
+ type RegisterExpoPushTokenOptions = {
484
+ deviceId: string;
485
+ registerPushToken: (deviceId: string, token: string) => Promise<unknown>;
486
+ };
487
+ /**
488
+ * Request OS permission, fetch Expo push token, register it on the API.
489
+ * No-op on web, preview, missing peer, missing EAS projectId, or denied permission.
490
+ */
491
+ declare function registerExpoPushToken(options: RegisterExpoPushTokenOptions): Promise<string | null>;
492
+
363
493
  /** Shared Wasto HTTP client wired with optional API error reporting. */
364
494
  declare function useWastoClient(): WastoClient;
365
495
 
@@ -462,4 +592,4 @@ declare function useAppNavigation(): NavigationApi;
462
592
  /** Soft read — returns null when no provider (e.g. isolated component tests). */
463
593
  declare function useOptionalAppNavigation(): NavigationApi | null;
464
594
 
465
- export { type AppHeaderActionElement, type AppHeaderConfig, type AppHeaderElement, type AppHeaderLogoElement, type AppHeaderSlot, type AppHeaderTitleElement, Article, type ArticleDisplayMode, type ArticleProps, BottomTab, type BottomTabProps, Challenge, type ChallengeProps, ChallengeVignette, type ChallengeVignetteProps, Chat, type ChatProps, CollectionCalendar, type CollectionCalendarProps, Columns, type ColumnsColumnCount, type ColumnsProps, ContentInset, type ContentInsetProps, Header, type HeaderProps, Image, type ImageProps, LocationsMap, type LocationsMapProps, MAQUETTE_COLORS, MAQUETTE_RADII, type NavigationApi, NavigationProvider, type NavigationProviderProps, NextCollection, type NextCollectionProps, NotificationsScreen, Page, type PageProps, Paragraph, type PararaphProps, ProfileScreen, RecyclingInfo, type RecyclingInfoProps, type ResolvedTheme, SPACE, SYSTEM_ROUTES, Search, type SearchProps, type SystemRouteKey, type Theme, type ThemeShadowStyle, Title, type TitleLevel, type TitleProps, Vision, type VisionActionCardProps, type VisionCaptureResult, type VisionProps, type VisionSuggestion, type WastoContextType, type WastoErrorContext, WastoProvider, type WastoProviderProps, type WastoSentryConfig, cardStyle, coloredShadow, createMaquetteTheme, groupedSectionStyle, initWastoSentry, isWastoSentryActive, reportError, resolveTheme, softCardStyle, useAppNavigation, useOptionalAppNavigation, useWasto, useWastoClient, wrapWithSentry };
595
+ export { type AppDataContextType, type AppHeaderActionElement, type AppHeaderConfig, type AppHeaderElement, type AppHeaderLogoElement, type AppHeaderSlot, type AppHeaderTitleElement, Article, type ArticleDisplayMode, type ArticleProps, BottomTab, type BottomTabProps, Challenge, type ChallengeProps, ChallengeVignette, type ChallengeVignetteProps, Chat, type ChatProps, CollectionCalendar, type CollectionCalendarProps, Columns, type ColumnsColumnCount, type ColumnsProps, ContentInset, type ContentInsetProps, DEFAULT_HEADER_ACTIONS, Header, HeaderConfigProvider, type HeaderConfigProviderProps, type HeaderProps, Image, type ImageProps, LocationsMap, type LocationsMapProps, MAQUETTE_COLORS, MAQUETTE_RADII, NEXT_EVENTS_DAY_RADIUS, type NavigationApi, NavigationProvider, type NavigationProviderProps, NextCollection, type NextCollectionProps, NotificationsScreen, Page, type PageProps, Paragraph, type PararaphProps, ProfileScreen, RecyclingInfo, type RecyclingInfoProps, type RegisterExpoPushTokenOptions, type ResolvedTheme, SPACE, SYSTEM_ROUTES, Search, type SearchProps, type SystemRouteKey, type Theme, type ThemeShadowStyle, Title, type TitleLevel, type TitleProps, Vision, type VisionActionCardProps, type VisionCaptureResult, type VisionProps, type VisionSuggestion, type WastoContextType, type WastoErrorContext, WastoProvider, type WastoProviderProps, type WastoSentryConfig, cardStyle, coloredShadow, createMaquetteTheme, groupedSectionStyle, initWastoSentry, isWastoSentryActive, registerExpoPushToken, reportError, resolveTheme, softCardStyle, useAppData, useAppNavigation, useHeaderConfig, useOptionalAppData, useOptionalAppNavigation, useWasto, useWastoClient, wrapWithSentry };