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
115 changes: 115 additions & 0 deletions apps/native-component-list/src/screens/UI/TooltipScreen.android.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
TooltipBox,
type TooltipBoxRef,
Button,
TextButton,
Host,
Text as ComposeText,
Column,
Row,
Card,
LazyColumn,
} from '@expo/ui/jetpack-compose';
import { fillMaxWidth, padding } from '@expo/ui/jetpack-compose/modifiers';
import { useRef } from 'react';

export default function TooltipScreen() {
const tooltipRef = useRef<TooltipBoxRef>(null);
return (
<Host style={{ flex: 1 }}>
<LazyColumn verticalArrangement={{ spacedBy: 16 }} modifiers={[padding(16, 16, 16, 16)]}>
<Card modifiers={[fillMaxWidth()]}>
<Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}>
<ComposeText>Plain Tooltip</ComposeText>
<ComposeText>Long-press the button to show a plain tooltip.</ComposeText>
<TooltipBox>
<TooltipBox.PlainTooltip>
<ComposeText>Add to favorites</ComposeText>
</TooltipBox.PlainTooltip>
<Button onClick={() => {}}>
<ComposeText>Favorite</ComposeText>
</Button>
</TooltipBox>
</Column>
</Card>

<Card modifiers={[fillMaxWidth()]}>
<Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}>
<ComposeText>Rich Tooltip</ComposeText>
<ComposeText>
Long-press the button to show a rich tooltip with title and body.
</ComposeText>
<TooltipBox>
<TooltipBox.RichTooltip>
<TooltipBox.RichTooltip.Title>
<ComposeText>Camera</ComposeText>
</TooltipBox.RichTooltip.Title>
<TooltipBox.RichTooltip.Text>
<ComposeText>Take photos and record videos with your device camera.</ComposeText>
</TooltipBox.RichTooltip.Text>
</TooltipBox.RichTooltip>
<Button onClick={() => {}}>
<ComposeText>Open Camera</ComposeText>
</Button>
</TooltipBox>
</Column>
</Card>

<Card modifiers={[fillMaxWidth()]}>
<Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}>
<ComposeText>Rich Tooltip with Action</ComposeText>
<ComposeText>
Long-press the button to show a persistent rich tooltip with an action button.
</ComposeText>
<TooltipBox isPersistent>
<TooltipBox.RichTooltip>
<TooltipBox.RichTooltip.Title>
<ComposeText>Permissions Required</ComposeText>
</TooltipBox.RichTooltip.Title>
<TooltipBox.RichTooltip.Text>
<ComposeText>
This feature requires camera and microphone access to function properly.
</ComposeText>
</TooltipBox.RichTooltip.Text>
<TooltipBox.RichTooltip.Action>
<TextButton onClick={() => {}}>
<ComposeText>Learn More</ComposeText>
</TextButton>
</TooltipBox.RichTooltip.Action>
</TooltipBox.RichTooltip>
<Button onClick={() => {}}>
<ComposeText>Record Video</ComposeText>
</Button>
</TooltipBox>
</Column>
</Card>
<Card modifiers={[fillMaxWidth()]}>
<Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}>
<ComposeText>Programmatic Show/Dismiss</ComposeText>
<ComposeText>Use ref methods to control the tooltip imperatively.</ComposeText>
<TooltipBox ref={tooltipRef} isPersistent>
<TooltipBox.PlainTooltip>
<ComposeText>Shown programmatically!</ComposeText>
</TooltipBox.PlainTooltip>
<Button onClick={() => {}}>
<ComposeText>Anchor</ComposeText>
</Button>
</TooltipBox>
<Row horizontalArrangement={{ spacedBy: 8 }}>
<Button onClick={() => tooltipRef.current?.show()}>
<ComposeText>Show</ComposeText>
</Button>
<Button onClick={() => tooltipRef.current?.dismiss()}>
<ComposeText>Dismiss</ComposeText>
</Button>
</Row>
</Column>
</Card>
</LazyColumn>
</Host>
);
}

TooltipScreen.navigationOptions = {
title: 'Tooltip',
};
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,14 @@ export const UIScreens = [
return optionalRequire(() => require('./SurfaceScreen'));
},
},
{
name: 'Tooltip component',
route: 'ui/tooltip',
options: {},
getComponent() {
return optionalRequire(() => require('./TooltipScreen'));
},
},
];

