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
77 changes: 77 additions & 0 deletions components/Package/CollapsibleSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useEffect, useState } from 'react';
import { View } from 'react-native';

import { H6 } from '~/common/styleguide';
import { Button } from '~/components/Button';
import { Arrow } from '~/components/Icons';
import tw from '~/util/tailwind';

import DependencyRow from './DependencyRow';
import EntityCounter from './EntityCounter';

type Props = {
title: string;
data?: Record<string, string> | null;
};

export default function CollapsibleSection({ title, data }: Props) {
const sectionKey = sanitizeTitle(title);
const key = `@ReactNativeDirectory:PackageSectionCollapsed:${sectionKey}`;

const [collapsed, setCollapsed] = useState<boolean>(
Boolean(window.localStorage.getItem(key)) ?? false
);

useEffect(() => {
async function refreshState() {
const val = await AsyncStorage.getItem(key);
setCollapsed(val === 'true');
}

void refreshState();
}, []);

if (!data || Object.keys(data).length === 0) {
return null;
}

async function toggleSection() {
const next = !collapsed;
setCollapsed(next);
await AsyncStorage.setItem(key, next ? 'true' : 'false');
}

return (
<>
<View style={tw`flex-row gap-1.5 justify-between items-center`}>
<H6 style={tw`flex items-center gap-1.5 text-[16px] text-secondary`}>
{title}
<EntityCounter count={Object.keys(data).length} />
</H6>
<Button
onPress={toggleSection}
style={tw`p-1 bg-palette-gray2 dark:bg-palette-gray7`}
containerStyle={tw`mt-px`}
aria-label={`${collapsed ? 'Expand' : 'Collapse'} section`}>
<Arrow style={[tw`w-4 h-3 text-icon`, collapsed ? tw`rotate-90` : tw`rotate-270`]} />
</Button>
</View>
{!collapsed && (
<View>
{Object.entries(data).map(([name, version]) => (
<DependencyRow key={`${sectionKey}-${name}`} name={name} version={version} />
))}
</View>
)}
</>
);
}

function sanitizeTitle(input: string) {
return String(input)
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
}
2 changes: 1 addition & 1 deletion components/Package/EntityCounter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function EntityCounter({ count, style }: Props) {
return (
<Label
style={[
tw`mt-[3px] text-[11px] rounded-xl py-0.5 px-1.5 text-[inherit] bg-palette-gray2 dark:bg-accented`,
tw`mt-[3px] h-4.5 text-[11px] rounded-xl py-0.5 px-1.5 text-[inherit] bg-palette-gray2 dark:bg-accented`,
style,
]}>
{count}
Expand Down
56 changes: 5 additions & 51 deletions scenes/PackageOverviewScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ContentContainer from '~/components/ContentContainer';
import MetaData from '~/components/Library/MetaData';
import TrendingMark from '~/components/Library/TrendingMark';
import UpdatedAtView from '~/components/Library/UpdateAtView';
import DependencyRow from '~/components/Package/DependencyRow';
import CollapsibleSection from '~/components/Package/CollapsibleSection';
import DetailsNavigation from '~/components/Package/DetailsNavigation';
import EntityCounter from '~/components/Package/EntityCounter';
import ExampleBox from '~/components/Package/ExampleBox';
Expand All @@ -19,7 +19,6 @@ import ReadmeBox from '~/components/Package/ReadmeBox';
import PageMeta from '~/components/PageMeta';
import { type NpmUser } from '~/types';
import { type PackageOverviewPageProps } from '~/types/pages';
import mapDependencies from '~/util/mapDependencies';
import tw from '~/util/tailwind';

const ReadmeBoxWithLoading = dynamic(() => import('~/components/Package/ReadmeBox'), {
Expand Down Expand Up @@ -184,55 +183,10 @@ export default function PackageOverviewScene({
</ul>
</>
)}
{dependencies && Object.keys(dependencies).length > 0 && (
<>
<H6 style={tw`flex gap-1.5 text-[16px] text-secondary`}>
Dependencies
<EntityCounter count={Object.keys(dependencies).length} />
</H6>
<View>
{mapDependencies(dependencies, ([name, version]: [string, string]) => (
<DependencyRow key={`dep-${name}`} name={name} version={version} />
))}
</View>
</>
)}
{peerDependencies && Object.keys(peerDependencies).length > 0 && (
<>
<H6 style={tw`flex gap-1.5 text-[16px] text-secondary`}>
Peer dependencies
<EntityCounter count={Object.keys(peerDependencies).length} />
</H6>
<View>
{mapDependencies(peerDependencies, ([name, version]: [string, string]) => (
<DependencyRow key={`peer-dep-${name}`} name={name} version={version} />
))}
</View>
</>
)}
{devDependencies && Object.keys(devDependencies).length > 0 && (
<>
<H6 style={tw`flex gap-1.5 text-[16px] text-secondary`}>
Development dependencies
<EntityCounter count={Object.keys(devDependencies).length} />
</H6>
<View>
{mapDependencies(devDependencies, ([name, version]: [string, string]) => (
<DependencyRow key={`dev-dep-${name}`} name={name} version={version} />
))}
</View>
</>
)}
{engines && Object.keys(engines).length > 0 && (
<>
<H6 style={tw`text-[16px] text-secondary`}>Engines</H6>
<View>
{mapDependencies(engines, ([name, version]: [string, string]) => (
<DependencyRow key={`engine-${name}`} name={name} version={version} />
))}
</View>
</>
)}
<CollapsibleSection title="Dependencies" data={dependencies} />
<CollapsibleSection title="Peer dependencies" data={peerDependencies} />
<CollapsibleSection title="Development dependencies" data={devDependencies} />
<CollapsibleSection title="Engines" data={engines} />
</View>
</View>
</ContentContainer>
Expand Down