@rpg-engine/long-bow 0.7.19 → 0.7.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Chat/Chat.d.ts +1 -0
- package/dist/components/ChatRevamp/ChatRevamp.d.ts +2 -0
- package/dist/long-bow.cjs.development.js +112 -41
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +112 -41
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Chat/Chat.tsx +44 -38
- package/src/components/ChatRevamp/ChatRevamp.tsx +111 -21
package/package.json
CHANGED
|
@@ -28,6 +28,7 @@ export interface IChatProps {
|
|
|
28
28
|
opacity?: number;
|
|
29
29
|
sendMessage: boolean;
|
|
30
30
|
styles?: IStyles;
|
|
31
|
+
isExpanded: boolean;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export const Chat: React.FC<IChatProps> = ({
|
|
@@ -43,15 +44,17 @@ export const Chat: React.FC<IChatProps> = ({
|
|
|
43
44
|
width: '100%',
|
|
44
45
|
height: '100%',
|
|
45
46
|
},
|
|
47
|
+
isExpanded,
|
|
46
48
|
}) => {
|
|
47
49
|
const [message, setMessage] = useState('');
|
|
48
50
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
49
51
|
|
|
50
52
|
useEffect(() => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
if (isExpanded) {
|
|
54
|
+
scrollChatToBottom();
|
|
55
|
+
}
|
|
53
56
|
inputRef.current?.focus();
|
|
54
|
-
}, [chatMessages]);
|
|
57
|
+
}, [chatMessages, isExpanded]);
|
|
55
58
|
|
|
56
59
|
const scrollChatToBottom = () => {
|
|
57
60
|
const scrollingElement = document.querySelector('.chat-body');
|
|
@@ -81,39 +84,41 @@ export const Chat: React.FC<IChatProps> = ({
|
|
|
81
84
|
<ChatContainer
|
|
82
85
|
width={styles.width || 'auto'}
|
|
83
86
|
height={styles.height || 'auto'}
|
|
87
|
+
isExpanded={isExpanded}
|
|
84
88
|
>
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
89
|
+
{isExpanded && (
|
|
90
|
+
<ErrorBoundary fallback={<p>Oops! Your chat has crashed.</p>}>
|
|
91
|
+
<CloseButton onClick={onCloseButton}>
|
|
92
|
+
<FaTimes />
|
|
93
|
+
</CloseButton>
|
|
94
|
+
<MessagesContainer className="chat-body">
|
|
95
|
+
{chatMessages.map((chatMessage, index) => (
|
|
96
|
+
<Message
|
|
97
|
+
color={styles.textColor || uiColors.yellow}
|
|
98
|
+
key={`${chatMessage._id}_${index}`}
|
|
99
|
+
>
|
|
100
|
+
{formatMessage(chatMessage)}
|
|
101
|
+
</Message>
|
|
102
|
+
))}
|
|
103
|
+
</MessagesContainer>
|
|
104
|
+
</ErrorBoundary>
|
|
105
|
+
)}
|
|
106
|
+
<Form onSubmit={handleSubmit} isExpanded={isExpanded}>
|
|
107
|
+
<TextField
|
|
108
|
+
ref={inputRef}
|
|
109
|
+
value={message}
|
|
110
|
+
onChange={e => setMessage(e.target.value)}
|
|
111
|
+
type="text"
|
|
112
|
+
autoComplete="off"
|
|
113
|
+
onFocus={onFocus}
|
|
114
|
+
onBlur={onBlur}
|
|
115
|
+
onPointerDown={onFocus}
|
|
116
|
+
placeholder="Type your message..."
|
|
117
|
+
/>
|
|
118
|
+
<SendButton type="submit" disabled={!message.trim()}>
|
|
119
|
+
▶
|
|
120
|
+
</SendButton>
|
|
121
|
+
</Form>
|
|
117
122
|
</ChatContainer>
|
|
118
123
|
);
|
|
119
124
|
};
|
|
@@ -127,9 +132,9 @@ interface IMessageProps {
|
|
|
127
132
|
color: string;
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
const ChatContainer = styled.div<IContainerProps>`
|
|
135
|
+
const ChatContainer = styled.div<IContainerProps & { isExpanded: boolean }>`
|
|
131
136
|
width: ${props => props.width};
|
|
132
|
-
height: ${props => props.height};
|
|
137
|
+
height: ${props => (props.isExpanded ? props.height : 'auto')};
|
|
133
138
|
background-color: rgba(30, 30, 30, 0.3);
|
|
134
139
|
display: flex;
|
|
135
140
|
flex-direction: column;
|
|
@@ -183,10 +188,11 @@ const Message = styled.div<IMessageProps>`
|
|
|
183
188
|
word-break: break-word;
|
|
184
189
|
`;
|
|
185
190
|
|
|
186
|
-
const Form = styled.form
|
|
191
|
+
const Form = styled.form<{ isExpanded: boolean }>`
|
|
187
192
|
display: flex;
|
|
188
193
|
padding: 8px;
|
|
189
194
|
background-color: rgba(42, 42, 42, 0.4);
|
|
195
|
+
border-radius: ${props => (props.isExpanded ? '0' : '8px')};
|
|
190
196
|
`;
|
|
191
197
|
|
|
192
198
|
const TextField = styled.input`
|
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
IPrivateChatMessage,
|
|
5
5
|
ITradeChatMessage,
|
|
6
6
|
} from '@rpg-engine/shared';
|
|
7
|
-
import React, { useState } from 'react';
|
|
7
|
+
import React, { useEffect, useState } from 'react';
|
|
8
|
+
import { IoMdContract, IoMdExpand } from 'react-icons/io';
|
|
8
9
|
import { RxCross2, RxMagnifyingGlass } from 'react-icons/rx';
|
|
9
|
-
import styled from 'styled-components';
|
|
10
|
+
import styled, { css } from 'styled-components';
|
|
10
11
|
import { uiColors } from '../../constants/uiColors';
|
|
11
12
|
import { uiFonts } from '../../constants/uiFonts';
|
|
12
13
|
import { Chat, IStyles } from '../Chat/Chat';
|
|
@@ -42,6 +43,8 @@ export interface IChatRevampProps {
|
|
|
42
43
|
searchCharacterUI: boolean;
|
|
43
44
|
hideSearchCharacterUI: () => void;
|
|
44
45
|
showSearchCharacterUI: () => void;
|
|
46
|
+
minimizedByDefault?: boolean;
|
|
47
|
+
autoCloseOnSend?: boolean;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
@@ -67,14 +70,32 @@ export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
|
67
70
|
searchCharacterUI,
|
|
68
71
|
hideSearchCharacterUI,
|
|
69
72
|
showSearchCharacterUI,
|
|
73
|
+
minimizedByDefault = false,
|
|
74
|
+
autoCloseOnSend = false,
|
|
70
75
|
}) => {
|
|
71
76
|
const [showRecentChats, setShowRecentChats] = useState(false);
|
|
77
|
+
const [isExpanded, setIsExpanded] = useState(!minimizedByDefault);
|
|
72
78
|
|
|
73
79
|
const isPrivate = activeTab === 'private';
|
|
74
80
|
const isTrade = activeTab === 'trade';
|
|
75
81
|
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (isPrivate) {
|
|
84
|
+
setIsExpanded(true);
|
|
85
|
+
}
|
|
86
|
+
}, [isPrivate]);
|
|
87
|
+
|
|
88
|
+
const toggleExpand = () => setIsExpanded(prev => !prev);
|
|
89
|
+
|
|
76
90
|
const toggleRecentChats = () => setShowRecentChats(prev => !prev);
|
|
77
91
|
|
|
92
|
+
const handleTabChange = (tabId: string) => {
|
|
93
|
+
if (tabId === 'private') {
|
|
94
|
+
setIsExpanded(true);
|
|
95
|
+
}
|
|
96
|
+
onChangeTab(tabId);
|
|
97
|
+
};
|
|
98
|
+
|
|
78
99
|
const handlePreviousChatCharacterClick = (
|
|
79
100
|
character: PrivateChatCharacter
|
|
80
101
|
) => {
|
|
@@ -88,7 +109,7 @@ export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
|
88
109
|
<Tab
|
|
89
110
|
key={`${tab.label}_${index}`}
|
|
90
111
|
active={tab.id === activeTab}
|
|
91
|
-
onPointerDown={() =>
|
|
112
|
+
onPointerDown={() => handleTabChange(tab.id)}
|
|
92
113
|
>
|
|
93
114
|
{tab.label}
|
|
94
115
|
</Tab>
|
|
@@ -158,43 +179,85 @@ export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
|
158
179
|
return (
|
|
159
180
|
<Chat
|
|
160
181
|
chatMessages={chatMessages}
|
|
161
|
-
onSendChatMessage={
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
onSendChatMessage={() => {
|
|
183
|
+
if (autoCloseOnSend) {
|
|
184
|
+
onCloseButton();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isPrivate) {
|
|
188
|
+
return onSendPrivateChatMessage;
|
|
189
|
+
} else if (isTrade) {
|
|
190
|
+
return onSendTradeMessage;
|
|
191
|
+
} else {
|
|
192
|
+
return onSendGlobalChatMessage;
|
|
193
|
+
}
|
|
194
|
+
}}
|
|
168
195
|
sendMessage={true}
|
|
169
196
|
onCloseButton={onCloseButton}
|
|
170
197
|
styles={styles}
|
|
171
198
|
onFocus={onFocus}
|
|
172
199
|
onBlur={onBlur}
|
|
200
|
+
isExpanded={isExpanded}
|
|
173
201
|
/>
|
|
174
202
|
);
|
|
175
203
|
};
|
|
176
204
|
|
|
177
205
|
return (
|
|
178
|
-
|
|
179
|
-
{
|
|
206
|
+
<ChatRevampContainer>
|
|
207
|
+
<TopBar isExpanded={isExpanded}>
|
|
208
|
+
{isExpanded && renderTabs()}
|
|
209
|
+
<ExpandButton onClick={toggleExpand} isExpanded={isExpanded}>
|
|
210
|
+
{isExpanded ? <IoMdContract size={20} /> : <IoMdExpand size={20} />}
|
|
211
|
+
</ExpandButton>
|
|
212
|
+
</TopBar>
|
|
180
213
|
<PrivateChatContainer
|
|
181
214
|
width={styles?.width || '80%'}
|
|
182
215
|
height={styles?.height || 'auto'}
|
|
216
|
+
isExpanded={isExpanded}
|
|
183
217
|
>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
218
|
+
{isExpanded && (
|
|
219
|
+
<RecentChatTabContainer
|
|
220
|
+
isPrivate={isPrivate}
|
|
221
|
+
isOpen={showRecentChats}
|
|
222
|
+
>
|
|
223
|
+
{renderRecentChatTopBar()}
|
|
224
|
+
{renderRecentChatList()}
|
|
225
|
+
</RecentChatTabContainer>
|
|
226
|
+
)}
|
|
188
227
|
<ChatWrapper>{renderChatContent()}</ChatWrapper>
|
|
189
228
|
</PrivateChatContainer>
|
|
190
|
-
|
|
229
|
+
</ChatRevampContainer>
|
|
191
230
|
);
|
|
192
231
|
};
|
|
193
232
|
|
|
194
|
-
const
|
|
233
|
+
const ChatRevampContainer = styled.div`
|
|
234
|
+
display: flex;
|
|
235
|
+
flex-direction: column;
|
|
195
236
|
width: 100%;
|
|
237
|
+
position: relative;
|
|
238
|
+
`;
|
|
239
|
+
|
|
240
|
+
interface ITopBarProps {
|
|
241
|
+
isExpanded: boolean;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const TopBar = styled.div<ITopBarProps>`
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
justify-content: flex-start;
|
|
248
|
+
|
|
249
|
+
${({ isExpanded }) =>
|
|
250
|
+
!isExpanded &&
|
|
251
|
+
css`
|
|
252
|
+
min-height: 32px; // Ensure there's always space for the expand button when its collapsed
|
|
253
|
+
`}
|
|
254
|
+
`;
|
|
255
|
+
|
|
256
|
+
const TabContainer = styled.div`
|
|
196
257
|
display: flex;
|
|
197
258
|
gap: 10px;
|
|
259
|
+
align-items: center;
|
|
260
|
+
flex-grow: 1;
|
|
198
261
|
`;
|
|
199
262
|
|
|
200
263
|
const Tab = styled.button<{ active: boolean }>`
|
|
@@ -216,14 +279,40 @@ const Tab = styled.button<{ active: boolean }>`
|
|
|
216
279
|
color: ${props => (props.active ? 'white' : uiColors.darkGray)};
|
|
217
280
|
`;
|
|
218
281
|
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
282
|
+
const ExpandButton = styled.button<{ isExpanded: boolean }>`
|
|
283
|
+
position: absolute;
|
|
284
|
+
top: 0;
|
|
285
|
+
${({ isExpanded }) => (isExpanded ? 'right: 0' : 'left: 0')};
|
|
286
|
+
width: 30px;
|
|
287
|
+
height: 30px;
|
|
288
|
+
background-color: ${uiColors.orange};
|
|
289
|
+
color: white;
|
|
290
|
+
border: none;
|
|
291
|
+
border-radius: 50%;
|
|
292
|
+
display: flex;
|
|
293
|
+
justify-content: center;
|
|
294
|
+
align-items: center;
|
|
295
|
+
cursor: pointer;
|
|
296
|
+
transition: all 0.3s ease;
|
|
297
|
+
z-index: 10;
|
|
222
298
|
|
|
299
|
+
&:hover {
|
|
300
|
+
background-color: ${uiColors.orange};
|
|
301
|
+
}
|
|
302
|
+
`;
|
|
303
|
+
|
|
304
|
+
const PrivateChatContainer = styled.div<{
|
|
305
|
+
width: string;
|
|
306
|
+
height: string;
|
|
307
|
+
isExpanded: boolean;
|
|
308
|
+
}>`
|
|
309
|
+
width: ${({ width }) => width};
|
|
310
|
+
height: ${({ height, isExpanded }) => (isExpanded ? height : 'auto')};
|
|
223
311
|
padding: 10px;
|
|
224
312
|
background-color: rgba(0, 0, 0, 0.2);
|
|
225
313
|
display: flex;
|
|
226
314
|
gap: 10px;
|
|
315
|
+
flex-wrap: wrap;
|
|
227
316
|
`;
|
|
228
317
|
|
|
229
318
|
const RecentChatTabContainer = styled.div<{
|
|
@@ -394,4 +483,5 @@ const ChatWrapper = styled.div`
|
|
|
394
483
|
overflow: hidden;
|
|
395
484
|
display: flex;
|
|
396
485
|
flex-direction: column;
|
|
486
|
+
position: relative;
|
|
397
487
|
`;
|