Skyroc Native UI

Search

搜索栏,内置放大镜、清除按钮与取消操作

搜索栏(Search)是 Input 的场景化封装:输入框本体完全委托给 Input(固定 variant="filled"、默认开启 clearable),本组件只负责外层布局、左侧标签、默认放大镜图标和右侧操作按钮。值同样由 Input 托管,受控传 value + onChangeText,非受控传 defaultValue

import { Search } from '@skyroc/native-ui';

基础用法

只给 placeholder 就能得到一个完整搜索框:左侧放大镜、灰底输入区、有值时出现清除按钮,键盘右下角是「搜索」键。

SearchBasic.tsx
Loading…
import { Search } from '@skyroc/native-ui';
import { View } from 'react-native';

const SearchBasic = () => {
  return (
    <View className="bg-background py-2">
      <Search placeholder="搜索商品" />
    </View>
  );
};

export { SearchBasic };

何时使用

  • 列表页、商品页顶部的关键词检索入口。
  • 需要「标签 + 搜索框 + 取消」这类组合的搜索页头部,用 labelshowAction 拼装。
  • 只是一个普通文本输入(不含搜索语义)时用 Input,不要用 Search

形状

shape 控制输入区圆角:

形状圆角
square跟随 sizesm 小、lg 大),默认
round恒为胶囊形(rounded-full
SearchShape.tsx
Loading…
import { Search } from '@skyroc/native-ui';
import { View } from 'react-native';

const SHAPES = ['square', 'round'] as const;

const SearchShape = () => {
  return (
    <View className="bg-background py-2">
      {SHAPES.map(shape => (
        <Search
          key={shape}
          placeholder={shape}
          shape={shape}
        />
      ))}
    </View>
  );
};

export { SearchShape };

square 本身不输出任何圆角类,把圆角交回 Inputsize 决定 —— 否则统一写死一档圆角会把三种尺寸拍平成同一个视觉比例。

尺寸

size 同时作用于外层留白、搜索图标像素大小,并直接透传给内部 Input(决定高度与字号):

尺寸外层内边距图标输入框高度
smpx-2 py-1.5(8 / 6)1440
mdpx-3 py-2(12 / 8)1648
lgpx-4 py-2.5(16 / 10)1864
SearchSize.tsx
Loading…
import { Search } from '@skyroc/native-ui';
import { View } from 'react-native';

const SIZES = ['sm', 'md', 'lg'] as const;

const SearchSize = () => {
  return (
    <View className="bg-background py-2">
      {SIZES.map(size => (
        <Search
          key={size}
          placeholder={size}
          size={size}
        />
      ))}
    </View>
  );
};

export { SearchSize };

标签与操作

label 渲染在输入框左侧,showAction 打开右侧操作按钮,按钮内容由 action 决定(默认文本「取消」),点击触发 onCancel。两者都接受 string / number(自动包一层 Text)或任意节点(原样渲染)。

SearchLabelAction.tsx
Loading…
import Feather from '@expo/vector-icons/Feather';
import { Search, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';
import { withUniwind } from 'uniwind';

/** Feather 不认 className,用 withUniwind 把 `accent-*` 工具类映射到 color 上,避免写死 hex */
const Icon = withUniwind(Feather);

const SearchLabelAction = () => {
  const [cancelCount, setCancelCount] = useState(0);

  function handleCancel() {
    setCancelCount(prev => prev + 1);
  }

  return (
    <View className="bg-background py-2">
      <Search
        showAction
        label="城市"
        placeholder="搜索地点"
        onCancel={handleCancel}
      />
      <Search
        showAction
        action={
          <Icon
            colorClassName="accent-primary"
            name="sliders"
            size={18}
          />
        }
        placeholder="action 传图标节点"
        shape="round"
        onCancel={handleCancel}
      />
      <Text className="px-4 pt-2 text-sm text-muted-foreground">onCancel 触发次数:{cancelCount}</Text>
    </View>
  );
};

export { SearchLabelAction };

操作按钮只是一个带 role="button"Pressable,组件不会替你收起键盘或清空输入 —— 取消的语义由 onCancel 自行实现。

清除按钮

clearable 默认为 true(这点与 Input 相反),有值且未禁用时在输入区尾部显示清除按钮。清空动作由 Input 完成,onClear 只是通知。

SearchClearable.tsx
Loading…
import { Search, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

const SearchClearable = () => {
  const [clearCount, setClearCount] = useState(0);

  function handleClear() {
    setClearCount(prev => prev + 1);
  }

  return (
    <View className="bg-background py-2">
      <Search
        defaultValue="可清除的关键词"
        onClear={handleClear}
      />
      <Search
        clearable={false}
        defaultValue="不显示清除按钮"
      />
      <Text className="px-4 pt-2 text-sm text-muted-foreground">onClear 触发次数:{clearCount}</Text>
    </View>
  );
};

export { SearchClearable };

受控用法

value + onChangeText 即为受控。onSearch 在键盘搜索键按下时触发,参数是提交那一刻输入框里的文本。

SearchControlled.tsx
Loading…
import { Search, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

const SearchControlled = () => {
  const [controlled, setControlled] = useState('');
  const [submitted, setSubmitted] = useState('-');

  function handleSearch(value: string) {
    setSubmitted(value || '(空)');
  }

  return (
    <View className="bg-background py-2">
      <Search
        placeholder="输入后按键盘搜索键"
        value={controlled}
        onChangeText={setControlled}
        onSearch={handleSearch}
      />
      <Text className="px-4 pt-2 text-sm text-muted-foreground">当前值:{controlled || '(空)'}</Text>
      <Text className="px-4 text-sm text-muted-foreground">onSearch 收到:{submitted}</Text>
    </View>
  );
};

export { SearchControlled };

非受控用法

不传 value 时输入值由内部 Input 托管。onSearch 的参数取自提交事件的 nativeEvent.text,因此非受控下同样拿得到完整文本,不必自己再存一份 state。

SearchUncontrolled.tsx
Loading…
import { Search, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

const SearchUncontrolled = () => {
  const [submitted, setSubmitted] = useState('-');

  function handleSearch(value: string) {
    setSubmitted(value || '(空)');
  }

  return (
    <View className="bg-background py-2">
      {/* 值由 Input 内部托管,onSearch 取的是提交事件里的文本,同样拿得到 */}
      <Search
        defaultValue="非受控默认值"
        placeholder="改一改再按搜索键"
        onSearch={handleSearch}
      />
      <Text className="px-4 pt-2 text-sm text-muted-foreground">onSearch 收到:{submitted}</Text>
    </View>
  );
};

export { SearchUncontrolled };

onSubmitEditing 也照常触发(在 onSearch 之后),需要原始事件对象时用它。

自定义前置内容

leading 传入后完全取代默认放大镜图标,可用于「定位图标 + 城市搜索」这类变体。

SearchCustom.tsx
Loading…
import Feather from '@expo/vector-icons/Feather';
import { Search } from '@skyroc/native-ui';
import { View } from 'react-native';
import { withUniwind } from 'uniwind';

/** Feather 不认 className,用 withUniwind 把 `accent-*` 工具类映射到 color 上,避免写死 hex */
const Icon = withUniwind(Feather);

const SearchCustom = () => {
  return (
    <View className="bg-background py-2">
      <Search
        leading={
          <Icon
            colorClassName="accent-primary"
            name="map-pin"
            size={16}
          />
        }
        placeholder="替换默认放大镜"
      />
    </View>
  );
};

export { SearchCustom };

图标不会自动继承主题色:@expo/vector-icons 的组件不认 className,demo 里用 withUniwindaccent-* 工具类映射到 color

样式覆盖

className 追加到根容器上,classNames 覆盖搜索栏自身的 slot,inputClassNames 继续下钻到内部 Input 的 slot:

slot归属作用位置
rootclassNames最外层 View(横向排布、内边距)
labelclassNames左侧标签文字
inputclassNames内部 Input 的根节点(背景、圆角)
actionclassNames右侧操作按钮容器 Pressable
actionTextclassNames右侧操作按钮的文字
controlinputClassNames内部 InputTextInput
actioninputClassNames内部 Input 的清除按钮容器
SearchStyles.tsx
Loading…
import { Search } from '@skyroc/native-ui';
import { View } from 'react-native';

/** ClassName / classNames 控制 Search 布局,inputClassNames 继续下钻到内部 Input */
const SearchStyles = () => {
  return (
    <View className="bg-background py-2">
      <Search
        showAction
        className="rounded-xl border border-primary-200"
        classNames={{ actionText: 'font-semibold text-destructive', input: 'bg-primary-50', label: 'text-primary' }}
        label="范围"
        placeholder="Search 各 slot"
      />
      <Search
        clearable
        defaultValue="内部 Input 的 control / action"
        inputClassNames={{ action: 'opacity-50', control: 'font-semibold text-primary' }}
      />
    </View>
  );
};

export { SearchStyles };

输入框根节点用 classNames.input 即可,inputClassNames.root 也会被合并进去,但不必两处都写。搜索图标的颜色由内部的 accent-muted-foreground 决定,不在可覆盖的 slot 内;需要换色请用 leading 自己传图标。

禁用

disabled 透传给内部 Input:输入不可编辑、整体降到 50% 不透明度、清除按钮消失。右侧操作按钮不受影响,需要一并禁用时自行处理 onCancel

SearchDisabled.tsx
Loading…
import { Search } from '@skyroc/native-ui';
import { View } from 'react-native';

const SearchDisabled = () => {
  return (
    <View className="bg-background py-2">
      <Search
        disabled
        defaultValue="不可编辑"
      />
    </View>
  );
};

export { SearchDisabled };

API

Search 继承 Input 的属性(placeholdervaluedefaultValueonChangeTextdisablederrortypetrailing 以及 TextInput 的原生属性),其中 variant 被固定为 filled 而不对外开放,className / classNames / leading 的语义见下表。

属性说明类型默认值
shape输入区圆角形状,square 跟随 size,round 恒为胶囊'square' | 'round''square'
size尺寸,决定外层留白、图标大小,并透传给内部 Input'sm' | 'md' | 'lg''md'
label左侧标签内容,string / number 自动包裹 TextReactNode-
leading左侧内容,传入后完全取代默认放大镜图标ReactNode-
showAction是否显示右侧操作按钮booleanfalse
action右侧操作按钮内容,string / number 自动包裹 TextReactNode'取消'
onCancel点击右侧操作按钮的回调() => void-
onSearch键盘搜索键触发,参数为提交时输入框内的文本(value: string) => void-
clearable是否可清除,有值且未禁用时显示清除按钮booleantrue
returnKeyType键盘右下角按键类型,透传给底层 TextInputTextInputProps["returnKeyType"]'search'
className根容器类名,合并到变体样式之后string-
classNames搜索栏各 slot 的类名覆盖,见「样式覆盖」一节SlotClassNames<SearchSlots>-
inputClassNames内部 Input 各 slot 的类名覆盖,主要用于 control 与 actionSlotClassNames<InputSlots>-
ref内部 TextInput 的 ref,可调用 focus / blur / clear 等原生方法Ref<TextInput>-

类型

import type { SearchProps, SearchSlots } from '@skyroc/native-ui';

SearchSlots

可通过 classNames 覆盖的 slot 名称,input 落在内部 Input 的根节点上。

'action' | 'actionText' | 'input' | 'label' | 'root'

InputSlots

内部 Input 的 slot 名称,供 inputClassNames 使用。

'action' | 'control' | 'root'

SlotClassNames

classNames 的取值形态:把 slot 名映射到类名,每个 slot 都可选。

Partial<Record<Slots, string>>

包内还导出了 searchVariantsSearchVariantPropsSEARCH_ICON_SIZE_MAP(各尺寸下搜索图标的像素大小)。