feat: replace content item dropdowns with sheets (#968)

This commit is contained in:
lostb1t
2025-08-21 17:19:53 +02:00
committed by GitHub
parent 576a820c0c
commit 2d69bd5103
8 changed files with 336 additions and 40 deletions

View File

@@ -5,6 +5,7 @@ import {
BottomSheetModal,
BottomSheetScrollView,
} from "@gorhom/bottom-sheet";
import { isEqual } from "lodash";
import type React from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -27,7 +28,7 @@ interface Props<T> extends ViewProps {
title: string;
searchFilter?: (item: T, query: string) => boolean;
renderItemLabel: (item: T) => React.ReactNode;
showSearch?: boolean;
disableSearch?: boolean;
multiple?: boolean;
}
@@ -49,7 +50,7 @@ const LIMIT = 100;
* @param {string} props.title - The title of the bottom sheet
* @param {function} props.searchFilter - Function to filter items based on search query
* @param {function} props.renderItemLabel - Function to render the label for each item
* @param {boolean} [props.showSearch=true] - Whether to show the search input
* @param {boolean} [props.disableSearch=false] - Whether to disable the search input
*
* @returns {React.ReactElement} The FilterSheet component
*
@@ -70,11 +71,11 @@ export const FilterSheet = <T,>({
title,
searchFilter,
renderItemLabel,
showSearch = true,
disableSearch = false,
multiple = false,
}: Props<T>) => {
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
const snapPoints = useMemo(() => ["80%"], []);
const snapPoints = useMemo(() => ["85%"], []);
const { t } = useTranslation();
const [data, setData] = useState<T[]>([]);
@@ -82,6 +83,8 @@ export const FilterSheet = <T,>({
const [search, setSearch] = useState<string>("");
const [showSearch, setShowSearch] = useState<boolean>(false);
const filteredData = useMemo(() => {
if (!search) return _data;
const results = [];
@@ -93,6 +96,13 @@ export const FilterSheet = <T,>({
return results.slice(0, 100);
}, [search, _data, searchFilter]);
useEffect(() => {
if (!data || data.length === 0 || disableSearch) return;
if (data.length > 15) {
setShowSearch(true);
}
}, [data]);
// Loads data in batches of LIMIT size, starting from offset,
// to implement efficient "load more" functionality
useEffect(() => {
@@ -159,7 +169,7 @@ export const FilterSheet = <T,>({
{showSearch && (
<Input
placeholder={t("search.search")}
className='my-2'
className='my-2 border-neutral-800 border'
value={search}
onChangeText={(text) => {
setSearch(text);
@@ -196,8 +206,8 @@ export const FilterSheet = <T,>({
}}
className=' bg-neutral-800 px-4 py-3 flex flex-row items-center justify-between'
>
<Text>{renderItemLabel(item)}</Text>
{values.some((i) => i === item) ? (
<Text className='flex shrink'>{renderItemLabel(item)}</Text>
{values.some((i) => isEqual(i, item)) ? (
<Ionicons name='radio-button-on' size={24} color='white' />
) : (
<Ionicons name='radio-button-off' size={24} color='white' />