@rpg-engine/long-bow 0.5.41 → 0.5.43

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.5.41",
3
+ "version": "0.5.43",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -83,7 +83,7 @@
83
83
  },
84
84
  "dependencies": {
85
85
  "@rollup/plugin-image": "^2.1.1",
86
- "@rpg-engine/shared": "^0.8.92",
86
+ "@rpg-engine/shared": "^0.9.0",
87
87
  "dayjs": "^1.11.2",
88
88
  "font-awesome": "^4.7.0",
89
89
  "fs-extra": "^10.1.0",
@@ -161,8 +161,8 @@ interface IButtonProps {
161
161
  }
162
162
 
163
163
  const ChatContainer = styled.div<IContainerProps>`
164
- height: ${props => props.height};
165
- width: ${({ width }) => width};
164
+ height: ${props => props.height} !important;
165
+ width: 100%;
166
166
  padding: 10px;
167
167
  background-color: rgba(0, 0, 0, 0.2);
168
168
  height: auto;
@@ -178,10 +178,7 @@ const TextField = styled.input`
178
178
  const MessagesContainer = styled.div`
179
179
  height: 70%;
180
180
  margin-bottom: 10px;
181
- .chat-body {
182
- max-height: auto;
183
- overflow-y: auto;
184
- }
181
+ overflow-y: auto;
185
182
  `;
186
183
 
187
184
  const Message = styled.div<IMessageProps>`
@@ -4,16 +4,13 @@ import {
4
4
  IPrivateChatMessage,
5
5
  } from '@rpg-engine/shared';
6
6
  import React, { useEffect, useState } from 'react';
7
+ import { RxCross2, RxMagnifyingGlass } from 'react-icons/rx';
7
8
  import styled from 'styled-components';
8
9
  import { uiColors } from '../../constants/uiColors';
10
+ import { uiFonts } from '../../constants/uiFonts';
9
11
  import { Chat, IStyles } from '../Chat/Chat';
10
12
  import { SearchCharacter } from './SearchCharacter';
11
13
 
12
- export interface ITabStyles {
13
- width?: string;
14
- height?: string;
15
- }
16
-
17
14
  export type PrivateChatCharacter = Pick<ICharacter, '_id' | 'name'>;
18
15
 
19
16
  export interface IChatRevampProps {
@@ -22,7 +19,6 @@ export interface IChatRevampProps {
22
19
  onCloseButton: () => void;
23
20
  onFocus?: () => void;
24
21
  onBlur?: () => void;
25
- opacity?: number;
26
22
  styles?: IStyles;
27
23
  tabs: { label: string; id: string }[];
28
24
  activeTab: string;
@@ -31,6 +27,11 @@ export interface IChatRevampProps {
31
27
  onChangeCharacterName: (characterName: string) => void;
32
28
  onCharacterClick?: (character: PrivateChatCharacter) => void;
33
29
  onSendPrivateChatMessage: (message: string) => void;
30
+ recentChatCharacters?: PrivateChatCharacter[];
31
+ recentSelectedChatCharacterId?: string;
32
+ onPreviousChatCharacterClick?: (character: PrivateChatCharacter) => void;
33
+ onRemoveRecentChatCharacter?: (character: PrivateChatCharacter) => void;
34
+ unseenMessageCharacterIds?: string[];
34
35
  }
35
36
 
36
37
  export const ChatRevamp = ({
@@ -47,15 +48,29 @@ export const ChatRevamp = ({
47
48
  privateChatCharacters,
48
49
  onCharacterClick,
49
50
  onSendPrivateChatMessage,
51
+ recentChatCharacters,
52
+ recentSelectedChatCharacterId = '',
53
+ onPreviousChatCharacterClick,
54
+ onRemoveRecentChatCharacter,
55
+ unseenMessageCharacterIds = [],
50
56
  }: IChatRevampProps) => {
51
- const [showSearchCharacter, setShowSearchCharacter] = useState(true);
57
+ const [showSearchCharacterUI, setShowSearchCharacterUI] = useState(true);
58
+ const [showRecentChats, setShowRecentChats] = useState(false);
52
59
 
53
60
  useEffect(() => {
54
- setShowSearchCharacter(true);
61
+ setShowSearchCharacterUI(true);
55
62
  }, [activeTab]);
56
63
 
57
64
  const isPrivate = activeTab === 'private';
58
65
 
66
+ const handlePreviousChatCharacterClick = (
67
+ character: PrivateChatCharacter
68
+ ) => {
69
+ if (!onPreviousChatCharacterClick) return;
70
+ onPreviousChatCharacterClick(character);
71
+ setShowSearchCharacterUI(false);
72
+ };
73
+
59
74
  return (
60
75
  <>
61
76
  <TabContainer>
@@ -69,29 +84,81 @@ export const ChatRevamp = ({
69
84
  </Tab>
70
85
  ))}
71
86
  </TabContainer>
72
- {isPrivate && showSearchCharacter ? (
73
- <SearchCharacter
74
- onFocus={onFocus}
75
- onBlur={onBlur}
76
- onChangeCharacterName={onChangeCharacterName}
77
- styles={styles}
78
- recentCharacters={privateChatCharacters}
79
- setShowSearchCharacter={setShowSearchCharacter}
80
- onCharacterClick={onCharacterClick}
81
- />
82
- ) : (
83
- <Chat
84
- chatMessages={chatMessages}
85
- onSendChatMessage={
86
- isPrivate ? onSendPrivateChatMessage : onSendGlobalChatMessage
87
- }
88
- sendMessage={true}
89
- onCloseButton={onCloseButton}
90
- styles={styles}
91
- onFocus={onFocus}
92
- onBlur={onBlur}
93
- />
94
- )}
87
+ <PrivateChatContainer
88
+ width={styles?.width || '80%'}
89
+ height={styles?.height || 'auto'}
90
+ >
91
+ <RecentChatTabContainer isPrivate={isPrivate} isOpen={showRecentChats}>
92
+ <RecentChatTopBar isOpen={showRecentChats}>
93
+ <BurgerIconContainer
94
+ onPointerDown={() => setShowRecentChats(t => !t)}
95
+ hasUnseenMessages={
96
+ unseenMessageCharacterIds?.length > 0 ? true : false
97
+ }
98
+ >
99
+ <BurgerLineIcon></BurgerLineIcon>
100
+ <BurgerLineIcon></BurgerLineIcon>
101
+ <BurgerLineIcon></BurgerLineIcon>
102
+ </BurgerIconContainer>
103
+ {showRecentChats && (
104
+ <SearchButton
105
+ onPointerDown={() => setShowSearchCharacterUI(true)}
106
+ >
107
+ <RxMagnifyingGlass size={16} color={uiColors.white} />
108
+ </SearchButton>
109
+ )}
110
+ </RecentChatTopBar>
111
+
112
+ <RecentChatLogContainer isOpen={showRecentChats}>
113
+ {recentChatCharacters?.map(character => (
114
+ <ListElementContainer key={character._id}>
115
+ <ListElement
116
+ active={character._id === recentSelectedChatCharacterId}
117
+ onPointerDown={() =>
118
+ handlePreviousChatCharacterClick(character)
119
+ }
120
+ >
121
+ <StatusDot
122
+ isUnseen={
123
+ unseenMessageCharacterIds?.includes(character._id) ||
124
+ false
125
+ }
126
+ />
127
+ {character.name}
128
+ </ListElement>
129
+ <CloseButton
130
+ onPointerDown={() => onRemoveRecentChatCharacter?.(character)}
131
+ >
132
+ <RxCross2 size={16} />
133
+ </CloseButton>
134
+ </ListElementContainer>
135
+ ))}
136
+ </RecentChatLogContainer>
137
+ </RecentChatTabContainer>
138
+ {isPrivate && showSearchCharacterUI ? (
139
+ <SearchCharacter
140
+ onFocus={onFocus}
141
+ onBlur={onBlur}
142
+ onChangeCharacterName={onChangeCharacterName}
143
+ styles={styles}
144
+ recentCharacters={privateChatCharacters}
145
+ setShowSearchCharacter={setShowSearchCharacterUI}
146
+ onCharacterClick={onCharacterClick}
147
+ />
148
+ ) : (
149
+ <Chat
150
+ chatMessages={chatMessages}
151
+ onSendChatMessage={
152
+ isPrivate ? onSendPrivateChatMessage : onSendGlobalChatMessage
153
+ }
154
+ sendMessage={true}
155
+ onCloseButton={onCloseButton}
156
+ styles={styles}
157
+ onFocus={onFocus}
158
+ onBlur={onBlur}
159
+ />
160
+ )}
161
+ </PrivateChatContainer>
95
162
  </>
96
163
  );
97
164
  };
@@ -119,3 +186,136 @@ const Tab = styled.button<{ active: boolean }>`
119
186
  props.active ? uiColors.orange : 'transparent'};
