Skip to content

Commit f6e2afa

Browse files
committed
feat: input-accessory
1 parent 38b9651 commit f6e2afa

File tree

24 files changed

+2213
-2
lines changed

24 files changed

+2213
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- [@nativescript/google-signin](packages/google-signin/README.md)
2323
- [@nativescript/haptics](packages/haptics/README.md)
2424
- [@nativescript/imagepicker](packages/imagepicker/README.md)
25+
- [@nativescript/input-accessory](packages/input-accessory/README.md)
2526
- [@nativescript/ios-security](packages/ios-security/README.md)
2627
- [@nativescript/iqkeyboardmanager](packages/iqkeyboardmanager/README.md)
2728
- [@nativescript/keyboard-toolbar](packages/keyboard-toolbar/README.md)

apps/demo/src/main-page.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<Button text="google-signin" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
2929
<Button text="haptics" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
3030
<Button text="imagepicker" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
31+
<Button text="input-accessory" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
3132
<Button text="ios-security" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
3233
<Button text="iqkeyboardmanager" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
3334
<Button text="keyboard-toolbar" tap="{{ viewDemo }}" class="bg-blue-500 rounded-full text-white p-4 mb-4"/>
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { EventData, Page, ScrollView, TextView, View } from '@nativescript/core';
2+
import { DemoSharedInputAccessory } from '@demo/shared';
3+
import { InputAccessoryManager } from '@nativescript/input-accessory';
4+
5+
export function navigatingTo(args: EventData) {
6+
const page = <Page>args.object;
7+
page.bindingContext = new DemoModel(page);
8+
}
9+
10+
export class DemoModel extends DemoSharedInputAccessory {
11+
private accessoryManager: InputAccessoryManager | null = null;
12+
private textView: TextView | null = null;
13+
private page: Page;
14+
private isAccessorySetup = false;
15+
private inputText = '';
16+
17+
constructor(page: Page) {
18+
super();
19+
this.page = page;
20+
21+
this.set('inputText', '');
22+
this.set('messages', [
23+
'Welcome to @nativescript/input-accessory 👋',
24+
'Tap in the field below to attach the input to the keyboard.',
25+
'Type and press Send to append messages while tracking keyboard transitions.',
26+
'Random thought: pineapple absolutely belongs on pizza. 🍍',
27+
'Just testing long text wrapping in this message bubble for layout stability.',
28+
'Keyboard animation looks smooth on both platforms so far.',
29+
'Reminder: drink water and stretch every hour 💧',
30+
'If this were a real chat, someone would definitely send a GIF here.',
31+
'This plugin is perfect for support chats and comment threads.',
32+
'Interactive dismiss feels super natural when dragging the list.',
33+
'Quick check-in: are we shipping this in the next release?',
34+
'Edge case test: message count should keep scrolling correctly.',
35+
'Last seeded message: ready for your own typing test 🚀',
36+
]);
37+
38+
this.page.on(Page.loadedEvent, () => {
39+
setTimeout(() => this.setupAccessory(), 100);
40+
});
41+
42+
this.page.on(Page.navigatingFromEvent, () => {
43+
this.accessoryManager?.cleanup();
44+
this.accessoryManager = null;
45+
this.isAccessorySetup = false;
46+
});
47+
}
48+
49+
onMessageInputLoaded(args: EventData) {
50+
this.textView = args.object as TextView;
51+
this.setupAccessory();
52+
}
53+
54+
onMessageTextChange() {
55+
this.inputText = this.textView?.text || '';
56+
this.accessoryManager?.updateAccessoryHeight();
57+
}
58+
59+
onMessageReturnPress() {
60+
this.sendMessage();
61+
}
62+
63+
sendMessage() {
64+
const inputText = (this.inputText || '').trim();
65+
if (!inputText) {
66+
return;
67+
}
68+
69+
this.appendMessage(`You: ${inputText}`);
70+
this.set('inputText', '');
71+
this.inputText = '';
72+
}
73+
74+
dismissKeyboard() {
75+
this.accessoryManager?.dismissKeyboard();
76+
}
77+
78+
private setupAccessory() {
79+
if (this.isAccessorySetup || !this.textView) {
80+
return;
81+
}
82+
83+
const scrollView = this.page.getViewById<ScrollView>('messagesScrollView');
84+
const inputContainer = this.page.getViewById<View>('inputContainer');
85+
86+
if (!scrollView || !inputContainer) {
87+
setTimeout(() => this.setupAccessory(), 100);
88+
return;
89+
}
90+
91+
this.accessoryManager = new InputAccessoryManager();
92+
this.accessoryManager.setup({
93+
page: this.page,
94+
scrollView,
95+
inputContainer,
96+
textView: this.textView,
97+
});
98+
99+
this.isAccessorySetup = true;
100+
this.scrollToBottom();
101+
}
102+
103+
private appendMessage(message: string) {
104+
const currentMessages = (this.get('messages') as string[]) || [];
105+
this.set('messages', [...currentMessages, message]);
106+
this.scrollToBottom();
107+
}
108+
109+
private scrollToBottom() {
110+
setTimeout(() => {
111+
this.accessoryManager?.updateAccessoryHeight();
112+
this.accessoryManager?.relayoutScrollViewContent();
113+
const scrollView = this.page.getViewById<ScrollView>('messagesScrollView');
114+
scrollView?.scrollToVerticalOffset(scrollView.scrollableHeight, true);
115+
}, 100);
116+
}
117+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
2+
<Page.actionBar>
3+
<ActionBar title="input-accessory" icon="" class="action-bar">
4+
</ActionBar>
5+
</Page.actionBar>
6+
<GridLayout rows="auto,*, auto">
7+
<StackLayout class="p-4" tap="{{ dismissKeyboard }}">
8+
<Label text="Keyboard attached input bar demo" class="h2 mb-2" textWrap="true" />
9+
<Label text="Try dragging the list while the keyboard is visible for interactive dismiss." class="mb-4 leading-[3]" textWrap="true" />
10+
</StackLayout>
11+
<GridLayout row="1" class="mx-4" iosOverflowSafeArea="false">
12+
<ScrollView id="messagesScrollView">
13+
<StackLayout tap="{{ dismissKeyboard }}" class="p-2 mb-14 bg-gray-200 rounded-b-lg">
14+
15+
<Repeater items="{{ messages }}">
16+
<Repeater.itemTemplate>
17+
<Label text="{{ $value }}" class="p-2 mb-2 text-sm leading-[3]" textWrap="true" />
18+
</Repeater.itemTemplate>
19+
</Repeater>
20+
</StackLayout>
21+
</ScrollView>
22+
23+
</GridLayout>
24+
25+
<GridLayout id="inputContainer" row="2" columns="*, auto" class="px-5 pb-2">
26+
<TextView id="messageInput" col="0" text="{{ inputText }}" hint="Type a message..." textChange="{{ onMessageTextChange }}" loaded="{{ onMessageInputLoaded }}" returnKeyType="send" class="pl-4 py-4 rounded-full bg-white/30 leading-[2]" />
27+
<Button col="1" text="Send" tap="{{ sendMessage }}" class="text-blue-500 ml-2" />
28+
</GridLayout>
29+
</GridLayout>
30+
</Page>

apps/demo/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"@nativescript/google-mobile-ads": ["../../packages/google-mobile-ads/index.d.ts"],
5151
"@nativescript/google-mobile-ads/angular": ["../../packages/google-mobile-ads/angular/index.ts"],
5252
"@nativescript/google-maps-utils": ["../../packages/google-maps-utils/index.d.ts"],
53-
"@nativescript/keyboard-toolbar": ["../../packages/keyboard-toolbar/index.d.ts"]
53+
"@nativescript/keyboard-toolbar": ["../../packages/keyboard-toolbar/index.d.ts"],
54+
"@nativescript/input-accessory": ["../../packages/input-accessory/index.d.ts"]
5455
}
5556
}
5657
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"extends": ["../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*", "node_modules/**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["*.ts", "*.tsx"],
11+
"rules": {}
12+
},
13+
{
14+
"files": ["*.js", "*.jsx"],
15+
"rules": {}
16+
}
17+
]
18+
}

0 commit comments

Comments
 (0)