Skip to content

Commit

Permalink
feat: introduce ignoresTopSafeArea prop
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Oct 9, 2024
1 parent c286206 commit 851640f
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 121 deletions.
13 changes: 9 additions & 4 deletions android/src/main/java/com/rcttabview/RCTTabViewViewManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ class RCTTabViewViewManager :
}
}

@ReactProp(name = "sidebarAdaptable")
fun setSidebarAdaptable(view: ReactBottomNavigationView, flag: Boolean) {
// iOS Only
}


@ReactProp(name = "labeled")
fun setLabeled(view: ReactBottomNavigationView, flag: Boolean?) {
Expand Down Expand Up @@ -132,4 +129,12 @@ class RCTTabViewViewManager :
MapBuilder.of("registrationName", "onPageSelected"),
)
}

// iOS Props

@ReactProp(name = "sidebarAdaptable")
fun setSidebarAdaptable(view: ReactBottomNavigationView, flag: Boolean) {}

@ReactProp(name = "ignoresTopSafeArea")
fun setIgnoresTopSafeArea(view: ReactBottomNavigationView, flag: Boolean) {}
}
11 changes: 10 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,27 @@ import SFSymbols from './Examples/SFSymbols';
import LabeledTabs from './Examples/Labeled';
import NativeBottomTabs from './Examples/NativeBottomTabs';

const FourTabsIgnoreSafeArea = () => {
return <FourTabs ignoresTopSafeArea />;
};

const examples = [
{ component: ThreeTabs, name: 'Three Tabs' },
{ component: FourTabs, name: 'Four Tabs' },
{ component: SFSymbols, name: 'SF Symbols' },
{ component: LabeledTabs, name: 'Labeled Tabs' },
{
component: FourTabs,
component: FourTabsIgnoreSafeArea,
name: 'Four Tabs - No header',
screenOptions: { headerShown: false },
},
{ component: NativeBottomTabs, name: 'Native Bottom Tabs' },
{ component: JSBottomTabs, name: 'JS Bottom Tabs' },
{
component: JSBottomTabs,
name: 'JS Bottom Tabs - No header',
screenOptions: { headerShown: false },
},
{ component: MaterialBottomTabs, name: 'Material (JS) Bottom Tabs' },
];

Expand Down
7 changes: 6 additions & 1 deletion example/src/Examples/FourTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';

