react-native-platform-components 0.9.0 → 1.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.
@@ -465,7 +465,7 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
465
465
  addView(picker)
466
466
  }
467
467
 
468
- val dlg = AlertDialog.Builder(act)
468
+ val dlg = AlertDialog.Builder(PCThemeSupport.appCompatDialogContext(act, "DatePicker"))
469
469
  .setTitle(androidDialogTitle ?: "")
470
470
  .setView(container)
471
471
  .setPositiveButton(androidPositiveTitle ?: "OK") { _, _ ->
@@ -513,7 +513,7 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
513
513
  }
514
514
  }
515
515
 
516
- val dlg = AlertDialog.Builder(act)
516
+ val dlg = AlertDialog.Builder(PCThemeSupport.appCompatDialogContext(act, "DatePicker"))
517
517
  .setTitle(androidDialogTitle ?: "")
518
518
  .setView(picker)
519
519
  .setPositiveButton(androidPositiveTitle ?: "OK") { _, _ ->
@@ -580,7 +580,7 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
580
580
  addView(picker)
581
581
  }
582
582
 
583
- val dlg = AlertDialog.Builder(act)
583
+ val dlg = AlertDialog.Builder(PCThemeSupport.appCompatDialogContext(act, "DatePicker"))
584
584
  .setTitle(androidDialogTitle ?: "")
585
585
  .setView(container)
586
586
  .setPositiveButton(androidPositiveTitle ?: "Next") { _, _ ->
@@ -617,6 +617,7 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
617
617
 
618
618
  val builder = MaterialDatePicker.Builder.datePicker()
619
619
  .setSelection(ts)
620
+ .setTheme(m3CalendarTheme(act))
620
621
 
621
622
  androidDialogTitle?.let { builder.setTitleText(it) }
622
623
  androidPositiveTitle?.let { builder.setPositiveButtonText(it) }
@@ -660,6 +661,7 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
660
661
 
661
662
  val is24 = android.text.format.DateFormat.is24HourFormat(act)
662
663
  val builder = MaterialTimePicker.Builder()
664
+ .setTheme(m3TimePickerTheme(act))
663
665
  .setTimeFormat(if (is24) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H)
664
666
  .setHour(cal.get(Calendar.HOUR_OF_DAY))
665
667
  .setMinute(cal.get(Calendar.MINUTE))
@@ -698,6 +700,7 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
698
700
 
699
701
  val builder = MaterialDatePicker.Builder.datePicker()
700
702
  .setSelection(ts)
703
+ .setTheme(m3CalendarTheme(act))
701
704
 
702
705
  androidDialogTitle?.let { builder.setTitleText(it) }
703
706
  androidPositiveTitle?.let { builder.setPositiveButtonText(it) }
@@ -733,6 +736,17 @@ class PCDatePickerView(context: Context) : FrameLayout(context), ReactScrollView
733
736
  picker.show(act.supportFragmentManager, "PCDatePicker_M3_DATE_THEN_TIME")
734
737
  }
735
738
 
