fix: make mobile type picker searchable
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
toggleMobileNoteTag,
|
||||
type MobileNotePropertyPatch,
|
||||
} from './mobileNoteProperties'
|
||||
import { filterMobilePropertyComboOptions, resolveMobilePropertyComboValue } from './mobilePropertyCombo'
|
||||
import { mobilePropertyDisplayValue, type MobilePropertyPickerKey } from './mobilePropertyPicker'
|
||||
import { styles } from './styles'
|
||||
import { colors } from './theme'
|
||||
@@ -110,7 +111,12 @@ function EditableTextProperty({
|
||||
variant?: 'chips' | 'combo'
|
||||
}) {
|
||||
const [draft, setDraft] = useState(value)
|
||||
const commitDraft = () => commitTextValue({ current: value, next: draft, onCommit })
|
||||
const commitDraft = () => {
|
||||
const next = variant === 'combo'
|
||||
? resolveMobilePropertyComboValue({ options: suggestions, value: draft })
|
||||
: draft
|
||||
commitTextValue({ current: value, next, onCommit, onSettled: setDraft })
|
||||
}
|
||||
|
||||
return (
|
||||
<PropertyPickerSection
|
||||
@@ -123,6 +129,7 @@ function EditableTextProperty({
|
||||
<View style={styles.propertyPickerOptions}>
|
||||
<TextInput
|
||||
autoCapitalize="sentences"
|
||||
autoFocus={variant === 'combo'}
|
||||
editable={!disabled}
|
||||
keyboardType="default"
|
||||
onBlur={commitDraft}
|
||||
@@ -131,6 +138,7 @@ function EditableTextProperty({
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={colors.mutedText}
|
||||
returnKeyType="done"
|
||||
selectTextOnFocus={variant === 'combo'}
|
||||
style={styles.propertyTextInput}
|
||||
value={draft}
|
||||
/>
|
||||
@@ -138,6 +146,7 @@ function EditableTextProperty({
|
||||
? (
|
||||
<PropertyComboOptions
|
||||
disabled={disabled}
|
||||
query={draft}
|
||||
suggestions={suggestions}
|
||||
value={value}
|
||||
onSelect={(selected) => {
|
||||
@@ -348,17 +357,21 @@ function PropertyChipOptions({ children }: { children: ReactNode }) {
|
||||
function PropertyComboOptions({
|
||||
disabled,
|
||||
onSelect,
|
||||
query,
|
||||
suggestions,
|
||||
value,
|
||||
}: {
|
||||
disabled: boolean
|
||||
onSelect: (option: string) => void
|
||||
query: string
|
||||
suggestions: readonly string[]
|
||||
value: string
|
||||
}) {
|
||||
const options = filterMobilePropertyComboOptions({ options: suggestions, query })
|
||||
|
||||
return (
|
||||
<View style={styles.propertyComboBox}>
|
||||
{suggestions.map((option) => {
|
||||
{options.map((option) => {
|
||||
const isSelected = isMobileNotePropertySelected({ current: value, option })
|
||||
|
||||
return (
|
||||
@@ -379,6 +392,7 @@ function PropertyComboOptions({
|
||||
</Pressable>
|
||||
)
|
||||
})}
|
||||
{options.length === 0 ? <Text style={styles.propertyComboEmpty}>No matching types.</Text> : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -445,12 +459,15 @@ function commitTextValue({
|
||||
current,
|
||||
next,
|
||||
onCommit,
|
||||
onSettled,
|
||||
}: {
|
||||
current: string
|
||||
next: string
|
||||
onCommit: (value: string) => void
|
||||
onSettled?: (value: string) => void
|
||||
}) {
|
||||
const normalized = next.trim()
|
||||
onSettled?.(normalized)
|
||||
if (normalized !== current) {
|
||||
onCommit(normalized)
|
||||
}
|
||||
|
||||
16
apps/mobile/src/mobilePropertyCombo.test.ts
Normal file
16
apps/mobile/src/mobilePropertyCombo.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { filterMobilePropertyComboOptions, resolveMobilePropertyComboValue } from './mobilePropertyCombo'
|
||||
|
||||
describe('mobile property combo', () => {
|
||||
const options = ['Note', 'Essay', 'Project', 'Evergreen']
|
||||
|
||||
it('filters options from typed text', () => {
|
||||
expect(filterMobilePropertyComboOptions({ options, query: 'ess' })).toEqual(['Essay'])
|
||||
expect(filterMobilePropertyComboOptions({ options, query: 'e' })).toEqual(['Note', 'Essay', 'Project', 'Evergreen'])
|
||||
})
|
||||
|
||||
it('normalizes exact typed selections to the canonical option label', () => {
|
||||
expect(resolveMobilePropertyComboValue({ options, value: ' essay ' })).toBe('Essay')
|
||||
expect(resolveMobilePropertyComboValue({ options, value: 'Custom' })).toBe('Custom')
|
||||
})
|
||||
})
|
||||
30
apps/mobile/src/mobilePropertyCombo.ts
Normal file
30
apps/mobile/src/mobilePropertyCombo.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export function filterMobilePropertyComboOptions({
|
||||
options,
|
||||
query,
|
||||
}: {
|
||||
options: readonly string[]
|
||||
query: string
|
||||
}) {
|
||||
const normalizedQuery = normalizeComboText(query)
|
||||
if (!normalizedQuery) {
|
||||
return options
|
||||
}
|
||||
|
||||
return options.filter((option) => normalizeComboText(option).includes(normalizedQuery))
|
||||
}
|
||||
|
||||
export function resolveMobilePropertyComboValue({
|
||||
options,
|
||||
value,
|
||||
}: {
|
||||
options: readonly string[]
|
||||
value: string
|
||||
}) {
|
||||
const trimmed = value.trim()
|
||||
const exactOption = options.find((option) => normalizeComboText(option) === normalizeComboText(trimmed))
|
||||
return exactOption ?? trimmed
|
||||
}
|
||||
|
||||
function normalizeComboText(value: string) {
|
||||
return value.trim().toLowerCase()
|
||||
}
|
||||
@@ -27,4 +27,11 @@ export const propertyComboStyles = StyleSheet.create({
|
||||
propertyComboOptionTextSelected: {
|
||||
color: colors.primary,
|
||||
},
|
||||
propertyComboEmpty: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm,
|
||||
color: colors.mutedText,
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -515,3 +515,12 @@ Continue Phase 4 as a quality remediation pass before new feature work:
|
||||
- `pnpm --filter @tolaria/mobile typecheck` passed.
|
||||
- `pnpm --filter @tolaria/mobile test -- src/mobileEditorMessages.test.ts src/mobileWikilinkAutocomplete.test.ts` passed; the mobile test runner executed 61 files / 202 tests.
|
||||
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export-wikilink-autocomplete` passed.
|
||||
|
||||
## 2026-05-14 Type Combobox Follow-Up
|
||||
|
||||
- Reworked the mobile Type picker into a searchable combobox: opening Type focuses the text field, typing filters available type options, and exact typed matches normalize to the canonical type label.
|
||||
- Added empty-state copy for unmatched type searches.
|
||||
- Added pure mobile combobox tests for option filtering and canonical exact-match resolution.
|
||||
- `pnpm --filter @tolaria/mobile typecheck` passed.
|
||||
- `pnpm --filter @tolaria/mobile test -- src/mobilePropertyCombo.test.ts src/mobileNoteProperties.test.ts` passed; the mobile test runner executed 62 files / 204 tests.
|
||||
- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export-type-combobox` passed.
|
||||
|
||||
Reference in New Issue
Block a user