orn-ui
Organism

SearchList

Requiere <UIProvider> — Primeros pasos →

Instalación

Instala solo este componente (copia el código fuente a tu proyecto, sin dependencia npm)

npx orn-ui add search-list

O instala el paquete completo e impórtalo

pnpm add orn-ui
import { SearchList } from 'orn-ui/search-list';

Uso

  • cuándo usarlo — Una lista cuya entrada principal es un campo de búsqueda. Si no, List.
  • searchValue / onSearchChange — Controlado: el debounce y la query viven en la pantalla, no en el componente.
  • loadingMode — `replace` cambia la lista por un spinner; `overlay` mantiene el layout, lo que evita el salto mientras se escribe.
  • onScanPress — Agrega la acción de escanear al lado del campo — para códigos, QR, barras.
  • hasMore / onLoadMore — Paginado con final explícito, así el pie avisa cuando ya no queda nada.
Clip del demo todavía sin grabar — ver MEDIA.md

Variantes

Search + results

<SearchList
  searchValue={query}
  onSearchChange={setQuery}
  data={data.map((name) => ({ name }))}
  keyExtractor={(item) => item.name}
  renderItem={({ item }) => (
    <Card style={{ marginBottom: 8 }}>
      <Body>{item.name}</Body>
    </Card>
  )}
  emptyTitle="No matches"
/>

With scan action

<SearchList
  searchValue=""
  onSearchChange={() => {}}
  onScanPress={() => {}}
  data={[]}
  keyExtractor={(item: any) => item.name}
  renderItem={() => null}
  emptyTitle="Scan a barcode or search"
/>
Código completo del demo
const variants: VariantDef[] = [
  {
    label: 'Search + results',
    content: (
      <SearchList
        searchValue={query}
        onSearchChange={setQuery}
        data={data.map((name) => ({ name }))}
        keyExtractor={(item) => item.name}
        renderItem={({ item }) => (
          <Card style={{ marginBottom: 8 }}>
            <Body>{item.name}</Body>
          </Card>
        )}
        emptyTitle="No matches"
      />
    ),
  },
  {
    label: 'With scan action',
    content: (
      <SearchList
        searchValue=""
        onSearchChange={() => {}}
        onScanPress={() => {}}
        data={[]}
        keyExtractor={(item: any) => item.name}
        renderItem={() => null}
        emptyTitle="Scan a barcode or search"
      />
    ),
  },
];
return <VariantList variants={variants} fill />;

Props

NombreTipoPor defectoDescripción
header?ReactNodeChrome opcional arriba del buscador (ej. back button + título).
searchValuestring
onSearchChange(text: string) => void
searchPlaceholder?string
searchIsLoading?booleanfalse
onScanPress?() => void
scanAccessibilityLabel?stringScan
extraActions?ReactNode
dataT[]
keyExtractor(item: T, index: number) => string
renderItemListRenderItem<T>
isLoading?booleanfalseCarga inicial: mientras es true y no hay datos, se muestran placeholders.
loadingMode?'replace' | 'overlay'replace'replace' reemplaza toda la lista por un spinner/skeleton; 'overlay' lo muestra encima manteniendo el layout.
isRefreshing?booleanfalseRecarga con datos ya en pantalla (pull-to-refresh).
isLoadingMore?booleanfalse
isReady?booleantrueControlado por quien lo consume: mientras es false, se muestra un spinner de pantalla completa.
loadingText?string
loadingMoreText?string
hasMore?booleanfalse
noMoreText?string
skeletonCount?number6Filas fantasma de la carga inicial.
renderSkeletonItem?() => ReactElement<unknown, string | JSXElementConstructor<any>>Placeholder propio, para que calce con la forma real del ítem.
onLoadMore?() => void
onEndReached?() => void
onEndReachedThreshold?number0.3
onRefresh?() => void
emptyTitle?string
emptyDescription?string
emptyIconName?IconName
ListHeaderComponent?ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentType<any>
ListFooterComponent?ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentType<any>
contentContainerStyle?StyleProp<ViewStyle>
containerStyle?ViewStyle
ListComponent?ComponentType<any>FlatListComponente de lista a usar (FlashList, etc).
listProps?Record<string, unknown>