From 37a86ac04cd25244d77697b2b74b6ded4e8383b4 Mon Sep 17 00:00:00 2001 From: Tyler Coffman Date: Thu, 12 Feb 2026 11:52:59 -0800 Subject: [PATCH 1/3] Fix style prop of KeyboardAvoidingLegendList on iOS --- src/integrations/keyboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrations/keyboard.tsx b/src/integrations/keyboard.tsx index ab0be185..0ee55ad7 100644 --- a/src/integrations/keyboard.tsx +++ b/src/integrations/keyboard.tsx @@ -222,7 +222,7 @@ export const KeyboardAvoidingLegendList = (forwardRef as TypedForwardRef)(functi }), [styleProp, keyboardInset], ) - : undefined; + : styleProp; return ( Date: Fri, 13 Feb 2026 09:00:49 -0800 Subject: [PATCH 2/3] Fix .prettierrc.json to match code in repo. --- .prettierrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.prettierrc.json b/.prettierrc.json index 1c533245..1f3237ec 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,5 @@ { "tabWidth": 4, "printWidth": 120, - "singleQuote": true + "singleQuote": false } From fb18f4fed2833bbb9d409e06b732e889e4c79ca7 Mon Sep 17 00:00:00 2001 From: Tyler Coffman Date: Fri, 13 Feb 2026 09:00:49 -0800 Subject: [PATCH 3/3] Chat keyboard overlap example. This is an example of how to make it so that messages in your chat will be visible behind the input box when you scroll up in the chat. --- example/app/(tabs)/index.tsx | 4 + example/app/chat-keyboard-overlap/index.tsx | 252 ++++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 example/app/chat-keyboard-overlap/index.tsx diff --git a/example/app/(tabs)/index.tsx b/example/app/(tabs)/index.tsx index 9a97f75d..f9025150 100644 --- a/example/app/(tabs)/index.tsx +++ b/example/app/(tabs)/index.tsx @@ -80,6 +80,10 @@ const data: ListElement[] = [ title: "Chat keyboard", url: "/chat-keyboard", }, + { + title: "Chat keyboard overlap", + url: "/chat-keyboard-overlap", + }, { title: "Movies FlashList", url: "/movies-flashlist", diff --git a/example/app/chat-keyboard-overlap/index.tsx b/example/app/chat-keyboard-overlap/index.tsx new file mode 100644 index 00000000..5813743e --- /dev/null +++ b/example/app/chat-keyboard-overlap/index.tsx @@ -0,0 +1,252 @@ +import { type PropsWithChildren, useState } from "react"; +import { BlurView } from "expo-blur"; +import { Button, Platform, StyleSheet, Text, TextInput, View } from "react-native"; +import { KeyboardGestureArea, KeyboardProvider, KeyboardStickyView } from "react-native-keyboard-controller"; +import { SafeAreaView, useSafeAreaInsets } from "react-native-safe-area-context"; + +import { KeyboardAvoidingLegendList } from "@legendapp/list/keyboard"; + +type Message = { + id: string; + text: string; + sender: "user" | "bot"; + timeStamp: number; +}; + +let idCounter = 0; +const MS_PER_SECOND = 1000; + +const defaultChatMessages: Message[] = [ + { + id: String(idCounter++), + sender: "user", + text: "Hi, I have a question about your product", + timeStamp: Date.now() - MS_PER_SECOND * 5, + }, + { + id: String(idCounter++), + sender: "bot", + text: "Hello there! How can I assist you today?", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "user", + text: "I'm looking for information about pricing plans", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "We offer several pricing tiers based on your needs", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "Our basic plan starts at $9.99 per month", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "user", + text: "Do you offer any discounts for annual billing?", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "Yes! You can save 20% with our annual billing option", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "user", + text: "That sounds great. What features are included?", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "The basic plan includes all core features plus 10GB storage", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "Premium plans include priority support and additional tools", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "user", + text: "I think the basic plan would work for my needs", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "Perfect! I can help you get set up with that", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "user", + text: "Thanks for your help so far", + timeStamp: Date.now() - MS_PER_SECOND * 4, + }, + { + id: String(idCounter++), + sender: "bot", + text: "You're welcome! Is there anything else I can assist with today?", + timeStamp: Date.now() - MS_PER_SECOND * 3, + }, +]; + +function ChatMessage({ item }: { item: Message }) { + return ( + <> + + {item.text} + + + {new Date(item.timeStamp).toLocaleTimeString()} + + + ); +} + +const ChatKeyboard = () => { + const [messages, setMessages] = useState(defaultChatMessages); + const [inputText, setInputText] = useState(""); + const insets = useSafeAreaInsets(); + + const sendMessage = () => { + const text = inputText || "Empty message"; + if (text.trim()) { + setMessages((messagesNew) => [ + ...messagesNew, + { id: String(idCounter++), sender: "user", text: text, timeStamp: Date.now() }, + ]); + setInputText(""); + setTimeout(() => { + setMessages((messagesNew) => [ + ...messagesNew, + { + id: String(idCounter++), + sender: "bot", + text: `Answer: ${text.toUpperCase()}`, + timeStamp: Date.now(), + }, + ]); + }, 300); + } + }; + + return ( + + + item.id} + maintainScrollAtEnd + maintainVisibleContentPosition + renderItem={ChatMessage} + safeAreaInsetBottom={insets.bottom} + style={styles.list} + /> + + + + +