@expo/ui 0.0.1 → 0.0.2
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.
- package/CHANGELOG.md +18 -0
- package/android/build.gradle +5 -13
- package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +24 -1
- package/android/src/main/java/expo/modules/ui/PickerView.kt +108 -0
- package/android/src/main/java/expo/modules/ui/SliderView.kt +73 -0
- package/android/src/main/java/expo/modules/ui/SwitchView.kt +118 -0
- package/android/src/main/java/expo/modules/ui/Utils.kt +52 -0
- package/android/src/main/java/expo/modules/ui/button/Button.kt +140 -0
- package/android/src/main/java/expo/modules/ui/menu/ContextMenu.kt +160 -0
- package/android/src/main/java/expo/modules/ui/menu/ContextMenuRecords.kt +55 -0
- package/build/components/Button/index.d.ts +81 -0
- package/build/components/Button/index.d.ts.map +1 -0
- package/build/components/ContextMenu/index.d.ts +80 -0
- package/build/components/ContextMenu/index.d.ts.map +1 -0
- package/build/components/ContextMenu/utils.d.ts +24 -0
- package/build/components/ContextMenu/utils.d.ts.map +1 -0
- package/build/components/Picker/index.d.ts +65 -0
- package/build/components/Picker/index.d.ts.map +1 -0
- package/build/components/Section/index.d.ts +12 -0
- package/build/components/Section/index.d.ts.map +1 -0
- package/build/components/Section/index.ios.d.ts +8 -0
- package/build/components/Section/index.ios.d.ts.map +1 -0
- package/build/components/Slider/index.d.ts +54 -0
- package/build/components/Slider/index.d.ts.map +1 -0
- package/build/components/Switch/index.d.ts +101 -0
- package/build/components/Switch/index.d.ts.map +1 -0
- package/build/src/index.d.ts +13 -0
- package/build/src/index.d.ts.map +1 -0
- package/components/Button/index.tsx +132 -0
- package/components/ContextMenu/index.tsx +147 -0
- package/components/ContextMenu/utils.ts +139 -0
- package/components/Picker/index.tsx +83 -0
- package/components/Section/index.ios.tsx +58 -0
- package/components/Section/index.tsx +56 -0
- package/components/Slider/index.tsx +85 -0
- package/components/Switch/index.tsx +144 -0
- package/expo-module.config.json +3 -10
- package/ios/Button/Button.swift +54 -0
- package/ios/Button/ButtonProps.swift +43 -0
- package/ios/ContextMenu/ContextMenu.swift +106 -0
- package/ios/ContextMenu/ContextMenuRecords.swift +29 -0
- package/ios/ExpoUI.podspec +2 -1
- package/ios/ExpoUIModule.swift +6 -1
- package/ios/PickerView.swift +52 -0
- package/ios/SectionView.swift +27 -0
- package/ios/SliderView.swift +51 -0
- package/ios/SwitchView.swift +74 -0
- package/package.json +2 -2
- package/src/index.ts +22 -3
- package/tsconfig.json +1 -1
- package/android/src/main/java/expo/modules/ui/SingleChoiceSegmentedControlView.kt +0 -47
- package/build/ExpoUI.types.d.ts +0 -13
- package/build/ExpoUI.types.d.ts.map +0 -1
- package/build/ExpoUIModule.d.ts +0 -6
- package/build/ExpoUIModule.d.ts.map +0 -1
- package/build/ExpoUIView.d.ts +0 -4
- package/build/ExpoUIView.d.ts.map +0 -1
- package/build/index.d.ts +0 -4
- package/build/index.d.ts.map +0 -1
- package/ios/SingleChoiceSegmentedControlProps.swift +0 -10
- package/ios/SingleChoiceSegmentedControlView.swift +0 -29
- package/src/ExpoUI.types.ts +0 -8
- package/src/ExpoUIModule.ts +0 -5
- package/src/ExpoUIView.tsx +0 -10
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.0.2 — 2025-02-11
|
|
14
|
+
|
|
15
|
+
### 🎉 New features
|
|
16
|
+
|
|
17
|
+
- Add `color` and `elementColors` props. ([#34666](https://github.com/expo/expo/pull/34666) by [@aleqsio](https://github.com/aleqsio))
|
|
18
|
+
- Add Button component.([#34340](https://github.com/expo/expo/pull/34340) by [@behenate](https://github.com/behenate))
|
|
19
|
+
- Apple TV support and source restructure. ([#34532](https://github.com/expo/expo/pull/34532) by [@douglowder](https://github.com/douglowder))
|
|
20
|
+
- Add `ContextMenu` component. ([#34553](https://github.com/expo/expo/pull/34553) by [@behenate](https://github.com/behenate))
|
|
21
|
+
|
|
22
|
+
### 🐛 Bug fixes
|
|
23
|
+
|
|
24
|
+
- Fix tvOS compilation. ([#34730](https://github.com/expo/expo/pull/34730) by [@douglowder](https://github.com/douglowder))
|
|
25
|
+
|
|
26
|
+
### 💡 Others
|
|
27
|
+
|
|
28
|
+
- [apple] Migrate remaining `expo-module.config.json` to unified platform syntax. ([#34445](https://github.com/expo/expo/pull/34445) by [@reichhartd](https://github.com/reichhartd))
|
|
29
|
+
- Rename the events for the `Switch` component. ([#34577](https://github.com/expo/expo/pull/34577) by [@behenate](https://github.com/behenate))
|
|
30
|
+
|
|
13
31
|
## 0.0.1 — 2025-01-21
|
|
14
32
|
|
|
15
33
|
### 💡 Others
|
package/android/build.gradle
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
apply plugin: 'com.android.library'
|
|
2
|
-
|
|
3
|
-
group = 'expo.modules.ui'
|
|
4
|
-
version = '0.0.1'
|
|
5
|
-
|
|
6
|
-
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
|
-
apply from: expoModulesCorePlugin
|
|
8
|
-
applyKotlinExpoModulesCorePlugin()
|
|
9
|
-
|
|
10
1
|
buildscript {
|
|
11
2
|
repositories {
|
|
12
3
|
mavenCentral()
|
|
@@ -16,17 +7,18 @@ buildscript {
|
|
|
16
7
|
}
|
|
17
8
|
}
|
|
18
9
|
|
|
10
|
+
apply plugin: 'com.android.library'
|
|
11
|
+
apply plugin: 'expo-module-gradle-plugin'
|
|
19
12
|
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
|
|
20
13
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
useDefaultAndroidSdkVersions()
|
|
14
|
+
group = 'expo.modules.ui'
|
|
15
|
+
version = '0.0.2'
|
|
24
16
|
|
|
25
17
|
android {
|
|
26
18
|
namespace "expo.modules.ui"
|
|
27
19
|
defaultConfig {
|
|
28
20
|
versionCode 1
|
|
29
|
-
versionName "0.0.
|
|
21
|
+
versionName "0.0.2"
|
|
30
22
|
}
|
|
31
23
|
buildFeatures {
|
|
32
24
|
compose true
|
|
@@ -2,14 +2,37 @@ package expo.modules.ui
|
|
|
2
2
|
|
|
3
3
|
import expo.modules.kotlin.modules.Module
|
|
4
4
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
5
|
+
import expo.modules.ui.button.Button
|
|
6
|
+
import expo.modules.ui.menu.ContextMenu
|
|
5
7
|
|
|
6
8
|
class ExpoUIModule : Module() {
|
|
7
9
|
override fun definition() = ModuleDefinition {
|
|
8
10
|
Name("ExpoUI")
|
|
9
11
|
|
|
10
12
|
// Defines a single view for now – a single choice segmented control
|
|
11
|
-
View(
|
|
13
|
+
View(PickerView::class) {
|
|
12
14
|
Events("onOptionSelected")
|
|
13
15
|
}
|
|
16
|
+
|
|
17
|
+
View(SwitchView::class) {
|
|
18
|
+
Events("onValueChange")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
View(Button::class) {
|
|
22
|
+
Events("onButtonPressed")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
View(SliderView::class) {
|
|
26
|
+
Events("onValueChanged")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
View(ContextMenu::class) {
|
|
30
|
+
Events(
|
|
31
|
+
"onContextMenuButtonPressed",
|
|
32
|
+
"onContextMenuPickerOptionSelected",
|
|
33
|
+
"onContextMenuSwitchValueChanged",
|
|
34
|
+
"onExpandedChanged"
|
|
35
|
+
)
|
|
36
|
+
}
|
|
14
37
|
}
|
|
15
38
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
package expo.modules.ui
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import androidx.compose.material3.SegmentedButton
|
|
6
|
+
import androidx.compose.material3.SegmentedButtonDefaults
|
|
7
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
8
|
+
import expo.modules.kotlin.views.ExpoComposeView
|
|
9
|
+
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
|
10
|
+
import androidx.compose.material3.Text
|
|
11
|
+
import androidx.compose.runtime.MutableState
|
|
12
|
+
import androidx.compose.runtime.getValue
|
|
13
|
+
import androidx.compose.runtime.mutableStateOf
|
|
14
|
+
import expo.modules.kotlin.AppContext
|
|
15
|
+
import expo.modules.kotlin.views.AutoSizingComposable
|
|
16
|
+
import expo.modules.kotlin.records.Field
|
|
17
|
+
import expo.modules.kotlin.records.Record
|
|
18
|
+
import expo.modules.kotlin.views.ComposeProps
|
|
19
|
+
|
|
20
|
+
class PickerColors : Record {
|
|
21
|
+
@Field
|
|
22
|
+
val activeBorderColor: Color? = null
|
|
23
|
+
|
|
24
|
+
@Field
|
|
25
|
+
val activeContentColor: Color? = null
|
|
26
|
+
|
|
27
|
+
@Field
|
|
28
|
+
val inactiveBorderColor: Color? = null
|
|
29
|
+
|
|
30
|
+
@Field
|
|
31
|
+
val inactiveContentColor: Color? = null
|
|
32
|
+
|
|
33
|
+
@Field
|
|
34
|
+
val disabledActiveBorderColor: Color? = null
|
|
35
|
+
|
|
36
|
+
@Field
|
|
37
|
+
val disabledActiveContentColor: Color? = null
|
|
38
|
+
|
|
39
|
+
@Field
|
|
40
|
+
val disabledInactiveBorderColor: Color? = null
|
|
41
|
+
|
|
42
|
+
@Field
|
|
43
|
+
val disabledInactiveContentColor: Color? = null
|
|
44
|
+
|
|
45
|
+
@Field
|
|
46
|
+
val activeContainerColor: Color? = null
|
|
47
|
+
|
|
48
|
+
@Field
|
|
49
|
+
val inactiveContainerColor: Color? = null
|
|
50
|
+
|
|
51
|
+
@Field
|
|
52
|
+
val disabledActiveContainerColor: Color? = null
|
|
53
|
+
|
|
54
|
+
@Field
|
|
55
|
+
val disabledInactiveContainerColor: Color? = null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
data class PickerProps(
|
|
59
|
+
val options: MutableState<Array<String>> = mutableStateOf(emptyArray()),
|
|
60
|
+
val selectedIndex: MutableState<Int?> = mutableStateOf(null),
|
|
61
|
+
val elementColors: MutableState<PickerColors> = mutableStateOf(PickerColors())
|
|
62
|
+
) : ComposeProps
|
|
63
|
+
|
|
64
|
+
class PickerView(context: Context, appContext: AppContext) : ExpoComposeView<PickerProps>(context, appContext) {
|
|
65
|
+
override val props = PickerProps()
|
|
66
|
+
private val onOptionSelected by EventDispatcher()
|
|
67
|
+
|
|
68
|
+
init {
|
|
69
|
+
setContent {
|
|
70
|
+
val (selectedIndex) = props.selectedIndex
|
|
71
|
+
val (options) = props.options
|
|
72
|
+
val (colors) = props.elementColors
|
|
73
|
+
DynamicTheme {
|
|
74
|
+
AutoSizingComposable(shadowNodeProxy) {
|
|
75
|
+
SingleChoiceSegmentedButtonRow {
|
|
76
|
+
options.forEachIndexed { index, label ->
|
|
77
|
+
SegmentedButton(
|
|
78
|
+
shape = SegmentedButtonDefaults.itemShape(
|
|
79
|
+
index = index,
|
|
80
|
+
count = options.size
|
|
81
|
+
),
|
|
82
|
+
onClick = {
|
|
83
|
+
onOptionSelected(mapOf("index" to index, "label" to label))
|
|
84
|
+
},
|
|
85
|
+
selected = index == selectedIndex,
|
|
86
|
+
label = { Text(label) },
|
|
87
|
+
colors = SegmentedButtonDefaults.colors(
|
|
88
|
+
activeBorderColor = colors.activeBorderColor.compose,
|
|
89
|
+
activeContentColor = colors.activeContentColor.compose,
|
|
90
|
+
inactiveBorderColor = colors.inactiveBorderColor.compose,
|
|
91
|
+
inactiveContentColor = colors.inactiveContentColor.compose,
|
|
92
|
+
disabledActiveBorderColor = colors.disabledActiveBorderColor.compose,
|
|
93
|
+
disabledActiveContentColor = colors.disabledActiveContentColor.compose,
|
|
94
|
+
disabledInactiveBorderColor = colors.disabledInactiveBorderColor.compose,
|
|
95
|
+
disabledInactiveContentColor = colors.disabledInactiveContentColor.compose,
|
|
96
|
+
activeContainerColor = colors.activeContainerColor.compose,
|
|
97
|
+
inactiveContainerColor = colors.inactiveContainerColor.compose,
|
|
98
|
+
disabledActiveContainerColor = colors.disabledActiveContainerColor.compose,
|
|
99
|
+
disabledInactiveContainerColor = colors.disabledInactiveContainerColor.compose
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package expo.modules.ui
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
6
|
+
import expo.modules.kotlin.views.ExpoComposeView
|
|
7
|
+
import androidx.compose.material3.Slider
|
|
8
|
+
import androidx.compose.material3.SliderDefaults
|
|
9
|
+
import androidx.compose.runtime.MutableState
|
|
10
|
+
import androidx.compose.runtime.mutableFloatStateOf
|
|
11
|
+
import androidx.compose.runtime.mutableIntStateOf
|
|
12
|
+
import androidx.compose.runtime.mutableStateOf
|
|
13
|
+
import expo.modules.kotlin.AppContext
|
|
14
|
+
import expo.modules.kotlin.records.Field
|
|
15
|
+
import expo.modules.kotlin.records.Record
|
|
16
|
+
import expo.modules.kotlin.views.ComposeProps
|
|
17
|
+
|
|
18
|
+
class SliderColors : Record {
|
|
19
|
+
@Field
|
|
20
|
+
val thumbColor: Color? = null
|
|
21
|
+
|
|
22
|
+
@Field
|
|
23
|
+
val activeTrackColor: Color? = null
|
|
24
|
+
|
|
25
|
+
@Field
|
|
26
|
+
val inactiveTrackColor: Color? = null
|
|
27
|
+
|
|
28
|
+
@Field
|
|
29
|
+
val activeTickColor: Color? = null
|
|
30
|
+
|
|
31
|
+
@Field
|
|
32
|
+
val inactiveTickColor: Color? = null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
data class SliderProps(
|
|
36
|
+
val value: MutableState<Float> = mutableFloatStateOf(0.0f),
|
|
37
|
+
val min: MutableState<Float> = mutableFloatStateOf(0.0f),
|
|
38
|
+
val max: MutableState<Float> = mutableFloatStateOf(1.0f),
|
|
39
|
+
val steps: MutableState<Int> = mutableIntStateOf(0),
|
|
40
|
+
val elementColors: MutableState<SliderColors> = mutableStateOf(SliderColors())
|
|
41
|
+
) : ComposeProps
|
|
42
|
+
|
|
43
|
+
class SliderView(context: Context, appContext: AppContext) : ExpoComposeView<SliderProps>(context, appContext) {
|
|
44
|
+
override val props = SliderProps()
|
|
45
|
+
private val onValueChanged by EventDispatcher()
|
|
46
|
+
|
|
47
|
+
init {
|
|
48
|
+
setContent {
|
|
49
|
+
val (value) = props.value
|
|
50
|
+
val (min) = props.min
|
|
51
|
+
val (max) = props.max
|
|
52
|
+
val (steps) = props.steps
|
|
53
|
+
val (colors) = props.elementColors
|
|
54
|
+
DynamicTheme {
|
|
55
|
+
Slider(
|
|
56
|
+
value = value.coerceAtLeast(min).coerceAtMost(max),
|
|
57
|
+
valueRange = min..max,
|
|
58
|
+
steps = steps,
|
|
59
|
+
onValueChange = {
|
|
60
|
+
onValueChanged(mapOf("value" to it))
|
|
61
|
+
},
|
|
62
|
+
colors = SliderDefaults.colors(
|
|
63
|
+
thumbColor = colors.thumbColor.compose,
|
|
64
|
+
activeTrackColor = colors.activeTrackColor.compose,
|
|
65
|
+
inactiveTrackColor = colors.inactiveTrackColor.compose,
|
|
66
|
+
activeTickColor = colors.activeTickColor.compose,
|
|
67
|
+
inactiveTickColor = colors.inactiveTickColor.compose
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
package expo.modules.ui
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import androidx.compose.material3.Checkbox
|
|
6
|
+
import androidx.compose.material3.CheckboxDefaults
|
|
7
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
8
|
+
import expo.modules.kotlin.views.ExpoComposeView
|
|
9
|
+
import androidx.compose.material3.Switch
|
|
10
|
+
import androidx.compose.material3.SwitchDefaults
|
|
11
|
+
import androidx.compose.runtime.Composable
|
|
12
|
+
import androidx.compose.runtime.MutableState
|
|
13
|
+
import androidx.compose.runtime.mutableStateOf
|
|
14
|
+
import expo.modules.kotlin.AppContext
|
|
15
|
+
import expo.modules.kotlin.views.AutoSizingComposable
|
|
16
|
+
import expo.modules.kotlin.views.ComposeProps
|
|
17
|
+
import expo.modules.kotlin.records.Field
|
|
18
|
+
import expo.modules.kotlin.records.Record
|
|
19
|
+
import java.io.Serializable
|
|
20
|
+
|
|
21
|
+
open class ValueChangeEvent(
|
|
22
|
+
@Field open val value: Boolean = false
|
|
23
|
+
) : Record, Serializable
|
|
24
|
+
|
|
25
|
+
class SwitchColors : Record {
|
|
26
|
+
@Field
|
|
27
|
+
val checkedThumbColor: Color? = null
|
|
28
|
+
|
|
29
|
+
@Field
|
|
30
|
+
val checkedTrackColor: Color? = null
|
|
31
|
+
|
|
32
|
+
@Field
|
|
33
|
+
val uncheckedThumbColor: Color? = null
|
|
34
|
+
|
|
35
|
+
@Field
|
|
36
|
+
val uncheckedTrackColor: Color? = null
|
|
37
|
+
|
|
38
|
+
@Field
|
|
39
|
+
val checkedColor: Color? = null
|
|
40
|
+
|
|
41
|
+
@Field
|
|
42
|
+
val disabledCheckedColor: Color? = null
|
|
43
|
+
|
|
44
|
+
@Field
|
|
45
|
+
val uncheckedColor: Color? = null
|
|
46
|
+
|
|
47
|
+
@Field
|
|
48
|
+
val disabledUncheckedColor: Color? = null
|
|
49
|
+
|
|
50
|
+
@Field
|
|
51
|
+
val checkmarkColor: Color? = null
|
|
52
|
+
|
|
53
|
+
@Field
|
|
54
|
+
val disabledIndeterminateColor: Color? = null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
data class SwitchProps(
|
|
58
|
+
val value: MutableState<Boolean> = mutableStateOf(false),
|
|
59
|
+
val variant: MutableState<String> = mutableStateOf("switch"),
|
|
60
|
+
val elementColors: MutableState<SwitchColors> = mutableStateOf(SwitchColors())
|
|
61
|
+
) : ComposeProps
|
|
62
|
+
|
|
63
|
+
class SwitchView(context: Context, appContext: AppContext) : ExpoComposeView<SwitchProps>(context, appContext) {
|
|
64
|
+
override val props = SwitchProps()
|
|
65
|
+
private val onValueChange by EventDispatcher<ValueChangeEvent>()
|
|
66
|
+
|
|
67
|
+
init {
|
|
68
|
+
setContent {
|
|
69
|
+
val (checked) = props.value
|
|
70
|
+
val (variant) = props.variant
|
|
71
|
+
val (colors) = props.elementColors
|
|
72
|
+
val onCheckedChange = { checked: Boolean ->
|
|
73
|
+
onValueChange(ValueChangeEvent(checked))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Composable
|
|
77
|
+
fun SwitchComposable() {
|
|
78
|
+
Switch(
|
|
79
|
+
checked = checked,
|
|
80
|
+
onCheckedChange = onCheckedChange,
|
|
81
|
+
colors = SwitchDefaults.colors(
|
|
82
|
+
// For some reason the default way of passing colors using `compose` results in a transparent view
|
|
83
|
+
checkedThumbColor = colors.checkedThumbColor.composeOrNull ?: SwitchDefaults.colors().checkedThumbColor,
|
|
84
|
+
checkedTrackColor = colors.checkedTrackColor.composeOrNull ?: SwitchDefaults.colors().checkedTrackColor,
|
|
85
|
+
uncheckedThumbColor = colors.uncheckedThumbColor.composeOrNull ?: SwitchDefaults.colors().uncheckedThumbColor,
|
|
86
|
+
uncheckedTrackColor = colors.uncheckedTrackColor.composeOrNull ?: SwitchDefaults.colors().uncheckedTrackColor
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Composable
|
|
92
|
+
fun CheckboxComposable() {
|
|
93
|
+
Checkbox(
|
|
94
|
+
checked = checked,
|
|
95
|
+
onCheckedChange = onCheckedChange,
|
|
96
|
+
colors = CheckboxDefaults.colors(
|
|
97
|
+
checkedColor = colors.checkedColor.compose,
|
|
98
|
+
disabledCheckedColor = colors.disabledCheckedColor.compose,
|
|
99
|
+
uncheckedColor = colors.uncheckedColor.compose,
|
|
100
|
+
disabledUncheckedColor = colors.disabledUncheckedColor.compose,
|
|
101
|
+
checkmarkColor = colors.checkmarkColor.compose,
|
|
102
|
+
disabledIndeterminateColor = colors.disabledIndeterminateColor.compose
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
DynamicTheme {
|
|
108
|
+
AutoSizingComposable(shadowNodeProxy) {
|
|
109
|
+
if (variant == "switch") {
|
|
110
|
+
SwitchComposable()
|
|
111
|
+
} else {
|
|
112
|
+
CheckboxComposable()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
package expo.modules.ui
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import android.os.Build
|
|
5
|
+
import androidx.compose.foundation.isSystemInDarkTheme
|
|
6
|
+
import androidx.compose.material3.MaterialTheme
|
|
7
|
+
import androidx.compose.material3.darkColorScheme
|
|
8
|
+
import androidx.compose.material3.dynamicDarkColorScheme
|
|
9
|
+
import androidx.compose.material3.dynamicLightColorScheme
|
|
10
|
+
import androidx.compose.material3.lightColorScheme
|
|
11
|
+
import androidx.compose.runtime.Composable
|
|
12
|
+
import androidx.compose.ui.platform.LocalContext
|
|
13
|
+
|
|
14
|
+
@Composable
|
|
15
|
+
fun DynamicTheme(content: @Composable (() -> Unit)) {
|
|
16
|
+
val context = LocalContext.current
|
|
17
|
+
val colors = when {
|
|
18
|
+
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
|
|
19
|
+
if (isSystemInDarkTheme()) {
|
|
20
|
+
dynamicDarkColorScheme(context)
|
|
21
|
+
} else {
|
|
22
|
+
dynamicLightColorScheme(context)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
isSystemInDarkTheme() -> darkColorScheme()
|
|
27
|
+
else -> lightColorScheme()
|
|
28
|
+
}
|
|
29
|
+
MaterialTheme(colorScheme = colors) {
|
|
30
|
+
content()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fun colorToComposeColorOrNull(color: Color?): androidx.compose.ui.graphics.Color? {
|
|
35
|
+
return color?.let {
|
|
36
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
37
|
+
androidx.compose.ui.graphics.Color(it.red(), it.green(), it.blue(), it.alpha())
|
|
38
|
+
} else {
|
|
39
|
+
null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun colorToComposeColor(color: Color?): androidx.compose.ui.graphics.Color {
|
|
45
|
+
return colorToComposeColorOrNull(color) ?: androidx.compose.ui.graphics.Color.Unspecified
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
val Color?.compose: androidx.compose.ui.graphics.Color
|
|
49
|
+
get() = colorToComposeColor(this)
|
|
50
|
+
|
|
51
|
+
val Color?.composeOrNull: androidx.compose.ui.graphics.Color?
|
|
52
|
+
get() = colorToComposeColorOrNull(this)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
package expo.modules.ui.button
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import androidx.compose.foundation.layout.RowScope
|
|
6
|
+
import androidx.compose.material3.ButtonDefaults
|
|
7
|
+
import androidx.compose.material3.ElevatedButton
|
|
8
|
+
import androidx.compose.material3.FilledTonalButton
|
|
9
|
+
import androidx.compose.material3.OutlinedButton
|
|
10
|
+
import expo.modules.kotlin.viewevent.EventDispatcher
|
|
11
|
+
import expo.modules.kotlin.views.ExpoComposeView
|
|
12
|
+
import androidx.compose.material3.Text
|
|
13
|
+
import androidx.compose.material3.TextButton
|
|
14
|
+
import androidx.compose.runtime.Composable
|
|
15
|
+
import androidx.compose.runtime.MutableState
|
|
16
|
+
import androidx.compose.runtime.mutableStateOf
|
|
17
|
+
import expo.modules.kotlin.AppContext
|
|
18
|
+
import expo.modules.kotlin.records.Field
|
|
19
|
+
import expo.modules.kotlin.records.Record
|
|
20
|
+
import expo.modules.kotlin.views.ComposeProps
|
|
21
|
+
import java.io.Serializable
|
|
22
|
+
import expo.modules.kotlin.types.Enumerable
|
|
23
|
+
import expo.modules.ui.DynamicTheme
|
|
24
|
+
import expo.modules.ui.compose
|
|
25
|
+
|
|
26
|
+
open class ButtonPressedEvent() : Record, Serializable
|
|
27
|
+
|
|
28
|
+
enum class ButtonVariant(val value: String) : Enumerable {
|
|
29
|
+
DEFAULT("default"),
|
|
30
|
+
BORDERED("bordered"),
|
|
31
|
+
BORDERLESS("borderless"),
|
|
32
|
+
OUTLINED("outlined"),
|
|
33
|
+
ELEVATED("elevated")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class ButtonColors : Record {
|
|
37
|
+
@Field
|
|
38
|
+
val containerColor: Color? = null
|
|
39
|
+
|
|
40
|
+
@Field
|
|
41
|
+
val contentColor: Color? = null
|
|
42
|
+
|
|
43
|
+
@Field
|
|
44
|
+
val disabledContainerColor: Color? = null
|
|
45
|
+
|
|
46
|
+
@Field
|
|
47
|
+
val disabledContentColor: Color? = null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
data class ButtonProps(
|
|
51
|
+
val text: MutableState<String> = mutableStateOf(""),
|
|
52
|
+
val variant: MutableState<ButtonVariant?> = mutableStateOf(ButtonVariant.DEFAULT),
|
|
53
|
+
val elementColors: MutableState<ButtonColors> = mutableStateOf(ButtonColors())
|
|
54
|
+
|
|
55
|
+
) : ComposeProps
|
|
56
|
+
|
|
57
|
+
@Composable
|
|
58
|
+
fun StyledButton(variant: ButtonVariant, colors: ButtonColors, onPress: () -> Unit, content: @Composable (RowScope.() -> Unit)) {
|
|
59
|
+
when (variant) {
|
|
60
|
+
ButtonVariant.BORDERED -> FilledTonalButton(
|
|
61
|
+
onPress,
|
|
62
|
+
content = content,
|
|
63
|
+
colors = ButtonDefaults.filledTonalButtonColors(
|
|
64
|
+
containerColor = colors.containerColor.compose,
|
|
65
|
+
contentColor = colors.contentColor.compose,
|
|
66
|
+
disabledContainerColor = colors.disabledContainerColor.compose,
|
|
67
|
+
disabledContentColor = colors.disabledContentColor.compose
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
ButtonVariant.BORDERLESS -> TextButton(
|
|
72
|
+
onPress,
|
|
73
|
+
content = content,
|
|
74
|
+
colors = ButtonDefaults.textButtonColors(
|
|
75
|
+
containerColor = colors.containerColor.compose,
|
|
76
|
+
contentColor = colors.contentColor.compose,
|
|
77
|
+
disabledContainerColor = colors.disabledContainerColor.compose,
|
|
78
|
+
disabledContentColor = colors.disabledContentColor.compose
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
ButtonVariant.OUTLINED -> OutlinedButton(
|
|
83
|
+
onPress,
|
|
84
|
+
content = content,
|
|
85
|
+
colors = ButtonDefaults.outlinedButtonColors(
|
|
86
|
+
containerColor = colors.containerColor.compose,
|
|
87
|
+
contentColor = colors.contentColor.compose,
|
|
88
|
+
disabledContainerColor = colors.disabledContainerColor.compose,
|
|
89
|
+
disabledContentColor = colors.disabledContentColor.compose
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
ButtonVariant.ELEVATED -> ElevatedButton(
|
|
94
|
+
onPress,
|
|
95
|
+
content = content,
|
|
96
|
+
colors = ButtonDefaults.elevatedButtonColors(
|
|
97
|
+
containerColor = colors.containerColor.compose,
|
|
98
|
+
contentColor = colors.contentColor.compose,
|
|
99
|
+
disabledContainerColor = colors.disabledContainerColor.compose,
|
|
100
|
+
disabledContentColor = colors.disabledContentColor.compose
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
else -> androidx.compose.material3.Button(
|
|
105
|
+
onPress,
|
|
106
|
+
content = content,
|
|
107
|
+
colors = ButtonDefaults.buttonColors(
|
|
108
|
+
containerColor = colors.containerColor.compose,
|
|
109
|
+
contentColor = colors.contentColor.compose,
|
|
110
|
+
disabledContainerColor = colors.disabledContainerColor.compose,
|
|
111
|
+
disabledContentColor = colors.disabledContentColor.compose
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class Button(context: Context, appContext: AppContext) : ExpoComposeView<ButtonProps>(context, appContext) {
|
|
118
|
+
override val props = ButtonProps()
|
|
119
|
+
private val onButtonPressed by EventDispatcher<ButtonPressedEvent>()
|
|
120
|
+
|
|
121
|
+
init {
|
|
122
|
+
clipToPadding = false // needed for elevated buttons to work
|
|
123
|
+
clipChildren = false
|
|
124
|
+
|
|
125
|
+
setContent {
|
|
126
|
+
val (variant) = props.variant
|
|
127
|
+
val (text) = props.text
|
|
128
|
+
val (colors) = props.elementColors
|
|
129
|
+
DynamicTheme {
|
|
130
|
+
StyledButton(
|
|
131
|
+
variant ?: ButtonVariant.DEFAULT,
|
|
132
|
+
colors,
|
|
133
|
+
{ onButtonPressed.invoke(ButtonPressedEvent()) }
|
|
134
|
+
) {
|
|
135
|
+
Text(text)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|