mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-31 21:52:54 +00:00
* fix(app): preserve IME composition in form inputs Form model publication was replaying changing default text into the native or DOM editing surface. Keep the editing surface authoritative, defer web publication until composition commits, and reserve resetKey changes for explicit text replacement. * refactor(app): centralize text editing authority Route every app text field, including the composer and plugin runtime, through one platform-owned editing primitive. Its type and runtime reject controlled value replay, its command handle owns programmatic replacement, and lint prevents raw React Native TextInput imports outside the boundary. * fix(app): restore composer placeholder styling
65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
import React, { forwardRef, useCallback, useEffect, useRef } from "react";
|
|
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
|
import { useIsCompactFormFactor } from "@/constants/layout";
|
|
import { isNative } from "@/constants/platform";
|
|
import { createControlGeometry } from "@/components/ui/control-geometry";
|
|
import {
|
|
EditingTextInput,
|
|
type EditingTextInputHandle,
|
|
type EditingTextInputProps,
|
|
} from "@/components/ui/text-input";
|
|
|
|
export type AdaptiveTextInputProps = EditingTextInputProps & {
|
|
initialValue?: string;
|
|
resetKey?: string | number;
|
|
};
|
|
|
|
const styles = StyleSheet.create((theme) => ({
|
|
outline: {
|
|
...createControlGeometry(theme).controlFocusRingColor,
|
|
},
|
|
text: {
|
|
color: theme.colors.foreground,
|
|
},
|
|
}));
|
|
|
|
const ThemedTextInput = withUnistyles(EditingTextInput, (theme) => ({
|
|
placeholderTextColor: theme.colors.foregroundMuted,
|
|
}));
|
|
|
|
export const AdaptiveTextInput = forwardRef<EditingTextInputHandle, AdaptiveTextInputProps>(
|
|
function AdaptiveTextInputInner(props, ref) {
|
|
const isMobile = useIsCompactFormFactor();
|
|
const { initialValue, resetKey, style, onChangeText, ...inputProps } = props;
|
|
const inputRef = useRef<EditingTextInputHandle | null>(null);
|
|
const replacementTextRef = useRef(initialValue ?? "");
|
|
const previousResetKeyRef = useRef(resetKey);
|
|
replacementTextRef.current = initialValue ?? "";
|
|
const setInputRef = useCallback(
|
|
(handle: EditingTextInputHandle | null) => {
|
|
inputRef.current = handle;
|
|
if (typeof ref === "function") ref(handle);
|
|
else if (ref) ref.current = handle;
|
|
},
|
|
[ref],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (resetKey === previousResetKeyRef.current) return;
|
|
previousResetKeyRef.current = resetKey;
|
|
inputRef.current?.replaceText(replacementTextRef.current);
|
|
}, [resetKey]);
|
|
|
|
return (
|
|
<ThemedTextInput
|
|
{...inputProps}
|
|
ref={setInputRef}
|
|
initialValue={initialValue}
|
|
onChangeText={onChangeText}
|
|
style={[styles.outline, style, styles.text]}
|
|
variant={isMobile && isNative ? "bottom-sheet" : "default"}
|
|
/>
|
|
);
|
|
},
|
|
);
|