| author | |
| committer | |
| log | 14337401bc801d6239dd10e34997b4481081cf1d |
| tree | 387cf062c471070fe5e12007afa84410f364b3da |
| parent | 6088c8104cfda0c11eafd1e1f636d47106fee136 |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 79 insertions(+), 87 deletions(-)
apps/mobile/src/components/MaterialSearchField.tsx created+59| ... | ... | @@ -0,0 +1,59 @@ |
| 1 | import type { RefObject } from "react"; | |
| 2 | import { Pressable, TextInput, View } from "react-native"; | |
| 3 | ||
| 4 | import { SymbolView } from "./AppSymbol"; | |
| 5 | ||
| 6 | export function MaterialSearchField({ | |
| 7 | inputRef, | |
| 8 | accessibilityLabel, | |
| 9 | clearAccessibilityLabel, | |
| 10 | placeholder, | |
| 11 | value, | |
| 12 | onChangeText, | |
| 13 | }: { | |
| 14 | readonly inputRef: RefObject<TextInput | null>; | |
| 15 | readonly accessibilityLabel: string; | |
| 16 | readonly clearAccessibilityLabel: string; | |
| 17 | readonly placeholder: string; | |
| 18 | readonly value: string; | |
| 19 | readonly onChangeText: (value: string) => void; | |
| 20 | }) { | |
| 21 | return ( | |
| 22 | <View className="h-12 min-w-0 flex-1 flex-row items-center gap-2 rounded-full border border-input-border bg-input px-3"> | |
| 23 | <SymbolView name="magnifyingglass" size={18} tintColorClassName="accent-foreground-muted" /> | |
| 24 | <TextInput | |
| 25 | ref={inputRef} | |
| 26 | accessibilityLabel={accessibilityLabel} | |
| 27 | autoFocus | |
| 28 | autoCapitalize="none" | |
| 29 | autoCorrect={false} | |
| 30 | returnKeyType="search" | |
| 31 | placeholder={placeholder} | |
| 32 | placeholderTextColorClassName="accent-placeholder" | |
| 33 | selectionColorClassName="accent-primary/32" | |
| 34 | cursorColorClassName="accent-primary" | |
| 35 | selectionHandleColorClassName="accent-primary" | |
| 36 | className="min-w-0 flex-1 py-2 font-sans text-base text-foreground" | |
| 37 | value={value} | |
| 38 | onChangeText={onChangeText} | |
| 39 | /> | |
| 40 | {value.length > 0 ? ( | |
| 41 | <Pressable | |
| 42 | accessibilityLabel={clearAccessibilityLabel} | |
| 43 | accessibilityRole="button" | |
| 44 | hitSlop={10} | |
| 45 | onPress={() => { | |
| 46 | onChangeText(""); | |
| 47 | inputRef.current?.focus(); | |
| 48 | }} | |
| 49 | > | |
| 50 | <SymbolView | |
| 51 | name="xmark.circle.fill" | |
| 52 | size={18} | |
| 53 | tintColorClassName="accent-foreground-muted" | |
| 54 | /> | |
| 55 | </Pressable> | |
| 56 | ) : null} | |
| 57 | </View> | |
| 58 | ); | |
| 59 | } |
apps/mobile/src/features/files/MaterialFilesHeader.tsx+10-42| ... | ... | @@ -1,9 +1,9 @@ |
| 1 | 1 | import { useCallback, useEffect, useRef, useState, type ReactNode } from "react"; |
| 2 | import { BackHandler, Keyboard, Pressable, TextInput, View } from "react-native"; | |
| 2 | import { BackHandler, Keyboard, type TextInput, View } from "react-native"; | |
| 3 | 3 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 4 | 4 | |
| 5 | 5 | import { AndroidHeaderIconButton, AndroidScreenHeader } from "../../components/AndroidScreenHeader"; |
| 6 | import { SymbolView } from "../../components/AppSymbol"; | |
| 6 | import { MaterialSearchField } from "../../components/MaterialSearchField"; | |
| 7 | 7 | |
| 8 | 8 | /** Keep Files search in the same header row on compact and expanded layouts. */ |
| 9 | 9 | export function MaterialFilesHeader(props: { |
| ... | ... | @@ -74,46 +74,14 @@ export function MaterialFilesHeader(props: { |
| 74 | 74 | icon="arrow.left" |
| 75 | 75 | onPress={closeSearch} |
| 76 | 76 | /> |
| 77 | <View className="h-12 min-w-0 flex-1 flex-row items-center gap-2 rounded-full border border-input-border bg-input px-3"> | |
| 78 | <SymbolView | |
| 79 | name="magnifyingglass" | |
| 80 | size={18} | |
| 81 | tintColorClassName="accent-foreground-muted" | |
| 82 | /> | |
| 83 | <TextInput | |
| 84 | ref={searchRef} | |
| 85 | accessibilityLabel="Search files" | |
| 86 | autoFocus | |
| 87 | autoCapitalize="none" | |
| 88 | autoCorrect={false} | |
| 89 | returnKeyType="search" | |
| 90 | placeholder="Search files" | |
| 91 | placeholderTextColorClassName="accent-placeholder" | |
| 92 | selectionColorClassName="accent-primary/32" | |
| 93 | cursorColorClassName="accent-primary" | |
| 94 | selectionHandleColorClassName="accent-primary" | |
| 95 | className="min-w-0 flex-1 py-2 font-sans text-base text-foreground" | |
| 96 | value={props.searchQuery} | |
| 97 | onChangeText={onSearchQueryChange} | |
| 98 | /> | |
| 99 | {props.searchQuery.length > 0 ? ( | |
| 100 | <Pressable | |
| 101 | accessibilityLabel="Clear file search" | |
| 102 | accessibilityRole="button" | |
| 103 | hitSlop={10} | |
| 104 | onPress={() => { | |
| 105 | onSearchQueryChange(""); | |
| 106 | searchRef.current?.focus(); | |
| 107 | }} | |
| 108 | > | |
| 109 | <SymbolView | |
| 110 | name="xmark.circle.fill" | |
| 111 | size={18} | |
| 112 | tintColorClassName="accent-foreground-muted" | |
| 113 | /> | |
| 114 | </Pressable> | |
| 115 | ) : null} | |
| 116 | </View> | |
| 77 | <MaterialSearchField | |
| 78 | inputRef={searchRef} | |
| 79 | accessibilityLabel="Search files" | |
| 80 | clearAccessibilityLabel="Clear file search" | |
| 81 | placeholder="Search files" | |
| 82 | value={props.searchQuery} | |
| 83 | onChangeText={onSearchQueryChange} | |
| 84 | /> | |
| 117 | 85 | </View> |
| 118 | 86 | </View> |
| 119 | 87 | ) : null} |
apps/mobile/src/features/home/MaterialThreadListToolbar.tsx+10-45| ... | ... | @@ -1,12 +1,5 @@ |
| 1 | 1 | import { useCallback, useEffect, useRef, useState, type ComponentProps } from "react"; |
| 2 | import { | |
| 3 | BackHandler, | |
| 4 | Keyboard, | |
| 5 | Pressable, | |
| 6 | TextInput, | |
| 7 | View, | |
| 8 | type LayoutChangeEvent, | |
| 9 | } from "react-native"; | |
| 2 | import { BackHandler, Keyboard, type TextInput, View, type LayoutChangeEvent } from "react-native"; | |
| 10 | 3 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 11 | 4 | import type { MenuAction } from "@react-native-menu/menu"; |
| 12 | 5 | |
| ... | ... | @@ -15,7 +8,7 @@ import { CompactBrandTitle } from "../../components/CompactBrandTitle"; |
| 15 | 8 | import { MaterialFloatingActionButton } from "../../components/MaterialFloatingActionButton"; |
| 16 | 9 | import { AndroidAnchoredMenu } from "../../components/AndroidAnchoredMenu"; |
| 17 | 10 | import { ControlPillMenu } from "../../components/ControlPill"; |
| 18 | import { SymbolView } from "../../components/AppSymbol"; | |
| 11 | import { MaterialSearchField } from "../../components/MaterialSearchField"; | |
| 19 | 12 | import { useHardwareKeyboardCommand } from "../keyboard/hardwareKeyboardCommands"; |
| 20 | 13 | import { WorkspaceConnectionTitle } from "./WorkspaceConnectionTitle"; |
| 21 | 14 | import { useWorkspaceState } from "../../state/workspace"; |
| ... | ... | @@ -68,42 +61,14 @@ export function MaterialThreadListToolbar(props: { |
| 68 | 61 | ? "line.3.horizontal.decrease.circle.fill" |
| 69 | 62 | : "line.3.horizontal.decrease.circle"; |
| 70 | 63 | const searchField = ( |
| 71 | <View className="h-12 min-w-0 flex-1 flex-row items-center gap-2 rounded-full border border-input-border bg-input px-3"> | |
| 72 | <SymbolView name="magnifyingglass" size={18} tintColorClassName="accent-foreground-muted" /> | |
| 73 | <TextInput | |
| 74 | ref={searchRef} | |
| 75 | accessibilityLabel="Search threads" | |
| 76 | autoFocus={true} | |
| 77 | autoCapitalize="none" | |
| 78 | autoCorrect={false} | |
| 79 | returnKeyType="search" | |
| 80 | placeholder="Search" | |
| 81 | placeholderTextColorClassName="accent-placeholder" | |
| 82 | selectionColorClassName="accent-primary/32" | |
| 83 | cursorColorClassName="accent-primary" | |
| 84 | selectionHandleColorClassName="accent-primary" | |
| 85 | className="min-w-0 flex-1 py-2 font-sans text-base text-foreground" | |
| 86 | value={props.searchQuery} | |
| 87 | onChangeText={onSearchQueryChange} | |
| 88 | /> | |
| 89 | {props.searchQuery.length > 0 ? ( | |
| 90 | <Pressable | |
| 91 | accessibilityLabel="Clear search" | |
| 92 | accessibilityRole="button" | |
| 93 | hitSlop={10} | |
| 94 | onPress={() => { | |
| 95 | props.onSearchQueryChange(""); | |
| 96 | searchRef.current?.focus(); | |
| 97 | }} | |
| 98 | > | |
| 99 | <SymbolView | |
| 100 | name="xmark.circle.fill" | |
| 101 | size={18} | |
| 102 | tintColorClassName="accent-foreground-muted" | |
| 103 | /> | |
| 104 | </Pressable> | |
| 105 | ) : null} | |
| 106 | </View> | |
| 64 | <MaterialSearchField | |
| 65 | inputRef={searchRef} | |
| 66 | accessibilityLabel="Search threads" | |
| 67 | clearAccessibilityLabel="Clear search" | |
| 68 | placeholder="Search" | |
| 69 | value={props.searchQuery} | |
| 70 | onChangeText={onSearchQueryChange} | |
| 71 | /> | |
| 107 | 72 | ); |
| 108 | 73 | |
| 109 | 74 | return ( |