@expo/ui 0.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.
package/.eslintrc.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ['universe/native', 'universe/web'],
4
+ ignorePatterns: ['build'],
5
+ };
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## Unpublished
4
+
5
+ ### 🛠 Breaking changes
6
+
7
+ ### 🎉 New features
8
+
9
+ ### 🐛 Bug fixes
10
+
11
+ ### 💡 Others
12
+
13
+ ## 0.0.0 — 2025-01-21
14
+
15
+ ### 🎉 New features
16
+
17
+ - [iOS] Add Picker component ([#34198](https://github.com/expo/expo/pull/34198) by [@aleqsio](https://github.com/aleqsio))
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # expo-ui
2
+
3
+ A collection of UI components by Expo.
@@ -0,0 +1,67 @@
1
+ apply plugin: 'com.android.library'
2
+
3
+ group = 'expo.modules.ui'
4
+ version = '0.0.0'
5
+
6
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
+ apply from: expoModulesCorePlugin
8
+ applyKotlinExpoModulesCorePlugin()
9
+
10
+ buildscript {
11
+ repositories {
12
+ mavenCentral()
13
+ }
14
+ dependencies {
15
+ classpath("org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin:${kotlinVersion}")
16
+ }
17
+ }
18
+
19
+ apply plugin: 'org.jetbrains.kotlin.plugin.compose'
20
+
21
+ useCoreDependencies()
22
+ useExpoPublishing()
23
+
24
+ // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
25
+ // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
26
+ // Most of the time, you may like to manage the Android SDK versions yourself.
27
+ def useManagedAndroidSdkVersions = false
28
+ if (useManagedAndroidSdkVersions) {
29
+ useDefaultAndroidSdkVersions()
30
+ } else {
31
+ buildscript {
32
+ // Simple helper that allows the root project to override versions declared by this library.
33
+ ext.safeExtGet = { prop, fallback ->
34
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
35
+ }
36
+ }
37
+ project.android {
38
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
39
+ defaultConfig {
40
+ minSdkVersion safeExtGet("minSdkVersion", 21)
41
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
42
+ }
43
+ }
44
+ }
45
+
46
+ android {
47
+ namespace "expo.modules.ui"
48
+ defaultConfig {
49
+ versionCode 1
50
+ versionName "0.0.0"
51
+ }
52
+ buildFeatures {
53
+ compose true
54
+ }
55
+ lintOptions {
56
+ abortOnError false
57
+ }
58
+ }
59
+
60
+ dependencies {
61
+ implementation 'androidx.compose.foundation:foundation-android:1.7.6'
62
+ implementation 'androidx.compose.ui:ui-android:1.7.6'
63
+ implementation "androidx.compose.material3:material3:1.3.1"
64
+ implementation 'androidx.lifecycle:lifecycle-runtime:2.8.7'
65
+ implementation 'androidx.fragment:fragment-ktx:1.8.5'
66
+ implementation 'androidx.compose.material3:material3-android:1.3.1'
67
+ }
@@ -0,0 +1,2 @@
1
+ <manifest>
2
+ </manifest>
@@ -0,0 +1,15 @@
1
+ package expo.modules.ui
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+ import expo.modules.kotlin.modules.ModuleDefinition
5
+
6
+ class ExpoUIModule : Module() {
7
+ override fun definition() = ModuleDefinition {
8
+ Name("ExpoUI")
9
+
10
+ // Defines a single view for now – a single choice segmented control
11
+ View(SingleChoiceSegmentedControlView::class) {
12
+ Events("onOptionSelected")
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,47 @@
1
+ package expo.modules.ui
2
+
3
+ import android.content.Context
4
+ import androidx.compose.material3.SegmentedButton
5
+ import androidx.compose.material3.SegmentedButtonDefaults
6
+ import expo.modules.kotlin.viewevent.EventDispatcher
7
+ import expo.modules.kotlin.views.ExpoComposeView
8
+ import androidx.compose.material3.SingleChoiceSegmentedButtonRow
9
+ import androidx.compose.material3.Text
10
+ import androidx.compose.runtime.MutableState
11
+ import androidx.compose.runtime.getValue
12
+ import androidx.compose.runtime.mutableStateOf
13
+ import androidx.compose.runtime.remember
14
+ import expo.modules.kotlin.AppContext
15
+ import expo.modules.kotlin.views.ComposeProps
16
+
17
+ data class SegmentedControlProps(
18
+ val options: MutableState<Array<String>> = mutableStateOf(emptyArray()),
19
+ val selectedIndex: MutableState<Int?> = mutableStateOf(null)
20
+ ) : ComposeProps
21
+
22
+ class SingleChoiceSegmentedControlView(context: Context, appContext: AppContext) : ExpoComposeView<SegmentedControlProps>(context, appContext) {
23
+ override val props = SegmentedControlProps()
24
+ private val onOptionSelected by EventDispatcher()
25
+
26
+ init {
27
+ setContent {
28
+ val selectedIndex by remember { props.selectedIndex }
29
+ val options by remember { props.options }
30
+ SingleChoiceSegmentedButtonRow {
31
+ options.forEachIndexed { index, label ->
32
+ SegmentedButton(
33
+ shape = SegmentedButtonDefaults.itemShape(
34
+ index = index,
35
+ count = options.size
36
+ ),
37
+ onClick = {
38
+ onOptionSelected(mapOf("index" to index, "label" to label))
39
+ },
40
+ selected = index == selectedIndex,
41
+ label = { Text(label) }
42
+ )
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,13 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+ export type ExpoUIViewProps = {
3
+ options: string[];
4
+ selectedIndex: number | null;
5
+ onOptionSelected: (event: {
6
+ nativeEvent: {
7
+ index: number;
8
+ label: string;
9
+ };
10
+ }) => void;
11
+ style?: StyleProp<ViewStyle>;
12
+ };
13
+ //# sourceMappingURL=ExpoUI.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoUI.types.d.ts","sourceRoot":"","sources":["../src/ExpoUI.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IACrF,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { NativeModule } from 'expo';
2
+ declare class ExpoUIModule extends NativeModule {
3
+ }
4
+ declare const _default: ExpoUIModule;
5
+ export default _default;
6
+ //# sourceMappingURL=ExpoUIModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoUIModule.d.ts","sourceRoot":"","sources":["../src/ExpoUIModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,OAAO,YAAa,SAAQ,YAAY;CAAG;;AAElD,wBAA2D"}
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+ import { ExpoUIViewProps } from './ExpoUI.types';
3
+ export default function ExpoUIView(props: ExpoUIViewProps): React.JSX.Element;
4
+ //# sourceMappingURL=ExpoUIView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoUIView.d.ts","sourceRoot":"","sources":["../src/ExpoUIView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAIjD,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,KAAK,EAAE,eAAe,qBAExD"}
@@ -0,0 +1,4 @@
1
+ export { default } from './ExpoUIModule';
2
+ export { default as SingleChoiceSegmentedControlView } from './ExpoUIView';
3
+ export * from './ExpoUI.types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,gCAAgC,EAAE,MAAM,cAAc,CAAC;AAC3E,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,16 @@
1
+ {
2
+ "platforms": [
3
+ "apple",
4
+ "android"
5
+ ],
6
+ "android": {
7
+ "modules": [
8
+ "expo.modules.ui.ExpoUIModule"
9
+ ]
10
+ },
11
+ "apple": {
12
+ "modules": [
13
+ "ExpoUIModule"
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,28 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'ExpoUI'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = package['description']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = {
14
+ :ios => '15.1'
15
+ }
16
+ s.swift_version = '5.4'
17
+ s.source = { git: 'https://github.com/expo/expo.git' }
18
+ s.static_framework = true
19
+
20
+ s.dependency 'ExpoModulesCore'
21
+
22
+ # Swift/Objective-C compatibility
23
+ s.pod_target_xcconfig = {
24
+ 'DEFINES_MODULE' => 'YES'
25
+ }
26
+
27
+ s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
28
+ end
@@ -0,0 +1,11 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+
5
+ public class ExpoUIModule: Module {
6
+ public func definition() -> ModuleDefinition {
7
+ Name("ExpoUI")
8
+
9
+ View(SingleChoiceSegmentedControlView.self)
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ import SwiftUI
4
+ import ExpoModulesCore
5
+
6
+ class SingleChoiceSegmentedControlProps: ExpoSwiftUI.ViewProps {
7
+ @Field var options: [String] = []
8
+ @Field var selectedIndex: Int?
9
+ var onOptionSelected = EventDispatcher()
10
+ }
@@ -0,0 +1,29 @@
1
+ // Copyright 2025-present 650 Industries. All rights reserved.
2
+
3
+ import SwiftUI
4
+ import ExpoModulesCore
5
+
6
+ struct SingleChoiceSegmentedControlView: ExpoSwiftUI.View {
7
+ @State var selection: Int?
8
+ @EnvironmentObject var props: SingleChoiceSegmentedControlProps
9
+ var body: some View {
10
+ Picker("", selection: $selection) {
11
+ ForEach(Array(props.options.enumerated()), id: \.element) { index, option in
12
+ Text(option).tag(index)
13
+ }
14
+ }
15
+ .pickerStyle(SegmentedPickerStyle())
16
+ .onChange(of: selection, perform: { newValue in
17
+ if props.selectedIndex == newValue {
18
+ return
19
+ }
20
+ props.onOptionSelected([
21
+ "index": newValue ?? 0,
22
+ "label": props.options[newValue ?? 0]
23
+ ])
24
+ })
25
+ .onReceive(props.selectedIndex.publisher, perform: { newValue in
26
+ selection = newValue
27
+ })
28
+ }
29
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@expo/ui",
3
+ "version": "0.0.0",
4
+ "description": "A collection of UI components",
5
+ "main": "src/index.ts",
6
+ "types": "build/index.d.ts",
7
+ "scripts": {
8
+ "build": "expo-module build",
9
+ "clean": "expo-module clean",
10
+ "lint": "expo-module lint",
11
+ "test": "expo-module test",
12
+ "prepare": "expo-module prepare",
13
+ "prepublishOnly": "expo-module prepublishOnly",
14
+ "expo-module": "expo-module"
15
+ },
16
+ "homepage": "https://docs.expo.dev/versions/latest/sdk/ui/",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/expo/expo.git",
20
+ "directory": "packages/expo-ui"
21
+ },
22
+ "keywords": [
23
+ "react-native",
24
+ "expo",
25
+ "UI components"
26
+ ],
27
+ "author": "650 Industries, Inc.",
28
+ "license": "MIT",
29
+ "dependencies": {},
30
+ "devDependencies": {
31
+ "@types/react": "~18.3.12",
32
+ "expo-module-scripts": "^4.0.2"
33
+ },
34
+ "peerDependencies": {
35
+ "expo": "*",
36
+ "react": "*",
37
+ "react-native": "*"
38
+ }
39
+ }
@@ -0,0 +1,8 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ export type ExpoUIViewProps = {
4
+ options: string[];
5
+ selectedIndex: number | null;
6
+ onOptionSelected: (event: { nativeEvent: { index: number; label: string } }) => void;
7
+ style?: StyleProp<ViewStyle>;
8
+ };
@@ -0,0 +1,5 @@
1
+ import { NativeModule, requireNativeModule } from 'expo';
2
+
3
+ declare class ExpoUIModule extends NativeModule {}
4
+
5
+ export default requireNativeModule<ExpoUIModule>('ExpoUI');
@@ -0,0 +1,10 @@
1
+ import { requireNativeView } from 'expo';
2
+ import * as React from 'react';
3
+
4
+ import { ExpoUIViewProps } from './ExpoUI.types';
5
+
6
+ const NativeView: React.ComponentType<ExpoUIViewProps> = requireNativeView('ExpoUI');
7
+
8
+ export default function ExpoUIView(props: ExpoUIViewProps) {
9
+ return <NativeView {...props} />;
10
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { default } from './ExpoUIModule';
2
+ export { default as SingleChoiceSegmentedControlView } from './ExpoUIView';
3
+ export * from './ExpoUI.types';
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "expo-module-scripts/tsconfig.base",
3
+ "compilerOptions": {
4
+ "outDir": "./build",
5
+ "emitDeclarationOnly": true
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
+ }