120
187
  color: ${props => (props.active ? 'white' : uiColors.raisinBlack)};
121
188
  `;
189
+
190
+ const PrivateChatContainer = styled.div<{ width: string; height: string }>`
191
+ width: ${({ width }) => width};
192
+ min-height: ${({ height }) => height} !important;
193
+ padding: 10px;
194
+ background-color: rgba(0, 0, 0, 0.2);
195
+ height: auto;
196
+ display: flex;
197
+ gap: 10px;
198
+ `;
199
+
200
+ const RecentChatTabContainer = styled.div<{
201
+ isPrivate: boolean;
202
+ isOpen: boolean;
203
+ }>`
204
+ display: ${props => (props.isPrivate ? 'flex' : 'none')};
205
+ flex-direction: column;
206
+ border-right: 1px solid ${uiColors.gray};
207
+ outline: none;
208
+ width: ${props => (props.isOpen ? '20%' : '30px')} !important;
209
+ transition: width 0.3s ease-in-out;
210
+ overflow: hidden;
211
+
212
+ @media (max-width: 768px) {
213
+ width: ${props => (props.isOpen ? '40%' : '30px')} !important;
214
+ }
215
+ `;
216
+
217
+ const RecentChatTopBar = styled.div<{ isOpen: boolean }>`
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: space-between;
221
+ height: 30px;
222
+ `;
223
+
224
+ const SearchButton = styled.button`
225
+ border: none;
226
+ background-color: transparent;
227
+ display: flex;
228
+ flex-direction: column;
229
+ align-items: flex-end;
230
+ gap: 2px;
231
+ padding: 4px;
232
+ position: relative;
233
+ `;
234
+
235
+ const BurgerIconContainer = styled.button<{ hasUnseenMessages?: boolean }>`
236
+ border: none;
237
+ background-color: transparent;
238
+ display: flex;
239
+ flex-direction: column;
240
+ align-items: flex-end;
241
+ padding: 4px;
242
+ gap: 2px;
243
+ position: relative;
244
+
245
+ &:after {
246
+ content: '';
247
+ width: 6px;
248
+ height: 6px;
249
+ position: absolute;
250
+ top: 0;
251
+ right: 2px;
252
+ border-radius: 50%;
253
+ background-color: ${uiColors.lightGreen};
254
+ display: ${props => (props.hasUnseenMessages ? 'block' : 'none')};
255
+ }
256
+ `;
257
+
258
+ const BurgerLineIcon = styled.span`
259
+ width: 1rem;
260
+ height: 2px;
261
+ background-color: #ffffff;
262
+ `;
263
+
264
+ const RecentChatLogContainer = styled.div<{ isOpen: boolean }>`
265
+ border: none;
266
+ list-style: none;
267
+ display: flex;
268
+ opacity: ${props => (props.isOpen ? 1 : 0)};
269
+ flex-direction: column;
270
+ gap: 0.5rem;
271
+ transition: opacity 0.3s ease-in-out;
272
+ padding: 0;
273
+ margin: 0;
274
+ flex: 1;
275
+ `;
276
+
277
+ const ListElementContainer = styled.div`
278
+ display: flex;
279
+ justify-content: space-between;
280
+ align-items: center;
281
+ `;
282
+
283
+ const ListElement = styled.button<{ active: boolean }>`
284
+ margin: 0.5rem 0 !important;
285
+ font-size: ${uiFonts.size.small} !important;
286
+ padding: 2px;
287
+ all: unset;
288
+ color: ${props => (props.active ? uiColors.yellow : uiColors.white)};
289
+ width: 100%;
290
+ position: relative;
291
+ display: flex;
292
+ align-items: center;
293
+ gap: 4px;
294
+
295
+ &:hover {
296
+ color: #ff0;
297
+ }
298
+ `;
299
+
300
+ const StatusDot = styled.span<{ isUnseen: boolean }>`
301
+ width: 6px;
302
+ height: 6px;
303
+ border-radius: 50%;
304
+ background-color: ${props =>
305
+ props.isUnseen ? uiColors.lightGreen : uiColors.gray};
306
+ display: inline-block;
307
+ margin-right: 6px;
308
+ `;
309
+
310
+ const CloseButton = styled.button`
311
+ all: unset;
312
+ font-size: ${uiFonts.size.xxsmall};
313
+ margin: 0 0.5rem;
314
+ transition: all 0.2s ease-in-out;
315
+ background-color: ${uiColors.red};
316
+ color: ${uiColors.white};
317
+ &:hover {
318
+ background-color: ${uiColors.white};
319
+ color: ${uiColors.red};
320
+ }
321
+ `;
@@ -50,19 +50,18 @@ export const SearchCharacter = ({
50
50
  };
51
51
 
52
52
  return (
53
- <SearchCharacterContainer
54
- width={styles?.width || '80%'}
55
- height={styles?.height || 'auto'}
56
- >
53
+ <SearchContainer>
57
54
  <Form onSubmit={handleSubmit}>
58
55
  <Column flex={70}>
59
56
  <TextField
60
57
  value={characterName}
61
- id="inputCharacterName"
58
+ id="characterName"
59
+ name='characterName'
62
60
  onChange={e => {
63
61
  setCharacterName(e.target.value);
64
62
  onChangeCharacterName(e.target.value);
65
63
  }}
64
+ placeholder='Search for a character...'
66
65
  height={20}
67
66
  type="text"
68
67
  autoComplete="off"
@@ -70,11 +69,11 @@ export const SearchCharacter = ({
70
69
  onBlur={onBlur}
71
70
  onPointerDown={onFocus}
72
71
  autoFocus
73
- placeholder="Type a character name..."
74
- />
72
+ />
75
73
  </Column>
76
74
  <Column justifyContent="flex-end">
77
75
  <SearchButton
76
+ type='submit'
78
77
  buttonColor={styles?.buttonColor || '#005b96'}
79
78
  buttonBackgroundColor={
80
79
  styles?.buttonBackgroundColor || 'rgba(0,0,0,.5)'
@@ -96,27 +95,19 @@ export const SearchCharacter = ({
96
95
  ))}
97
96
  </ListContainer>
98
97
  )}
99
- </SearchCharacterContainer>
98
+ </SearchContainer>
100
99
  );
101
100
  };
102
101
 
103
- interface IContainerProps {
104
- width: string;
105
- height: string;
106
- }
107
102
 
108
103
  interface IButtonProps {
109
104
  buttonColor: string;
110
105
  buttonBackgroundColor: string;
111
106
  }
112
107
 
113
- const SearchCharacterContainer = styled.div<IContainerProps>`
114
- height: ${props => props.height};
115
- width: ${({ width }) => width};
116
- padding: 10px;
117
- background-color: rgba(0, 0, 0, 0.2);
118
- height: auto;
119
- `;
108
+ const SearchContainer = styled.div`
109
+ width: 100%;
110
+ `
120
111
 
121
112
  const Form = styled.form`