739
+ /**
740
+ * Material pickers resolve their theme from the activity. When the app theme is
741
+ * not a Material theme they would throw, so hand them a full Material 3 dialog
742
+ * theme instead (0 = use the activity theme).
743
+ */
744
+ private fun m3CalendarTheme(act: FragmentActivity): Int =
745
+ PCThemeSupport.materialDialogThemeOverride(act, "DatePicker", R.style.PCMaterial3CalendarDialogTheme)
746
+
747
+ private fun m3TimePickerTheme(act: FragmentActivity): Int =
748
+ PCThemeSupport.materialDialogThemeOverride(act, "DatePicker", R.style.PCMaterial3TimePickerDialogTheme)
749
+
736
750
  private fun buildM3CalendarConstraints(): CalendarConstraints? {
737
751
  val min = minDateMs
738
752
  val max = maxDateMs
@@ -190,7 +190,9 @@ class PCSegmentedControlView(context: Context) : FrameLayout(context), ReactScro
190
190
  rebuildGeneration += 1
191
191
  val generation = rebuildGeneration
192
192
 
193
- val group = MaterialButtonToggleGroup(context).apply {
193
+ // Material widgets need a Material theme; fall back to Material 3 defaults instead of crashing.
194
+ val themedContext = PCThemeSupport.materialContext(context, "SegmentedControl")
195
+ val group = MaterialButtonToggleGroup(themedContext).apply {
194
196
  layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
195
197
  isSingleSelection = true
196
198
  isSelectionRequired = selectionRequired
@@ -207,7 +209,7 @@ class PCSegmentedControlView(context: Context) : FrameLayout(context), ReactScro
207
209
  // without one keep their label. "auto" and "labeled" show icon + label.
208
210
  val iconOnly = labelVisibility == "unlabeled" && segment.hasIcon
209
211
 
210
- val button = MaterialButton(context, null, com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
212
+ val button = MaterialButton(themedContext, null, com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
211
213
  id = View.generateViewId()
212
214
  text = if (iconOnly) "" else segment.label
213
215
  isAllCaps = false // Preserve original text casing
@@ -311,7 +313,7 @@ class PCSegmentedControlView(context: Context) : FrameLayout(context), ReactScro
311
313
  if (segment.badge.isEmpty()) return
312
314
 
313
315
  val inset = (2 * resources.displayMetrics.density).toInt()
314
- val badge = BadgeDrawable.create(context).apply {
316
+ val badge = BadgeDrawable.create(button.context).apply {
315
317
  text = segment.badge
316
318
  badgeGravity = BadgeDrawable.TOP_END
317
319
  badgeBackgroundColor?.let { backgroundColor = it }
@@ -356,7 +356,8 @@ class PCSelectionMenuView(context: Context) : FrameLayout(context), ReactScrollV
356
356
  if (mode == MaterialMode.M3) {
357
357
  // M3 exposed dropdown menu - the standard Material 3 way
358
358
  // Must set box background mode BEFORE setting endIconMode to avoid IllegalStateException
359
- val til = TextInputLayout(context).apply {
359
+ // Material widgets need a Material theme; fall back to Material 3 defaults instead of crashing.
360
+ val til = TextInputLayout(PCThemeSupport.materialContext(context, "SelectionMenu")).apply {
360
361
  layoutParams = FrameLayout.LayoutParams(
361
362
  FrameLayout.LayoutParams.WRAP_CONTENT,
362
363
  FrameLayout.LayoutParams.WRAP_CONTENT
@@ -0,0 +1,94 @@
1
+ package com.platformcomponents
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import android.util.TypedValue
6
+ import android.view.ContextThemeWrapper
7
+ import androidx.annotation.AttrRes
8
+ import androidx.appcompat.R as AppCompatR
9
+ import com.google.android.material.R as MaterialR
10
+
11
+ /**
12
+ * Theme guards for the Material and AppCompat widgets this library creates.
13
+ *
14
+ * React Native and Expo templates ship an AppCompat app theme. Material widgets
15
+ * (MaterialButtonToggleGroup, TextInputLayout, MaterialDatePicker, ...) throw during
16
+ * inflation when the host theme is not a Material Components / Material 3 theme.
17
+ * Rather than crash the app, wrap the context in a Material 3 theme (widgets) or
18
+ * hand the picker dialogs a full Material 3 dialog theme, and warn once so the
19
+ * developer knows how to get their own colors applied.
20
+ */
21
+ internal object PCThemeSupport {
22
+ private const val TAG = "PlatformComponents"
23
+
24
+ @Volatile private var warnedMaterial = false
25
+ @Volatile private var warnedAppCompat = false
26
+
27
+ /** True for Theme.MaterialComponents.* and Theme.Material3.* (and descendants). */
28
+ fun hasMaterialTheme(context: Context): Boolean =
29
+ resolves(context, MaterialR.attr.materialButtonOutlinedStyle) &&
30
+ resolves(context, MaterialR.attr.colorSurface)
31
+
32
+ /** True for Theme.AppCompat.* and descendants, which includes every Material theme. */
33
+ fun hasAppCompatTheme(context: Context): Boolean =
34
+ resolves(context, AppCompatR.attr.alertDialogTheme)
35
+
36
+ /**
37
+ * Context to build Material widgets from. The host context when its theme is a
38
+ * Material theme; otherwise a Theme.Material3.DayNight wrapper, so the widget
39
+ * renders with Material 3 defaults instead of throwing.
40
+ */
41
+ fun materialContext(context: Context, component: String): Context {
42
+ if (hasMaterialTheme(context)) return context
43
+ warnMaterial(component)
44
+ return ContextThemeWrapper(context, MaterialR.style.Theme_Material3_DayNight_NoActionBar)
45
+ }
46
+
47
+ /**
48
+ * Context to build AppCompat dialogs from. The host context when its theme is an
49
+ * AppCompat theme; otherwise a Theme.AppCompat.DayNight.Dialog.Alert wrapper.
50
+ */
51
+ fun appCompatDialogContext(context: Context, component: String): Context {
52
+ if (hasAppCompatTheme(context)) return context
53
+ warnAppCompat(component)
54
+ return ContextThemeWrapper(context, AppCompatR.style.Theme_AppCompat_DayNight_Dialog_Alert)
55
+ }
56
+
57
+ /**
58
+ * Theme override for MaterialDatePicker / MaterialTimePicker when the activity
59
+ * theme is not a Material theme. Returns 0 (no override) when it is.
60
+ */
61
+ fun materialDialogThemeOverride(context: Context, component: String, fallbackTheme: Int): Int {
62
+ if (hasMaterialTheme(context)) return 0
63
+ warnMaterial(component)
64
+ return fallbackTheme
65
+ }
66
+
67
+ private fun resolves(context: Context, @AttrRes attr: Int): Boolean =
68
+ context.theme.resolveAttribute(attr, TypedValue(), true)
69
+
70
+ private fun warnMaterial(component: String) {
71
+ if (warnedMaterial) return
72
+ warnedMaterial = true
73
+ Log.w(
74
+ TAG,
75
+ "$component: the app theme is not a Material 3 theme, so Material components " +
76
+ "fall back to Material 3 default colors. To use your app's colors, make " +
77
+ "AppTheme inherit from Theme.Material3.DayNight.NoActionBar in " +
78
+ "android/app/src/main/res/values/styles.xml (Expo: pass " +
79
+ "{ android: { theme: 'material3' } } to the react-native-platform-components " +
80
+ "config plugin). See the README section \"Android Theme Configuration\"."
81
+ )
82
+ }
83
+
84
+ private fun warnAppCompat(component: String) {
85
+ if (warnedAppCompat) return
86
+ warnedAppCompat = true
87
+ Log.w(
88
+ TAG,
89
+ "$component: the app theme is not an AppCompat theme, so system dialogs fall " +
90
+ "back to Theme.AppCompat.DayNight.Dialog.Alert. Make AppTheme inherit from " +
91
+ "Theme.AppCompat.* or Theme.Material3.* to style them with your theme."
92
+ )
93
+ }
94
+ }
@@ -0,0 +1,40 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <!--
4
+ Fallback dialog themes for MaterialDatePicker / MaterialTimePicker when the host
5
+ app theme is not a Material theme (see PCThemeSupport). Each is a full
6
+ Theme.Material3 dialog theme plus the items from ThemeOverlay.Material3.MaterialCalendar
7
+ and ThemeOverlay.Material3.MaterialTimePicker (material 1.12.0), which the pickers
8
+ skip resolving when a theme override is supplied.
9
+ -->
10
+ <style name="PCMaterial3CalendarDialogTheme" parent="Theme.Material3.DayNight.Dialog">
11
+ <item name="buttonBarButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog.Flush</item>
12
+ <item name="materialCalendarStyle">@style/Widget.Material3.MaterialCalendar</item>
13
+ <item name="materialCalendarHeaderLayout">@style/Widget.Material3.MaterialCalendar.HeaderLayout</item>
14
+ <item name="materialCalendarHeaderDivider">@style/Widget.Material3.MaterialCalendar.HeaderDivider</item>
15
+ <item name="materialCalendarHeaderTitle">@style/Widget.Material3.MaterialCalendar.HeaderTitle</item>
16
+ <item name="materialCalendarHeaderSelection">@style/Widget.Material3.MaterialCalendar.HeaderSelection</item>
17
+ <item name="materialCalendarHeaderCancelButton">@style/Widget.Material3.MaterialCalendar.HeaderCancelButton</item>
18
+ <item name="materialCalendarHeaderConfirmButton">@style/Widget.Material3.Button.TextButton</item>
19
+ <item name="materialCalendarHeaderToggleButton">@style/Widget.Material3.MaterialCalendar.HeaderToggleButton</item>
20
+ <item name="materialCalendarYearNavigationButton">@style/Widget.Material3.MaterialCalendar.YearNavigationButton</item>
21
+ <item name="materialCalendarMonthNavigationButton">@style/Widget.Material3.MaterialCalendar.MonthNavigationButton</item>
22
+ <item name="materialCalendarDayOfWeekLabel">@style/Widget.Material3.MaterialCalendar.DayOfWeekLabel</item>
23
+ <item name="materialCalendarDay">@style/Widget.Material3.MaterialCalendar.DayTextView</item>
24
+ <item name="materialCalendarMonth">@style/Widget.Material3.MaterialCalendar.MonthTextView</item>
25
+ <item name="buttonBarPositiveButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog</item>
26
+ <item name="buttonBarNegativeButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog</item>
27
+ </style>
28
+
29
+ <style name="PCMaterial3TimePickerDialogTheme" parent="Theme.Material3.DayNight.Dialog">
30
+ <item name="buttonBarButtonStyle">@style/Widget.Material3.Button.TextButton.Dialog.Flush</item>
31
+ <item name="chipStyle">@style/Widget.Material3.MaterialTimePicker.Display</item>
32
+ <item name="textInputStyle">@style/Widget.Material3.MaterialTimePicker.Display.TextInputLayout</item>
33
+ <item name="materialDisplayDividerStyle">@style/Widget.Material3.MaterialTimePicker.Display.Divider</item>
34
+ <item name="materialButtonOutlinedStyle">@style/Widget.Material3.MaterialTimePicker.Button</item>
35
+ <item name="materialClockStyle">@style/Widget.Material3.MaterialTimePicker.Clock</item>
36
+ <item name="materialTimePickerStyle">@style/Widget.Material3.MaterialTimePicker</item>
37
+ <item name="imageButtonStyle">@style/Widget.Material3.MaterialTimePicker.ImageButton</item>
38
+ <item name="materialTimePickerTitleStyle">@style/TextAppearance.Material3.MaterialTimePicker.Title</item>
39
+ </style>
40
+ </resources>
@@ -0,0 +1,4 @@
1
+ import type { Config } from '@docusaurus/types';
2
+ declare const config: Config;
3
+ export default config;
4
+ //# sourceMappingURL=docusaurus.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docusaurus.config.d.ts","sourceRoot":"","sources":["../../../../docs/docusaurus.config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAMhD,QAAA,MAAM,MAAM,EAAE,MA+Eb,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { SidebarsConfig } from '@docusaurus/plugin-content-docs';
2
+ declare const sidebars: SidebarsConfig;
3
+ export default sidebars;
4
+ //# sourceMappingURL=sidebars.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebars.d.ts","sourceRoot":"","sources":["../../../../docs/sidebars.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,QAAA,MAAM,QAAQ,EAAE,cAwBf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Config } from '@docusaurus/types';
2
+ declare const config: Config;
3
+ export default config;
4
+ //# sourceMappingURL=docusaurus.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docusaurus.config.d.ts","sourceRoot":"","sources":["../../../../docs/docusaurus.config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAMhD,QAAA,MAAM,MAAM,EAAE,MA+Eb,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { SidebarsConfig } from '@docusaurus/plugin-content-docs';
2
+ declare const sidebars: SidebarsConfig;
3
+ export default sidebars;
4
+ //# sourceMappingURL=sidebars.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebars.d.ts","sourceRoot":"","sources":["../../../../docs/sidebars.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,QAAA,MAAM,QAAQ,EAAE,cAwBf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-platform-components",
3
- "version": "0.9.0",
4
- "description": "Native UI components for React Native: DatePicker, ContextMenu, SelectionMenu, SegmentedControl, LiquidGlass.",
3
+ "version": "1.0.0",
4
+ "description": "Native UI components for React Native that are native on Android too: DatePicker, ContextMenu, SelectionMenu, SegmentedControl (Material 3) and LiquidGlass (iOS 26). Fabric, typed, Expo config plugin.",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",
7
7
  "browser": "./lib/module/index.web.js",
@@ -57,6 +57,7 @@
57
57
  "scripts": {
58
58
  "example": "yarn workspace react-native-platform-components-example",
59
59
  "example-expo": "yarn workspace react-native-platform-components-example-expo",
60
+ "docs": "yarn workspace react-native-platform-components-docs",
60
61
  "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib plugin/build",
61
62
  "prepare": "bob build && yarn build:plugin",
62
63
  "typecheck": "tsc",
@@ -71,26 +72,42 @@
71
72
  },
72
73
  "keywords": [
73
74
  "react-native",
75
+ "expo",
76
+ "expo-config-plugin",
74
77
  "ios",
75
78
  "android",
79
+ "fabric",
80
+ "new-architecture",
81
+ "native-ui",
82
+ "ui-components",
76
83
  "datepicker",
77
84
  "date-picker",
78
85
  "time-picker",
79
- "picker",
86
+ "datetimepicker",
87
+ "uidatepicker",
88
+ "material-date-picker",
80
89
  "menu",
81
- "selection-menu",
82
90
  "context-menu",
91
+ "contextmenu",
92
+ "popup-menu",
93
+ "uicontextmenu",
94
+ "selection-menu",
83
95
  "dropdown",
84
- "native",
85
- "material-design",
86
- "expo",
96
+ "picker",
87
97
  "segmented-control",
88
98
  "segmentedcontrol",
89
- "tabs",
90
99
  "segmented-buttons",
91
100
  "segmentedbuttons",
101
+ "uisegmentedcontrol",
102
+ "tabs",
92
103
  "liquid-glass",
93
- "liquidglass"
104
+ "liquidglass",
105
+ "glass-effect",
106
+ "uiglasseffect",
107
+ "material-design",
108
+ "material3",
109
+ "material-3",
110
+ "material-you"
94
111
  ],
95
112
  "repository": {
96
113
  "type": "git",
@@ -141,12 +158,13 @@
141
158
  "typescript": "~6.0.3"
142
159
  },
143
160
  "peerDependencies": {
144
- "react": "*",
145
- "react-native": "*"
161
+ "react": ">=19.0.0",
162
+ "react-native": ">=0.81.0"
146
163
  },
147
164
  "workspaces": [
148
165
  "example",
149
- "example-expo"
166
+ "example-expo",
167
+ "docs"
150
168
  ],
151
169
  "packageManager": "yarn@4.11.0",
152
170
  "react-native-builder-bob": {
@@ -218,7 +236,9 @@
218
236
  "@release-it/conventional-changelog": {
219
237
  "preset": {
220
238
  "name": "angular"
221
- }
239
+ },
240
+ "infile": "CHANGELOG.md",
241
+ "header": "# Changelog"
222
242
  }
223
243
  }
224
244
  },
@@ -226,6 +246,7 @@
226
246
  "preset": "@react-native/jest-preset",
227
247
  "modulePathIgnorePatterns": [
228
248
  "<rootDir>/example/node_modules",
249
+ "<rootDir>/docs/",
229
250
  "<rootDir>/lib/"
230
251
  ],
231
252
  "testPathIgnorePatterns": [
@@ -1,26 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MATERIAL3_THEME_PARENT = void 0;
4
+ exports.resolveMaterial3Parent = resolveMaterial3Parent;
5
+ exports.applyMaterial3Theme = applyMaterial3Theme;
6
+ const config_plugins_1 = require("@expo/config-plugins");
7
+ exports.MATERIAL3_THEME_PARENT = 'Theme.Material3.DayNight.NoActionBar';
8
+ /**
9
+ * `react-native-edge-to-edge` re-parents `AppTheme` onto its own themes. When we
10
+ * see one of those, switch to the matching Material 3 flavour instead of
11
+ * replacing it, so edge-to-edge keeps working.
12
+ */
13
+ const EDGE_TO_EDGE_PREFIX = 'Theme.EdgeToEdge';
14
+ const APP_THEME_NAME = 'AppTheme';
15
+ function resolveMaterial3Parent(currentParent) {
16
+ if (!currentParent || !currentParent.startsWith(EDGE_TO_EDGE_PREFIX)) {
17
+ return exports.MATERIAL3_THEME_PARENT;
18
+ }
19
+ if (currentParent.includes('.Material3')) {
20
+ return currentParent;
21
+ }
22
+ const suffix = currentParent.slice(EDGE_TO_EDGE_PREFIX.length); // '' | '.Light'
23
+ return `${EDGE_TO_EDGE_PREFIX}.Material3${suffix}`;
24
+ }
25
+ /**
26
+ * Re-parents the app theme onto Material 3, keeping every existing `<item>`
27
+ * (`colorPrimary`, `android:editTextBackground`, status bar colors, ...).
28
+ * Idempotent: running prebuild again leaves the result unchanged.
29
+ */
30
+ function applyMaterial3Theme(xml) {
31
+ const styles = xml.resources.style ?? [];
32
+ const appTheme = styles.find((style) => style.$.name === APP_THEME_NAME);
33
+ if (appTheme) {
34
+ appTheme.$.parent = resolveMaterial3Parent(appTheme.$.parent);
35
+ }
36
+ else {
37
+ styles.push({
38
+ $: { name: APP_THEME_NAME, parent: exports.MATERIAL3_THEME_PARENT },
39
+ item: [],
40
+ });
41
+ }
42
+ xml.resources.style = styles;
43
+ return xml;
44
+ }
3
45
  /**
4
46
  * Expo config plugin for react-native-platform-components.
5
47
  *
6
- * This library requires no manual native configuration on either iOS or Android.
7
- * The native modules are automatically linked via Expo prebuild and React Native's
8
- * autolinking system.
9
- *
10
- * iOS:
11
- * - Pod linkage works automatically via the standard podspec
12
- * - No Info.plist, entitlements, or AppDelegate changes required
13
- *
14
- * Android:
15
- * - Gradle linkage works automatically via autolinking
16
- * - No AndroidManifest.xml, theme, or permission changes required
48
+ * Native linking needs no configuration on either platform: CocoaPods and
49
+ * Gradle autolinking pick the library up during `expo prebuild`.
17
50
  *
18
- * This plugin is provided for discoverability and to enable future configuration
19
- * options if needed. Running prebuild multiple times is idempotent.
51
+ * The plugin's job is the Android theme. Expo's generated `AppTheme` inherits
52
+ * from AppCompat, which is enough for ContextMenu, LiquidGlass and the
53
+ * `material: 'system'` modes, but Material 3 widgets look best under a
54
+ * Material 3 app theme. Pass `{ android: { theme: 'material3' } }` to have
55
+ * prebuild re-parent `AppTheme` accordingly.
20
56
  */
21
- const withPlatformComponents = (config) => {
22
- // No-op plugin: react-native-platform-components requires no native configuration
23
- // beyond standard autolinking which Expo prebuild handles automatically.
24
- return config;
57
+ const withPlatformComponents = (config, options) => {
58
+ const theme = options?.android?.theme ?? 'appcompat';
59
+ if (theme !== 'material3') {
60
+ return config;
61
+ }
62
+ return (0, config_plugins_1.withAndroidStyles)(config, (styleConfig) => {
63
+ styleConfig.modResults = applyMaterial3Theme(styleConfig.modResults);
64
+ return styleConfig;
65
+ });
25
66
  };
26
67
  exports.default = withPlatformComponents;
@@ -1,22 +1,39 @@
1
- import type { ConfigPlugin } from '@expo/config-plugins';
1
+ import { type AndroidConfig, type ConfigPlugin } from '@expo/config-plugins';
2
+ type ResourceXML = AndroidConfig.Resources.ResourceXML;
2
3
  /**
3
- * Expo config plugin for react-native-platform-components.
4
- *
5
- * This library requires no manual native configuration on either iOS or Android.
6
- * The native modules are automatically linked via Expo prebuild and React Native's
7
- * autolinking system.
4
+ * Parent theme for the Android `AppTheme` style.
8
5
  *
9
- * iOS:
10
- * - Pod linkage works automatically via the standard podspec
11
- * - No Info.plist, entitlements, or AppDelegate changes required
6
+ * - `'material3'`: re-parent `AppTheme` onto `Theme.Material3.DayNight.NoActionBar`.
7
+ * Required for `SegmentedControl` and for `DatePicker` / `SelectionMenu` in
8
+ * `android={{ material: 'm3' }}` mode to pick up your app's Material colors.
9
+ * - `'appcompat'` (default): leave Expo's `Theme.AppCompat.DayNight.NoActionBar` as is.
10
+ */
11
+ export type AndroidTheme = 'material3' | 'appcompat';
12
+ export type PlatformComponentsPluginOptions = {
13
+ android?: {
14
+ theme?: AndroidTheme;
15
+ };
16
+ };
17
+ export declare const MATERIAL3_THEME_PARENT = "Theme.Material3.DayNight.NoActionBar";
18
+ export declare function resolveMaterial3Parent(currentParent: string | undefined): string;
19
+ /**
20
+ * Re-parents the app theme onto Material 3, keeping every existing `<item>`
21
+ * (`colorPrimary`, `android:editTextBackground`, status bar colors, ...).
22
+ * Idempotent: running prebuild again leaves the result unchanged.
23
+ */
24
+ export declare function applyMaterial3Theme(xml: ResourceXML): ResourceXML;
25
+ /**
26
+ * Expo config plugin for react-native-platform-components.
12
27
  *
13
- * Android:
14
- * - Gradle linkage works automatically via autolinking
15
- * - No AndroidManifest.xml, theme, or permission changes required
28
+ * Native linking needs no configuration on either platform: CocoaPods and
29
+ * Gradle autolinking pick the library up during `expo prebuild`.
16
30
  *
17
- * This plugin is provided for discoverability and to enable future configuration
18
- * options if needed. Running prebuild multiple times is idempotent.
31
+ * The plugin's job is the Android theme. Expo's generated `AppTheme` inherits
32
+ * from AppCompat, which is enough for ContextMenu, LiquidGlass and the
33
+ * `material: 'system'` modes, but Material 3 widgets look best under a
34
+ * Material 3 app theme. Pass `{ android: { theme: 'material3' } }` to have
35
+ * prebuild re-parent `AppTheme` accordingly.
19
36
  */
20
- declare const withPlatformComponents: ConfigPlugin;
37
+ declare const withPlatformComponents: ConfigPlugin<PlatformComponentsPluginOptions | void>;
21
38
  export default withPlatformComponents;
22
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AAEH,QAAA,MAAM,sBAAsB,EAAE,YAI7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,WAAW,GAAG,aAAa,CAAC,SAAS,CAAC,WAAW,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,CAAC;AAErD,MAAM,MAAM,+BAA+B,GAAG;IAC5C,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,YAAY,CAAC;KACtB,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,sBAAsB,yCAAyC,CAAC;AAW7E,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,MAAM,CASR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,CAejE;AAED;;;;;;;;;;;GAWG;AACH,QAAA,MAAM,sBAAsB,EAAE,YAAY,CACxC,+BAA+B,GAAG,IAAI,CAWvC,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -11,6 +11,11 @@
11
11
  "outDir": "build",
12
12
  "rootDir": "src"
13
13
  },
14
- "include": ["src/**/*"],
15
- "exclude": ["node_modules"]
14
+ "include": [
15
+ "src/**/*"
16
+ ],
17
+ "exclude": [
18
+ "node_modules",
19
+ "src/__tests__"
20
+ ]
16
21
  }