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
100 changes: 50 additions & 50 deletions bun.lock

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions common/styleguide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ const textStyles = StyleSheet.create({
h1: tw`text-[57.25px] font-semibold`,
h2: tw`text-[35.5px] font-semibold`,
h3: tw`text-[26.5px] font-semibold`,
h4: tw`text-[22px]`,
h4: tw`text-[22px] tracking-tight`,
h5: tw`text-[20px]`,
h6: tw`text-[18px]`,
h6section: tw`text-[16px] tracking-tight text-secondary`,
headline: tw`text-[16px] font-medium`,
p: tw`text-[16px]`,
caption: tw`text-[15px] leading-[22px]`,
label: tw`text-[12px] font-medium`,
caption: tw`text-[15px] leading-[22px] tracking-normal`,
label: tw`text-[12px] font-medium tracking-normal`,
});

type CustomTextProps = TextProps &
Expand Down Expand Up @@ -77,6 +78,7 @@ export const H3 = createTextComponent(HtmlElements.H3, textStyles.h3);
export const H4 = createTextComponent(HtmlElements.H4, textStyles.h4);
export const H5 = createTextComponent(HtmlElements.H5, textStyles.h5);
export const H6 = createTextComponent(HtmlElements.H6, textStyles.h6);
export const H6Section = createTextComponent(HtmlElements.H6, textStyles.h6section);
export const P = createTextComponent(HtmlElements.P, textStyles.p);
export const Headline = createTextComponent(HtmlElements.P, textStyles.headline);
export const Caption = createTextComponent(HtmlElements.P, textStyles.caption);
Expand Down
2 changes: 1 addition & 1 deletion components/Library/TrendingMark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function TrendingMark({ library, style, markOnly = false }: Props
/>
<P
style={[
tw`pl-10 font-bold`,
tw`pl-10`,
markOnly ? tw`-my-px text-[15px]` : tw`my-0.5 text-xs`,
{
color: popularityStyles.backgroundColor,
Expand Down
66 changes: 21 additions & 45 deletions components/Package/CollapsibleSection.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,37 @@
import { useEffect, useState } from 'react';
import { View } from 'react-native';
import useSWR from 'swr';
import { type PropsWithChildren, type ReactElement, useEffect, useState } from 'react';
import { type StyleProp, View } from 'react-native';
import { type Style } from 'twrnc';

import { H6 } from '~/common/styleguide';
import { H6Section } from '~/common/styleguide';
import { Button } from '~/components/Button';
import { Arrow } from '~/components/Icons';
import { type PeerDependencyData } from '~/types';
import { TimeRange } from '~/util/datetime';
import tw from '~/util/tailwind';

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

type Props = {
type Props = PropsWithChildren<{
title: string;
data?: Record<string, string | PeerDependencyData> | null;
checkExistence?: boolean;
};
count?: number;
rightSlot?: ReactElement;
headerStyle?: StyleProp<Style>;
}>;

export default function CollapsibleSection({ title, data, checkExistence }: Props) {
export default function CollapsibleSection({
title,
count,
rightSlot,
headerStyle,
children,
}: Props) {
const sectionKey = sanitizeTitle(title);
const key = `@ReactNativeDirectory:PackageSectionCollapsed:${sectionKey}`;
const noData = !data || Object.keys(data).length === 0;

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

const { data: checkData } = useSWR(
checkExistence && !noData
? `/api/library?name=${Object.keys(data).join(',')}&check=true`
: null,
(url: string) => fetch(url).then(res => res.json()),
{
dedupingInterval: TimeRange.HOUR * 1000,
revalidateOnFocus: false,
}
);

useEffect(() => {
setCollapsed(window.localStorage.getItem(key) === 'true');
}, [key]);

if (noData) {
return null;
}

async function toggleSection() {
const nextState = !collapsed;
setCollapsed(nextState);
Expand All @@ -53,10 +41,11 @@ export default function CollapsibleSection({ title, data, checkExistence }: Prop
return (
<>
<View style={tw`flex-row items-center justify-between gap-1.5`}>
<H6 style={tw`flex items-center gap-1.5 text-[16px] text-secondary`}>
<H6Section style={[tw`flex items-center gap-1.5`, headerStyle]}>
{title}
<EntityCounter count={Object.keys(data).length} />
</H6>
{count && <EntityCounter count={count} />}
</H6Section>
{!collapsed ? rightSlot : undefined}
<Button
onPress={toggleSection}
style={tw`bg-palette-gray2 p-1 dark:bg-palette-gray7`}
Expand All @@ -65,20 +54,7 @@ export default function CollapsibleSection({ title, data, checkExistence }: Prop
<Arrow style={[tw`h-3 w-4 text-icon`, collapsed ? tw`rotate-90` : tw`rotate-270`]} />
</Button>
</View>
{!collapsed && (
<View>
{Object.entries(data)
.sort(([aName], [bName]) => aName.localeCompare(bName))
.map(([name, depData]) => (
<DependencyRow
key={`${sectionKey}-${name}`}
name={name}
data={depData}
packageExists={checkData?.[name]}
/>
))}
</View>
)}
{!collapsed && <View>{children}</View>}
</>
);
}
Expand Down
47 changes: 47 additions & 0 deletions components/Package/DependenciesSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import useSWR from 'swr';

import CollapsibleSection from '~/components/Package/CollapsibleSection';
import { type PeerDependencyData } from '~/types';
import { TimeRange } from '~/util/datetime';

import DependencyRow from './DependencyRow';

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

export default function DependenciesSection({ title, data, checkExistence }: Props) {
const noData = !data || Object.keys(data).length === 0;

const { data: checkData } = useSWR(
checkExistence && !noData
? `/api/library?name=${Object.keys(data).join(',')}&check=true`
: null,
(url: string) => fetch(url).then(res => res.json()),
{
dedupingInterval: TimeRange.HOUR * 1000,
revalidateOnFocus: false,
}
);

if (noData) {
return null;
}

return (
<CollapsibleSection title={title} count={Object.keys(data).length}>
{Object.entries(data)
.sort(([aName], [bName]) => aName.localeCompare(bName))
.map(([name, depData]) => (
<DependencyRow
key={`${title.toLocaleLowerCase()}-${name}`}
name={name}
data={depData}
packageExists={checkData?.[name]}
/>
))}
</CollapsibleSection>
);
}
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`h-4.5 mt-[3px] rounded-xl bg-palette-gray2 px-1.5 py-0.5 text-[11px] text-[inherit] dark:bg-accented`,
tw`h-4.5 mt-[3px] rounded-xl bg-palette-gray2 px-1.5 py-0.5 text-[11px] tabular-nums text-[inherit] dark:bg-accented`,
style,
]}>
{count}
Expand Down
12 changes: 4 additions & 8 deletions components/Package/MorePackagesBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useMemo } from 'react';
import { View } from 'react-native';
import useSWR from 'swr';

import { A, Caption, H6, Label, useLayout } from '~/common/styleguide';
import { A, Caption, H6Section, Label, useLayout } from '~/common/styleguide';
import { Download, Star, Warning } from '~/components/Icons';
import Tooltip from '~/components/Tooltip';
import { type APIResponseType, type LibraryType } from '~/types';
Expand Down Expand Up @@ -52,21 +52,17 @@ export default function MorePackagesBox({ library }: Props) {

return (
<>
<H6
style={[
tw`flex items-center gap-1.5 text-[16px] text-secondary`,
!isSmallScreen && tw`mt-4`,
]}>
<H6Section style={[tw`flex items-center gap-1.5`, !isSmallScreen && tw`mt-4`]}>
More packages from {startCase(owner)}
{!isLoading && data?.total && data.total > 0 ? (
<EntityCounter count={data.total > LIMIT ? data.total : data.total - 1} />
) : null}
{!isSmallScreen && data?.total && data.total > LIMIT && (
<A href={`/packages?owner=${encodeURI(owner)}`} style={tw`ml-auto`}>
<Caption style={tw`font-light`}>See all packages</Caption>
<Label style={tw`font-light`}>See all packages</Label>
</A>
)}
</H6>
</H6Section>
{!data || isLoading ? (
<ThreeDotsLoader />
) : (
Expand Down
23 changes: 12 additions & 11 deletions components/Package/TopicsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useState } from 'react';
import { View } from 'react-native';

import { A, H6, Label } from '~/common/styleguide';
import { A, Label } from '~/common/styleguide';
import { Button } from '~/components/Button';
import EntityCounter from '~/components/Package/EntityCounter';
import CollapsibleSection from '~/components/Package/CollapsibleSection';
import tw from '~/util/tailwind';

type Props = {
Expand All @@ -23,19 +23,20 @@ export default function TopicsSection({ topics }: Props) {
const trimmedTopics = hasManyTopics && !expanded ? topics.slice(0, MAX_TOPICS) : topics;

return (
<>
<H6 style={tw`-mb-0.5 flex min-h-[25px] items-center gap-1.5 text-[16px] text-secondary`}>
Topics
<EntityCounter count={topics.length} />
{hasManyTopics && !expanded && (
<CollapsibleSection
title="Topics"
count={topics.length}
headerStyle={tw`-mb-0.5 min-h-[25px]`}
rightSlot={
hasManyTopics && !expanded ? (
<Button
containerStyle={tw`ml-auto`}
style={tw`border border-default bg-default px-2 py-1`}
onPress={() => setExpanded(true)}>
<Label>Show All</Label>
<Label style={tw`text-[10px]`}>Show All</Label>
</Button>
)}
</H6>
) : undefined
}>
<View style={tw`flex-row flex-wrap items-start gap-x-2 gap-y-0.5`}>
{trimmedTopics.map(topic => (
<A key={topic} href={`/packages?search=${topic}`} style={tw`text-[12px] font-light`}>
Expand All @@ -48,6 +49,6 @@ export default function TopicsSection({ topics }: Props) {
</Label>
)}
</View>
</>
</CollapsibleSection>
);
}
6 changes: 3 additions & 3 deletions components/Package/VersionsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { type ColorValue, TextInput, View } from 'react-native';
import { useDebouncedCallback } from 'use-debounce';

import { Caption, H6, Label, useLayout } from '~/common/styleguide';
import { Caption, H6Section, Label, useLayout } from '~/common/styleguide';
import { Button } from '~/components/Button';
import { Search } from '~/components/Icons';
import InputKeyHint from '~/components/InputKeyHint';
Expand Down Expand Up @@ -73,15 +73,15 @@ export default function VersionsSection({ registryData, npmDownloads }: Props) {

return (
<>
<H6 style={tw`mt-3 flex items-end justify-between text-secondary`}>
<H6Section style={tw`mt-3 flex items-end justify-between text-secondary`}>
<span>Versions</span>
<Label style={tw`font-light text-secondary`}>
<span style={tw`font-medium text-primary-darker dark:text-primary-dark`}>
{filteredVersions.length}
</span>{' '}
matching {pluralize('version', filteredVersions.length)}
</Label>
</H6>
</H6Section>
<View style={tw`gap-2`}>
<View
style={tw`flex-row items-center rounded-lg border-2 border-default bg-palette-gray1 dark:bg-dark`}>
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@
"next-compose-plugins": "^2.2.1",
"next-fonts": "^1.5.1",
"next-images": "^1.8.5",
"oxfmt": "^0.40.0",
"oxlint": "^1.55.0",
"oxlint-tsgolint": "^0.16.0",
"oxfmt": "^0.41.0",
"oxlint": "^1.56.0",
"oxlint-tsgolint": "^0.17.0",
"simple-git-hooks": "^2.13.1",
"typescript": "^5.9.3",
"user-agent-data-types": "^0.4.2"
Expand Down
Loading