122
113
  display: flex;
@@ -150,6 +141,7 @@ const ListContainer = styled.ul`
150
141
  const ListElement = styled.li`
151
142
  margin: 0.5rem 0 !important;
152
143
  font-size: ${uiFonts.size.small};
144
+ padding: 0.5rem 2px;
153
145
 
154
146
  &:hover {
155
147
  color: #ff0;
@@ -66,6 +66,8 @@ type onDragStart =
66
66
  | undefined;
67
67
  type onDragEnd = ((quantity?: number) => void) | undefined;
68
68
 
69
+ const MIN_SLOTS_FOR_SCROLL = 20;
70
+
69
71
  export const ItemContainer: React.FC<IItemContainerProps> = ({
70
72
  itemContainer,
71
73
  onClose,
@@ -241,7 +243,11 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
241
243
  atlasJSON={atlasJSON}
242
244
  />
243
245
  )}
244
- <ItemsContainer className="item-container-body" ref={containerRef}>
246
+ <ItemsContainer
247
+ className="item-container-body"
248
+ ref={containerRef}
249
+ isScrollable={itemContainer.slotQty > MIN_SLOTS_FOR_SCROLL}
250
+ >
245
251
  {onRenderSlots()}
246
252
  </ItemsContainer>
247
253
  </SlotsContainer>
@@ -255,12 +261,16 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
255
261
  );