export default function UIScreen() {
Expand Down
14 changes: 6 additions & 8 deletions docs/pages/versions/unversioned/sdk/navigation-bar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ import {
} from '~/ui/components/ConfigSection';
import { SnackInline } from '~/ui/components/Snippet';

`expo-navigation-bar` enables you to modify and observe the native navigation bar on Android devices. Due to some Android platform restrictions, parts of this API overlap with the `expo-status-bar` API.

The APIs in this package have no impact when "Gesture Navigation" is enabled on the Android device. There is currently no native Android API to detect if "Gesture Navigation" is enabled or not.
`expo-navigation-bar` provides a component and an imperative API for controlling the app's navigation bar on Android devices, allowing you to change the color of its buttons or hide it.

## Installation

Expand Down Expand Up @@ -53,7 +51,7 @@ You can configure `expo-navigation-bar` using its built-in [config plugin](/conf
{
name: 'enforceContrast',
description:
'Determines whether the operating system should keep the navigation bar translucent to provide contrast between the navigation buttons and app content.',
'Determines whether the operating system should keep the navigation bar translucent to provide contrast between the navigation buttons and app content. Has no effect on Android 9 and below.',
default: 'true',
platform: 'android',
},
Expand All @@ -78,13 +76,13 @@ You can configure `expo-navigation-bar` using its built-in [config plugin](/conf

If you're not using Continuous Native Generation ([CNG](/workflow/continuous-native-generation/)) or you're using a native **android** project manually, then you need to add the following configuration to your native project:

- To hide the status bar on **Android**, add `expo_navigation_bar_visibility` to **android/app/src/main/res/values/strings.xml**:
- To hide the navigation bar on **Android**, add `expoNavigationBarHidden` to **android/app/src/main/res/values/styles.xml**:

```xml
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- ... -->
<string name="expo_navigation_bar_visibility" translatable="false">hidden</string>
</resources>
<item name="expoNavigationBarHidden">true</item>
</style>
```

</ConfigReactNative>
Expand Down
10 changes: 5 additions & 5 deletions docs/pages/versions/unversioned/sdk/status-bar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { ContentSpotlight } from '~/ui/components/ContentSpotlight';
import { SnackInline } from '~/ui/components/Snippet';

`expo-status-bar` gives you a component and imperative interface to control the app status bar to change its text color, background color, hide it, make it translucent or opaque, and apply animations to any of these changes. Exactly what you are able to do with the `StatusBar` component depends on the platform you're using.
`expo-status-bar` gives you a component and imperative interface to control the app status bar to change its text color, hide it, and apply animations to any of these changes. Exactly what you are able to do with the `StatusBar` component depends on the platform you're using.

> **tvOS and web support**
>
Expand Down Expand Up @@ -77,13 +77,13 @@ You can configure `expo-status-bar` using its built-in [config plugin](/config-p

If you're not using Continuous Native Generation ([CNG](/workflow/continuous-native-generation/)) or you're using a native project manually, then you need to add the following configuration to your native project:

- To hide the status bar on **Android**, add `expo_status_bar_visibility` to **android/app/src/main/res/values/strings.xml**:
- To hide the status bar on **Android**, add `expoStatusBarHidden` to **android/app/src/main/res/values/styles.xml**:

```xml
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- ... -->
<string name="expo_status_bar_visibility" translatable="false">hidden</string>
</resources>
<item name="expoStatusBarHidden">true</item>
</style>
```

- To hide the status bar on **iOS**, set the following keys in your **ios/&lt;project&gt;/Info.plist**:
Expand Down
144 changes: 144 additions & 0 deletions docs/pages/versions/unversioned/sdk/ui/jetpack-compose/tooltip.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
title: Tooltip
description: Jetpack Compose Tooltip components for displaying contextual information on long-press.
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui'
packageName: '@expo/ui'
platforms: ['android']
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';

Expo UI Tooltip matches the official Jetpack Compose [Tooltip](https://developer.android.com/develop/ui/compose/components/tooltip) API. `TooltipBox` wraps anchor content and displays a tooltip. The tooltip content is provided via the `TooltipBox.PlainTooltip` or `TooltipBox.RichTooltip` compound components, which match [`PlainTooltip`](https://developer.android.com/develop/ui/compose/components/tooltip#display-plain) and [`RichTooltip`](https://developer.android.com/develop/ui/compose/components/tooltip#display-rich) respectively. Tooltips can be triggered by long-press or shown programmatically via `ref`.

## Installation

<APIInstallSection />

## Usage

### Plain tooltip

Long-press the anchor content to display the tooltip.

```tsx PlainTooltipExample.tsx
import { Host, TooltipBox, Button, Text } from '@expo/ui/jetpack-compose';

export default function PlainTooltipExample() {
return (
<Host matchContents>
<TooltipBox>
<TooltipBox.PlainTooltip>
<Text>Add to favorites</Text>
</TooltipBox.PlainTooltip>
<Button onClick={() => {}}>
<Text>Favorite</Text>
</Button>
</TooltipBox>
</Host>
);
}
```

### Rich tooltip with title and body

Use `TooltipBox.RichTooltip` with `Title` and `Text` compound component pattern for more detailed contextual information.

```tsx RichTooltipExample.tsx
import { Host, TooltipBox, Button, Text } from '@expo/ui/jetpack-compose';

export default function RichTooltipExample() {
return (
<Host matchContents>
<TooltipBox>
<TooltipBox.RichTooltip>
<TooltipBox.RichTooltip.Title>
<Text>Camera</Text>
</TooltipBox.RichTooltip.Title>
<TooltipBox.RichTooltip.Text>
<Text>Take photos and record videos with your device camera.</Text>
</TooltipBox.RichTooltip.Text>
</TooltipBox.RichTooltip>
<Button onClick={() => {}}>
<Text>Open Camera</Text>
</Button>
</TooltipBox>
</Host>
);
}
```

### Rich tooltip with action

Add an interactive action with `TooltipBox.RichTooltip.Action`. Use `isPersistent` so the tooltip stays visible for the user to tap it. `hasAction` is automatically derived when an action slot is present.

```tsx RichTooltipActionExample.tsx
import { Host, TooltipBox, Button, TextButton, Text } from '@expo/ui/jetpack-compose';

export default function RichTooltipActionExample() {
return (
<Host matchContents>
<TooltipBox isPersistent>
<TooltipBox.RichTooltip>
<TooltipBox.RichTooltip.Title>
<Text>Permissions Required</Text>
</TooltipBox.RichTooltip.Title>
<TooltipBox.RichTooltip.Text>
<Text>This feature requires camera and microphone access.</Text>
</TooltipBox.RichTooltip.Text>
<TooltipBox.RichTooltip.Action>
<TextButton onClick={() => {}}>
<Text>Learn More</Text>
</TextButton>
</TooltipBox.RichTooltip.Action>
</TooltipBox.RichTooltip>
<Button onClick={() => {}}>
<Text>Record Video</Text>
</Button>
</TooltipBox>
</Host>
);
}
```

### Programmatic show and dismiss

Use a `ref` to imperatively `show()` or `dismiss()` the tooltip without requiring a long-press.

```tsx ProgrammaticTooltipExample.tsx
import { useRef } from 'react';
import { Host, TooltipBox, type TooltipBoxRef, Button, Text, Row } from '@expo/ui/jetpack-compose';

export default function ProgrammaticTooltipExample() {
const tooltipRef = useRef<TooltipBoxRef>(null);

return (
<Host matchContents>
<TooltipBox ref={tooltipRef} isPersistent>
<TooltipBox.PlainTooltip>
<Text>Shown programmatically!</Text>
</TooltipBox.PlainTooltip>
<Button onClick={() => {}}>
<Text>Anchor</Text>
</Button>
</TooltipBox>
<Row horizontalArrangement={{ spacedBy: 8 }}>
<Button onClick={() => tooltipRef.current?.show()}>
<Text>Show</Text>
</Button>
<Button onClick={() => tooltipRef.current?.dismiss()}>
<Text>Dismiss</Text>
</Button>
</Row>
</Host>
);
}
```

## API

```tsx
import { TooltipBox } from '@expo/ui/jetpack-compose';
```

<APISection packageName="expo-ui/jetpack-compose/tooltip" apiName="TooltipBox" />
Loading
Loading