@rpg-engine/long-bow 0.7.18 → 0.7.21
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 +1 -0
- package/dist/long-bow.cjs.development.js +99 -40
- 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 +99 -40
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Chat/Chat.tsx +47 -38
- package/src/components/ChatRevamp/ChatRevamp.tsx +96 -14
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
4
4
|
import { FaTimes } from 'react-icons/fa';
|
|
5
5
|
import styled from 'styled-components';
|
|
@@ -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,12 +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('');
|
|
50
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
48
51
|
|
|
49
52
|
useEffect(() => {
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
if (isExpanded) {
|
|
54
|
+
scrollChatToBottom();
|
|
55
|
+
}
|
|
56
|
+
inputRef.current?.focus();
|
|
57
|
+
}, [chatMessages, isExpanded]);
|
|
52
58
|
|
|
53
59
|
const scrollChatToBottom = () => {
|
|
54
60
|
const scrollingElement = document.querySelector('.chat-body');
|
|
@@ -78,39 +84,41 @@ export const Chat: React.FC<IChatProps> = ({
|
|
|
78
84
|
<ChatContainer
|
|
79
85
|
width={styles.width || 'auto'}
|
|
80
86
|
height={styles.height || 'auto'}
|
|
87
|
+
isExpanded={isExpanded}
|
|
81
88
|
>
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
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
|
-
|
|
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>
|
|
114
122
|
</ChatContainer>
|
|
115
123
|
);
|
|
116
124
|
};
|
|
@@ -124,9 +132,9 @@ interface IMessageProps {
|
|
|
124
132
|
color: string;
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
const ChatContainer = styled.div<IContainerProps>`
|
|
135
|
+
const ChatContainer = styled.div<IContainerProps & { isExpanded: boolean }>`
|
|
128
136
|
width: ${props => props.width};
|
|
129
|
-
height: ${props => props.height};
|
|
137
|
+
height: ${props => (props.isExpanded ? props.height : 'auto')};
|
|
130
138
|
background-color: rgba(30, 30, 30, 0.3);
|
|
131
139
|
display: flex;
|
|
132
140
|
flex-direction: column;
|
|
@@ -180,10 +188,11 @@ const Message = styled.div<IMessageProps>`
|
|
|
180
188
|
word-break: break-word;
|
|
181
189
|
`;
|
|
182
190
|
|
|
183
|
-
const Form = styled.form
|
|
191
|
+
const Form = styled.form<{ isExpanded: boolean }>`
|
|
184
192
|
display: flex;
|
|
185
193
|
padding: 8px;
|
|
186
194
|
background-color: rgba(42, 42, 42, 0.4);
|
|
195
|
+
border-radius: ${props => (props.isExpanded ? '0' : '8px')};
|
|
187
196
|
`;
|
|
188
197
|
|
|
189
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,7 @@ export interface IChatRevampProps {
|
|
|
42
43
|
searchCharacterUI: boolean;
|
|
43
44
|
hideSearchCharacterUI: () => void;
|
|
44
45
|
showSearchCharacterUI: () => void;
|
|
46
|
+
minimizedByDefault?: boolean;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
@@ -67,14 +69,31 @@ export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
|
67
69
|
searchCharacterUI,
|
|
68
70
|
hideSearchCharacterUI,
|
|
69
71
|
showSearchCharacterUI,
|
|
72
|
+
minimizedByDefault = false,
|
|
70
73
|
}) => {
|
|
71
74
|
const [showRecentChats, setShowRecentChats] = useState(false);
|
|
75
|
+
const [isExpanded, setIsExpanded] = useState(!minimizedByDefault);
|
|
72
76
|
|
|
73
77
|
const isPrivate = activeTab === 'private';
|
|
74
78
|
const isTrade = activeTab === 'trade';
|
|
75
79
|
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (isPrivate) {
|
|
82
|
+
setIsExpanded(true);
|
|
83
|
+
}
|
|
84
|
+
}, [isPrivate]);
|
|
85
|
+
|
|
86
|
+
const toggleExpand = () => setIsExpanded(prev => !prev);
|
|
87
|
+
|
|
76
88
|
const toggleRecentChats = () => setShowRecentChats(prev => !prev);
|
|
77
89
|
|
|
90
|
+
const handleTabChange = (tabId: string) => {
|
|
91
|
+
if (tabId === 'private') {
|
|
92
|
+
setIsExpanded(true);
|
|
93
|
+
}
|
|
94
|
+
onChangeTab(tabId);
|
|
95
|
+
};
|
|
96
|
+
|
|
78
97
|
const handlePreviousChatCharacterClick = (
|
|
79
98
|
character: PrivateChatCharacter
|
|
80
99
|
) => {
|
|
@@ -88,7 +107,7 @@ export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
|
88
107
|
<Tab
|
|
89
108
|
key={`${tab.label}_${index}`}
|
|
90
109
|
active={tab.id === activeTab}
|
|
91
|
-
onPointerDown={() =>
|
|
110
|
+
onPointerDown={() => handleTabChange(tab.id)}
|
|
92
111
|
>
|
|
93
112
|
{tab.label}
|
|
94
113
|
</Tab>
|
|
@@ -170,31 +189,67 @@ export const ChatRevamp: React.FC<IChatRevampProps> = ({
|
|
|
170
189
|
styles={styles}
|
|
171
190
|
onFocus={onFocus}
|
|
172
191
|
onBlur={onBlur}
|
|
192
|
+
isExpanded={isExpanded}
|
|
173
193
|
/>
|
|
174
194
|
);
|
|
175
195
|
};
|
|
176
196
|
|
|
177
197
|
return (
|
|
178
|
-
|
|
179
|
-
{
|
|
198
|
+
<ChatRevampContainer>
|
|
199
|
+
<TopBar isExpanded={isExpanded}>
|
|
200
|
+
{isExpanded && renderTabs()}
|
|
201
|
+
<ExpandButton onClick={toggleExpand} isExpanded={isExpanded}>
|
|
202
|
+
{isExpanded ? <IoMdContract size={20} /> : <IoMdExpand size={20} />}
|
|
203
|
+
</ExpandButton>
|
|
204
|
+
</TopBar>
|
|
180
205
|
<PrivateChatContainer
|
|
181
206
|
width={styles?.width || '80%'}
|
|
182
207
|
height={styles?.height || 'auto'}
|
|
208
|
+
isExpanded={isExpanded}
|
|
183
209
|
>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
210
|
+
{isExpanded && (
|
|
211
|
+
<RecentChatTabContainer
|
|
212
|
+
isPrivate={isPrivate}
|
|
213
|
+
isOpen={showRecentChats}
|
|
214
|
+
>
|
|
215
|
+
{renderRecentChatTopBar()}
|
|
216
|
+
{renderRecentChatList()}
|
|
217
|
+
</RecentChatTabContainer>
|
|
218
|
+
)}
|
|
188
219
|
<ChatWrapper>{renderChatContent()}</ChatWrapper>
|
|
189
220
|
</PrivateChatContainer>
|
|
190
|
-
|
|
221
|
+
</ChatRevampContainer>
|
|
191
222
|
);
|
|
192
223
|
};
|
|
193
224
|
|
|
194
|
-
const
|
|
225
|
+
const ChatRevampContainer = styled.div`
|
|
226
|
+
display: flex;
|
|
227
|
+
flex-direction: column;
|
|
195
228
|
width: 100%;
|
|
229
|
+
position: relative;
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
interface ITopBarProps {
|
|
233
|
+
isExpanded: boolean;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const TopBar = styled.div<ITopBarProps>`
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: center;
|
|
239
|
+
justify-content: flex-start;
|
|
240
|
+
|
|
241
|
+
${({ isExpanded }) =>
|
|
242
|
+
!isExpanded &&
|
|
243
|
+
css`
|
|
244
|
+
min-height: 32px; // Ensure there's always space for the expand button when its collapsed
|
|
245
|
+
`}
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
const TabContainer = styled.div`
|
|
196
249
|
display: flex;
|
|
197
250
|
gap: 10px;
|
|
251
|
+
align-items: center;
|
|
252
|
+
flex-grow: 1;
|
|
198
253
|
`;
|
|
199
254
|
|
|
200
255
|
const Tab = styled.button<{ active: boolean }>`
|
|
@@ -216,14 +271,40 @@ const Tab = styled.button<{ active: boolean }>`
|
|
|
216
271
|
color: ${props => (props.active ? 'white' : uiColors.darkGray)};
|
|
217
272
|
`;
|
|
218
273
|
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
274
|
+
const ExpandButton = styled.button<{ isExpanded: boolean }>`
|
|
275
|
+
position: absolute;
|
|
276
|
+
top: 0;
|
|
277
|
+
right: 0;
|
|
278
|
+
width: 30px;
|
|
279
|
+
height: 30px;
|
|
280
|
+
background-color: ${uiColors.orange};
|
|
281
|
+
color: white;
|
|
282
|
+
border: none;
|
|
283
|
+
border-radius: 50%;
|
|
284
|
+
display: flex;
|
|
285
|
+
justify-content: center;
|
|
286
|
+
align-items: center;
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
transition: all 0.3s ease;
|
|
289
|
+
z-index: 10;
|
|
290
|
+
|
|
291
|
+
&:hover {
|
|
292
|
+
background-color: ${uiColors.orange};
|
|
293
|
+
}
|
|
294
|
+
`;
|
|
222
295
|
|
|
296
|
+
const PrivateChatContainer = styled.div<{
|
|
297
|
+
width: string;
|
|
298
|
+
height: string;
|
|
299
|
+
isExpanded: boolean;
|
|
300
|
+
}>`
|
|
301
|
+
width: ${({ width }) => width};
|
|
302
|
+
height: ${({ height, isExpanded }) => (isExpanded ? height : 'auto')};
|
|
223
303
|
padding: 10px;
|
|
224
304
|
background-color: rgba(0, 0, 0, 0.2);
|
|
225
305
|
display: flex;
|
|
226
306
|
gap: 10px;
|
|
307
|
+
flex-direction: column;
|
|
227
308
|
`;
|
|
228
309
|
|
|
229
310
|
const RecentChatTabContainer = styled.div<{
|
|
@@ -394,4 +475,5 @@ const ChatWrapper = styled.div`
|
|
|
394
475
|
overflow: hidden;
|
|
395
476
|
display: flex;
|
|
396
477
|
flex-direction: column;
|
|
478
|
+
position: relative;
|
|
397
479
|
`;
|