Add PostCSS configuration and skills lock file
- Created a new PostCSS configuration file to integrate Tailwind CSS. - Added a skills lock file containing various Expo skills with their respective source and computed hashes.
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
# Toolbars and headers
|
||||
|
||||
Add native iOS toolbar items to Stack screens. Items can be placed in the header (left/right) or in a bottom toolbar area.
|
||||
|
||||
**Important:** iOS only. Available in Expo SDK 55+.
|
||||
|
||||
## Notes app example
|
||||
|
||||
```tsx
|
||||
import { Stack } from "expo-router";
|
||||
import { ScrollView } from "react-native";
|
||||
|
||||
export default function FoldersScreen() {
|
||||
return (
|
||||
<>
|
||||
{/* ScrollView must be the first child of the screen */}
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
>
|
||||
{/* Screen content */}
|
||||
</ScrollView>
|
||||
<Stack.Screen.Title large>Folders</Stack.Screen.Title>
|
||||
<Stack.SearchBar placeholder="Search" onChangeText={() => {}} />
|
||||
{/* Header toolbar - right side */}
|
||||
<Stack.Toolbar placement="right">
|
||||
<Stack.Toolbar.Button icon="folder.badge.plus" onPress={() => {}} />
|
||||
<Stack.Toolbar.Button onPress={() => {}}>Edit</Stack.Toolbar.Button>
|
||||
</Stack.Toolbar>
|
||||
|
||||
{/* Bottom toolbar */}
|
||||
<Stack.Toolbar placement="bottom">
|
||||
<Stack.Toolbar.SearchBarSlot />
|
||||
<Stack.Toolbar.Button
|
||||
icon="square.and.pencil"
|
||||
onPress={() => {}}
|
||||
separateBackground
|
||||
/>
|
||||
</Stack.Toolbar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Mail inbox example
|
||||
|
||||
```tsx
|
||||
import { Color, Stack } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { ScrollView, Text, View } from "react-native";
|
||||
|
||||
export default function InboxScreen() {
|
||||
const [isFilterOpen, setIsFilterOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
contentInsetAdjustmentBehavior="automatic"
|
||||
contentContainerStyle={{ paddingHorizontal: 16 }}
|
||||
>
|
||||
{/* Screen content */}
|
||||
</ScrollView>
|
||||
<Stack.Screen options={{ headerTransparent: true }} />
|
||||
<Stack.Screen.Title>Inbox</Stack.Screen.Title>
|
||||
<Stack.SearchBar placeholder="Search" onChangeText={() => {}} />
|
||||
{/* Header toolbar - right side */}
|
||||
<Stack.Toolbar placement="right">
|
||||
<Stack.Toolbar.Button onPress={() => {}}>Select</Stack.Toolbar.Button>
|
||||
<Stack.Toolbar.Menu icon="ellipsis">
|
||||
<Stack.Toolbar.Menu inline>
|
||||
<Stack.Toolbar.Menu inline title="Sort By">
|
||||
<Stack.Toolbar.MenuAction isOn>
|
||||
Categories
|
||||
</Stack.Toolbar.MenuAction>
|
||||
<Stack.Toolbar.MenuAction>List</Stack.Toolbar.MenuAction>
|
||||
</Stack.Toolbar.Menu>
|
||||
<Stack.Toolbar.MenuAction icon="info.circle">
|
||||
About categories
|
||||
</Stack.Toolbar.MenuAction>
|
||||
</Stack.Toolbar.Menu>
|
||||
<Stack.Toolbar.MenuAction icon="person.circle">
|
||||
Show Contact Photos
|
||||
</Stack.Toolbar.MenuAction>
|
||||
</Stack.Toolbar.Menu>
|
||||
</Stack.Toolbar>
|
||||
|
||||
{/* Bottom toolbar */}
|
||||
<Stack.Toolbar placement="bottom">
|
||||
<Stack.Toolbar.Button
|
||||
icon="line.3.horizontal.decrease"
|
||||
selected={isFilterOpen}
|
||||
onPress={() => setIsFilterOpen((prev) => !prev)}
|
||||
/>
|
||||
<Stack.Toolbar.View hidden={!isFilterOpen}>
|
||||
<View style={{ width: 70, height: 32, justifyContent: "center" }}>
|
||||
<Text style={{ fontSize: 12, fontWeight: 700 }}>Filter by</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
color: Color.ios.systemBlue,
|
||||
}}
|
||||
>
|
||||
Unread
|
||||
</Text>
|
||||
</View>
|
||||
</Stack.Toolbar.View>
|
||||
<Stack.Toolbar.Spacer />
|
||||
<Stack.Toolbar.SearchBarSlot />
|
||||
<Stack.Toolbar.Button
|
||||
icon="square.and.pencil"
|
||||
onPress={() => {}}
|
||||
separateBackground
|
||||
/>
|
||||
</Stack.Toolbar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Placement
|
||||
|
||||
- `"left"` - Header left
|
||||
- `"right"` - Header right
|
||||
- `"bottom"` (default) - Bottom toolbar
|
||||
|
||||
## Components
|
||||
|
||||
### Button
|
||||
|
||||
- Icon button: `<Stack.Toolbar.Button icon="star.fill" onPress={() => {}} />`
|
||||
- Text button: `<Stack.Toolbar.Button onPress={() => {}}>Done</Stack.Toolbar.Button>`
|
||||
|
||||
**Props:** `icon`, `image`, `onPress`, `disabled`, `hidden`, `variant` (`"plain"` | `"done"` | `"prominent"`), `tintColor`
|
||||
|
||||
### Menu
|
||||
|
||||
Dropdown menu for grouping actions.
|
||||
|
||||
```tsx
|
||||
<Stack.Toolbar.Menu icon="ellipsis">
|
||||
<Stack.Toolbar.Menu inline>
|
||||
<Stack.Toolbar.MenuAction>Sort by Recently Added</Stack.Toolbar.MenuAction>
|
||||
<Stack.Toolbar.MenuAction isOn>
|
||||
Sort by Date Captured
|
||||
</Stack.Toolbar.MenuAction>
|
||||
</Stack.Toolbar.Menu>
|
||||
<Stack.Toolbar.Menu title="Filter">
|
||||
<Stack.Toolbar.Menu inline>
|
||||
<Stack.Toolbar.MenuAction isOn icon="square.grid.2x2">
|
||||
All Items
|
||||
</Stack.Toolbar.MenuAction>
|
||||
</Stack.Toolbar.Menu>
|
||||
<Stack.Toolbar.MenuAction icon="heart">Favorites</Stack.Toolbar.MenuAction>
|
||||
<Stack.Toolbar.MenuAction icon="photo">Photos</Stack.Toolbar.MenuAction>
|
||||
<Stack.Toolbar.MenuAction icon="video">Videos</Stack.Toolbar.MenuAction>
|
||||
</Stack.Toolbar.Menu>
|
||||
</Stack.Toolbar.Menu>
|
||||
```
|
||||
|
||||
**Menu Props:** All Button props plus `title`, `inline`, `palette`, `elementSize` (`"small"` | `"medium"` | `"large"`)
|
||||
|
||||
**MenuAction Props:** `icon`, `onPress`, `isOn`, `destructive`, `disabled`, `subtitle`
|
||||
|
||||
When creating a palette with dividers, use `inline` combined with `elementSize="small"`. `palette` will not apply dividers on iOS 26.
|
||||
|
||||
### Spacer
|
||||
|
||||
```tsx
|
||||
<Stack.Toolbar.Spacer /> // Bottom toolbar - flexible
|
||||
<Stack.Toolbar.Spacer width={16} /> // Header - requires explicit width
|
||||
```
|
||||
|
||||
### View
|
||||
|
||||
Embed custom React Native components. When adding a custom view make sure that there is only a single child with **explicit width and height**.
|
||||
|
||||
```tsx
|
||||
<Stack.Toolbar.View>
|
||||
<View style={{ width: 70, height: 32, justifyContent: "center" }}>
|
||||
<Text style={{ fontSize: 12, fontWeight: 700 }}>Filter by</Text>
|
||||
</View>
|
||||
</Stack.Toolbar.View>
|
||||
```
|
||||
|
||||
You can pass custom components to views as well:
|
||||
|
||||
```tsx
|
||||
function CustomFilterView() {
|
||||
return (
|
||||
<View style={{ width: 70, height: 32, justifyContent: "center" }}>
|
||||
<Text style={{ fontSize: 12, fontWeight: 700 }}>Filter by</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
...
|
||||
<Stack.Toolbar.View>
|
||||
<CustomFilterView />
|
||||
</Stack.Toolbar.View>
|
||||
```
|
||||
|
||||
## Recommendations
|
||||
|
||||
- When creating more complex headers, extract them to a single component
|
||||
|
||||
```tsx
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<ScrollView>{/* Screen content */}</ScrollView>
|
||||
<InboxHeader />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function InboxHeader() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen.Title>Inbox</Stack.Screen.Title>
|
||||
<Stack.SearchBar placeholder="Search" onChangeText={() => {}} />
|
||||
<Stack.Toolbar placement="right">{/* Toolbar buttons */}</Stack.Toolbar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
- When using `Stack.Toolbar`, make sure that all `Stack.Toolbar.*` components are wrapped inside `Stack.Toolbar` component.
|
||||
|
||||
This will **not work**:
|
||||
|
||||
```tsx
|
||||
function Buttons() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Toolbar.Button icon="star.fill" onPress={() => {}} />
|
||||
<Stack.Toolbar.Button onPress={() => {}}>Done</Stack.Toolbar.Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<>
|
||||
<ScrollView>{/* Screen content */}</ScrollView>
|
||||
<Stack.Toolbar placement="right">
|
||||
<Buttons /> {/* ❌ This will NOT work */}
|
||||
</Stack.Toolbar>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
This will work:
|
||||
|
||||
```tsx
|
||||
function ToolbarWithButtons() {
|
||||
return (
|
||||
<Stack.Toolbar>
|
||||
<Stack.Toolbar.Button icon="star.fill" onPress={() => {}} />
|
||||
<Stack.Toolbar.Button onPress={() => {}}>Done</Stack.Toolbar.Button>
|
||||
</Stack.Toolbar>
|
||||
);
|
||||
}
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<>
|
||||
<ScrollView>{/* Screen content */}</ScrollView>
|
||||
<ToolbarWithButtons /> {/* ✅ This will work */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
- iOS only
|
||||
- `placement="bottom"` can only be used inside screen components (not in layout files)
|
||||
- `Stack.Toolbar.Badge` only works with `placement="left"` or `"right"`
|
||||
- Header Spacers require explicit `width`
|
||||
|
||||
## Reference
|
||||
|
||||
Docs https://docs.expo.dev/versions/unversioned/sdk/router - read to see the full API.
|
||||
Reference in New Issue
Block a user