Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 13 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,24 @@

Android Material 3 components for React Native apps

## Installation

```sh
npm install @material3/react-native
```

## Usage
## Getting started

From the root of the project:

```js
import { ReactNativeView } from "@material3/react-native";
1. Install dependencies

// ...

<ReactNativeView color="tomato" />
```sh
yarn
```

2. Start the example app

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

## License

MIT
```sh
yarn example start
```

---
3. Run the example app on Android

Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
```sh
yarn example android
```
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation 'com.google.android.material:material:1.12.0'
implementation "me.saket.cascade:cascade:2.3.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.material3.reactnative

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.google.android.material.dialog.MaterialAlertDialogBuilder

class AlertDialogComponent(
val props: ReadableMap,
val onPositivePress: Callback?,
val onNegativePress: Callback?,
val onNeutralPress: Callback?,
val onCancel: Callback?,
val reactContext: ReactApplicationContext,
) : DialogFragment() {
private var alreadyCalled = false
private var alertDialog: MaterialAlertDialogBuilder? = null

fun show() {
val activity = reactContext.currentActivity as FragmentActivity?
val fragmentManager = activity!!.supportFragmentManager

this.show(fragmentManager, AlertDialogModule.NAME)
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
buildDialog()
return alertDialog!!.create()
}

private fun buildDialog() {
alertDialog = MaterialAlertDialogBuilder(
requireContext(), getHeaderAlignmentTheme()
)

setTitle()
setMessage()
setActions()
setCancelable()
setIcon()
}

private fun setActions() {
val positiveButtonText = props.getString("positiveButtonText")
if (!positiveButtonText.isNullOrEmpty()) {
alertDialog!!.setPositiveButton(positiveButtonText) { _, _ ->
if (alreadyCalled) return@setPositiveButton

onPositivePress?.invoke()
alreadyCalled = true
}
}

val negativeButtonText = props.getString("negativeButtonText")
if (!negativeButtonText.isNullOrEmpty()) {
alertDialog!!.setNegativeButton(negativeButtonText) { _, _ ->
if (alreadyCalled) return@setNegativeButton

onNegativePress?.invoke()
alreadyCalled = true
}
}

val neutralButtonText = props.getString("neutralButtonText")
if (!neutralButtonText.isNullOrEmpty()) {
alertDialog!!.setNeutralButton(neutralButtonText) { _, _ ->
if (alreadyCalled) return@setNeutralButton

onNeutralPress?.invoke()
alreadyCalled = true
}
}
}

private fun setTitle() {
val title = props.getString("title")
if (title.isNullOrEmpty()) return

alertDialog!!.setTitle(title)
}

private fun setMessage() {
val message = props.getString("message")
if (message.isNullOrEmpty()) return

alertDialog!!.setMessage(message)
}

private fun setCancelable() {
if (props.hasKey("cancelable")) {
this.isCancelable = props.getBoolean("cancelable")
}
}

private fun getHeaderAlignmentTheme(): Int {
val headerAlignment = props.getString("headerAlignment")

return when (headerAlignment) {
"center" -> com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered
else -> com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog
}
}

private fun setIcon() {
val icon = props.getString("icon")

if (icon.isNullOrEmpty()) {
alertDialog!!.setIcon(null)
} else {
alertDialog!!.setIcon(IconHelper(alertDialog!!.context, icon).resolve())
}
}

override fun onDismiss(dialog: DialogInterface) {
if (alreadyCalled) return

onCancel?.invoke()
alreadyCalled = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.material3.reactnative

import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.UiThreadUtil

class AlertDialogModule(reactContext: ReactApplicationContext) :
NativeAlertDialogSpec(reactContext) {
override fun getName() = NAME

override fun show(
props: ReadableMap?,
onPositivePress: Callback?,
onNegativePress: Callback?,
onNeutralPress: Callback?,
onCancel: Callback?
) {
val alertDialog = AlertDialogComponent(
props = props!!,
onPositivePress = onPositivePress,
onNeutralPress = onNeutralPress,
reactContext = reactApplicationContext,
onNegativePress = onNegativePress,
onCancel = onCancel
)

UiThreadUtil.runOnUiThread {
alertDialog.show()
}
}

companion object {
const val NAME = "RTNAlertDialog"
}
}
Loading
Loading