256
262
  };
257
263
 
258
- const ItemsContainer = styled.div`
264
+ interface IItemsContainerProps {
265
+ isScrollable: boolean;
266
+ }
267
+
268
+ const ItemsContainer = styled.div<IItemsContainerProps>`
259
269
  display: flex;
260
270
  justify-content: center;
261
271
  flex-wrap: wrap;
262
272
  max-height: 270px;
263
- overflow-y: auto;
273
+ overflow-y: ${({ isScrollable }) => (isScrollable ? 'scroll' : 'hidden')};
264
274
  overflow-x: hidden;
265
275
  width: 415px;
266
276
  `;
@@ -5,7 +5,7 @@ import {
5
5
  ItemSlotType,
6
6
  ItemSubType,
7
7
  ItemType,
8
- UserAccountTypes
8
+ UserAccountTypes,
9
9
  } from '@rpg-engine/shared';
10
10
 
11
11
  export const items: IItem[] = [
@@ -586,7 +586,7 @@ export const itemContainerMock = (
586
586
  _id: '629ba0b6fe3f43002f58f23b',
587
587
  name: 'Item Container',
588
588
  owner: '629ba0b6fe3f43002f58f23b',
589
- slotQty: 60,
589
+ slotQty: 20,
590
590
  slots: {
591
591
  0: items[0],
592
592
  1: items[1],
@@ -614,4 +614,4 @@ export const itemContainerMock = (
614
614
  ...props,
615
615
  };
616
616
  }
617
- }
617
+ };
@@ -221,7 +221,6 @@ Default.args = {
221
221
  onSendGlobalChatMessage: () => {},
222
222
  onChangeCharacterName: () => {},
223
223
  chatMessages: chatMessagesMock,
224
- opacity: 0.5,
225
224
  styles: { width: `calc(100% - 0.5rem * 2)`, height: '200px' },
226
225
  onCloseButton: () => console.log('closing chat...'),
227
226
  tabs: tabsMock,
@@ -237,7 +236,6 @@ PrivateCharacters.args = {
237
236
  onSendGlobalChatMessage: () => {},
238
237
  onChangeCharacterName: () => {},
239
238
  chatMessages: chatMessagesMock,
240
- opacity: 0.5,
241
239
  styles: { width: `calc(100% - 0.5rem * 2)`, height: '200px' },
242
240
  onCloseButton: () => {},
243
241
  tabs: tabsMock,
@@ -245,4 +243,7 @@ PrivateCharacters.args = {
245
243
  activeTab: 'private',
246
244
  onChangeTab: () => {},
247
245
  onSendPrivateChatMessage: () => {},
246
+ recentChatCharacters: recentPrivateChatCharactersMock,
247
+ recentSelectedChatCharacterId: recentPrivateChatCharactersMock[0]._id,
248
+ unseenMessageCharacterIds: [recentPrivateChatCharactersMock[0]._id],
248
249
  };