export default function FourTabs() {
interface Props {
ignoresTopSafeArea?: boolean;
}

export default function FourTabs({ ignoresTopSafeArea = false }: Props) {
const [index, setIndex] = useState(0);
const [routes] = useState([
{
Expand Down Expand Up @@ -42,6 +46,7 @@ export default function FourTabs() {

return (
<TabView
ignoresTopSafeArea={ignoresTopSafeArea}
sidebarAdaptable
navigationState={{ index, routes }}
onIndexChange={setIndex}
Expand Down
35 changes: 16 additions & 19 deletions example/src/Screens/Albums.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
Image,
Platform,
SafeAreaView,
ScrollView,
type ScrollViewProps,
StyleSheet,
Expand Down Expand Up @@ -43,24 +42,22 @@ export function Albums(props: Partial<ScrollViewProps>) {
const itemSize = dimensions.width / Math.floor(dimensions.width / 150);

return (
<SafeAreaView>
<ScrollView contentContainerStyle={styles.content} {...props}>
{COVERS.map((source, i) => (
<View
key={i}
style={[
styles.item,
Platform.OS !== 'web' && {
height: itemSize,
width: itemSize,
},
]}
>
<Image source={source} resizeMode="cover" style={styles.photo} />
</View>
))}
</ScrollView>
</SafeAreaView>
<ScrollView contentContainerStyle={styles.content} {...props}>
{COVERS.map((source, i) => (
<View
key={i}
style={[
styles.item,
Platform.OS !== 'web' && {
height: itemSize,
width: itemSize,
},
]}
>
<Image source={source} resizeMode="cover" style={styles.photo} />
</View>
))}
</ScrollView>
);
}

Expand Down
178 changes: 87 additions & 91 deletions example/src/Screens/Article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Button,
Image,
Platform,
SafeAreaView,
ScrollView,
type ScrollViewProps,
StyleSheet,
Expand Down Expand Up @@ -50,98 +49,95 @@ export function Article({

console.log(Platform.OS, ' Rendering Article');
return (
<SafeAreaView>
<ScrollView
ref={ref}
style={{ backgroundColor: '#fff' }}
contentContainerStyle={styles.content}
{...rest}
>
<View style={styles.author}>
<Image
style={styles.avatar}
source={require('../../assets/avatar-1.png')}
/>
<View style={styles.meta}>
<Text style={[styles.name, { color: '#000' }]}>{author.name}</Text>
<Text style={[styles.timestamp, { color: '#000' }]}>{date}</Text>
</View>
</View>
<Button
title="Go to Albums"
onPress={() => {
jumpTo?.('albums');
}}
/>
<Heading>What is Lorem Ipsum?</Heading>
<Paragraph>
Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry&apos;s standard dummy text
ever since the 1500s, when an unknown printer took a galley of type
and scrambled it to make a type specimen book. It has survived not
only five centuries, but also the leap into electronic typesetting,
remaining essentially unchanged. It was popularised in the 1960s with
the release of Letraset sheets containing Lorem Ipsum passages, and
more recently with desktop publishing software like Aldus PageMaker
including versions of Lorem Ipsum.
</Paragraph>
<ScrollView
ref={ref}
style={{ backgroundColor: '#fff' }}

Check warning on line 54 in example/src/Screens/Article.tsx

View workflow job for this annotation

GitHub Actions / lint

Inline style: { backgroundColor: '#fff' }
contentContainerStyle={styles.content}
{...rest}
>
<View style={styles.author}>
<Image
style={styles.image}
resizeMode="cover"
source={require('../../assets/book.jpg')}
style={styles.avatar}
source={require('../../assets/avatar-1.png')}
/>
<Heading>Where does it come from?</Heading>
<Paragraph>
Contrary to popular belief, Lorem Ipsum is not simply random text. It
has roots in a piece of classical Latin literature from 45 BC, making
it over 2000 years old. Richard McClintock, a Latin professor at
Hampden-Sydney College in Virginia, looked up one of the more obscure
Latin words, consectetur, from a Lorem Ipsum passage, and going
through the cites of the word in classical literature, discovered the
undoubtable source. Lorem Ipsum comes from sections 1.10.32 and
1.10.33 of &quot;de Finibus Bonorum et Malorum&quot; (The Extremes of
Good and Evil) by Cicero, written in 45 BC. This book is a treatise on
the theory of ethics, very popular during the Renaissance. The first
line of Lorem Ipsum, &quot;Lorem ipsum dolor sit amet..&quot;, comes
from a line in section 1.10.32.
</Paragraph>
<Paragraph>
The standard chunk of Lorem Ipsum used since the 1500s is reproduced
below for those interested. Sections 1.10.32 and 1.10.33 from &quot;de
Finibus Bonorum et Malorum&quot; by Cicero are also reproduced in
their exact original form, accompanied by English versions from the
1914 translation by H. Rackham.
</Paragraph>
<Heading>Why do we use it?</Heading>
<Paragraph>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout. The point of
using Lorem Ipsum is that it has a more-or-less normal distribution of
letters, as opposed to using &quot;Content here, content here&quot;,
making it look like readable English. Many desktop publishing packages
and web page editors now use Lorem Ipsum as their default model text,
and a search for &quot;lorem ipsum&quot; will uncover many web sites
still in their infancy. Various versions have evolved over the years,
sometimes by accident, sometimes on purpose (injected humour and the
like).
</Paragraph>
<Heading>Where can I get some?</Heading>
<Paragraph>
There are many variations of passages of Lorem Ipsum available, but
the majority have suffered alteration in some form, by injected
humour, or randomised words which don&apos;t look even slightly
believable. If you are going to use a passage of Lorem Ipsum, you need
to be sure there isn&apos;t anything embarrassing hidden in the middle
of text. All the Lorem Ipsum generators on the Internet tend to repeat
predefined chunks as necessary, making this the first true generator
on the Internet. It uses a dictionary of over 200 Latin words,
combined with a handful of model sentence structures, to generate
Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is
therefore always free from repetition, injected humour, or
non-characteristic words etc.
</Paragraph>
</ScrollView>
</SafeAreaView>
<View style={styles.meta}>
<Text style={[styles.name, { color: '#000' }]}>{author.name}</Text>

Check warning on line 64 in example/src/Screens/Article.tsx

View workflow job for this annotation

GitHub Actions / lint

Inline style: { color: '#000' }
<Text style={[styles.timestamp, { color: '#000' }]}>{date}</Text>
</View>
</View>
<Button
title="Go to Albums"
onPress={() => {
jumpTo?.('albums');
}}
/>
<Heading>What is Lorem Ipsum?</Heading>
<Paragraph>
Lorem Ipsum is simply dummy text of the printing and typesetting
industry. Lorem Ipsum has been the industry&apos;s standard dummy text
ever since the 1500s, when an unknown printer took a galley of type and
scrambled it to make a type specimen book. It has survived not only five
centuries, but also the leap into electronic typesetting, remaining
essentially unchanged. It was popularised in the 1960s with the release
of Letraset sheets containing Lorem Ipsum passages, and more recently
with desktop publishing software like Aldus PageMaker including versions
of Lorem Ipsum.
</Paragraph>
<Image
style={styles.image}
resizeMode="cover"
source={require('../../assets/book.jpg')}
/>
<Heading>Where does it come from?</Heading>
<Paragraph>
Contrary to popular belief, Lorem Ipsum is not simply random text. It
has roots in a piece of classical Latin literature from 45 BC, making it
over 2000 years old. Richard McClintock, a Latin professor at
Hampden-Sydney College in Virginia, looked up one of the more obscure
Latin words, consectetur, from a Lorem Ipsum passage, and going through
the cites of the word in classical literature, discovered the
undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33
of &quot;de Finibus Bonorum et Malorum&quot; (The Extremes of Good and
Evil) by Cicero, written in 45 BC. This book is a treatise on the theory
of ethics, very popular during the Renaissance. The first line of Lorem
Ipsum, &quot;Lorem ipsum dolor sit amet..&quot;, comes from a line in
section 1.10.32.
</Paragraph>
<Paragraph>
The standard chunk of Lorem Ipsum used since the 1500s is reproduced
below for those interested. Sections 1.10.32 and 1.10.33 from &quot;de
Finibus Bonorum et Malorum&quot; by Cicero are also reproduced in their
exact original form, accompanied by English versions from the 1914
translation by H. Rackham.
</Paragraph>
<Heading>Why do we use it?</Heading>
<Paragraph>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout. The point of
using Lorem Ipsum is that it has a more-or-less normal distribution of
letters, as opposed to using &quot;Content here, content here&quot;,
making it look like readable English. Many desktop publishing packages
and web page editors now use Lorem Ipsum as their default model text,
and a search for &quot;lorem ipsum&quot; will uncover many web sites
still in their infancy. Various versions have evolved over the years,
sometimes by accident, sometimes on purpose (injected humour and the
like).
</Paragraph>
<Heading>Where can I get some?</Heading>
<Paragraph>
There are many variations of passages of Lorem Ipsum available, but the
majority have suffered alteration in some form, by injected humour, or
randomised words which don&apos;t look even slightly believable. If you
are going to use a passage of Lorem Ipsum, you need to be sure there
isn&apos;t anything embarrassing hidden in the middle of text. All the
Lorem Ipsum generators on the Internet tend to repeat predefined chunks
as necessary, making this the first true generator on the Internet. It
uses a dictionary of over 200 Latin words, combined with a handful of
model sentence structures, to generate Lorem Ipsum which looks
reasonable. The generated Lorem Ipsum is therefore always free from
repetition, injected humour, or non-characteristic words etc.
</Paragraph>
</ScrollView>
);
}

Expand Down
1 change: 1 addition & 0 deletions ios/RCTTabViewViewManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(icons, NSArray<RCTImageSource *>);
RCT_EXPORT_VIEW_PROPERTY(sidebarAdaptable, BOOL)
RCT_EXPORT_VIEW_PROPERTY(labeled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(ignoresTopSafeArea, BOOL)

@end
23 changes: 18 additions & 5 deletions ios/TabViewImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class TabViewProps: ObservableObject {
@Published var icons: [Int: UIImage] = [:]
@Published var sidebarAdaptable: Bool?
@Published var labeled: Bool?
@Published var ignoresTopSafeArea: Bool?
}

/**
Expand All @@ -32,16 +33,19 @@ struct RepresentableView: UIViewRepresentable {
struct TabViewImpl: View {
@ObservedObject var props: TabViewProps
var onSelect: (_ key: String) -> Void

var body: some View {
TabView(selection: $props.selectedPage) {
ForEach(props.children?.indices ?? 0..<0, id: \.self) { index in
let child = props.children?[safe: index] ?? UIView()
let tabData = props.items?.tabs[safe: index]
let icon = props.icons[index]

RepresentableView(view: child)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresTopSafeArea(
props.ignoresTopSafeArea ?? false,
frame: child.frame
)
.tabItem {
TabItem(
title: tabData?.title,
Expand Down Expand Up @@ -73,7 +77,7 @@ struct TabItem: View {
var icon: UIImage?
var sfSymbol: String?
var labeled: Bool?

var body: some View {
if let icon {
Image(uiImage: icon)
Expand All @@ -99,7 +103,7 @@ extension View {
self
}
}

@ViewBuilder
func tabBadge(_ data: String?) -> some View {
if #available(iOS 15.0, macOS 15.0, visionOS 2.0, *) {
Expand All @@ -112,4 +116,13 @@ extension View {
self
}
}

@ViewBuilder
func ignoresTopSafeArea(_ flag: Bool, frame: CGRect) -> some View {
if flag {
self.frame(width: frame.width, height: frame.height)
} else {
self.frame(width: frame.width)
}
}
}
Loading

0 comments on commit 851640f

Please sign in to comment.