@rpg-engine/long-bow 0.6.85 → 0.6.87
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/ChatRevamp/SearchCharacter.d.ts +5 -5
- package/dist/components/Spellbook/cards/SpellInfoWrapper.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +184 -110
- 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 +184 -110
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +2 -1
- package/src/components/ChatRevamp/ChatRevamp.tsx +81 -50
- package/src/components/ChatRevamp/SearchCharacter.tsx +184 -86
- package/src/components/Spellbook/cards/SpellInfoWrapper.tsx +21 -6
- package/src/stories/ChatRevamp.stories.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpg-engine/long-bow",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.87",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
"@rpg-engine/shared": "^0.9.52",
|
|
87
87
|
"dayjs": "^1.11.2",
|
|
88
88
|
"font-awesome": "^4.7.0",
|
|
89
|
+
"framer-motion": "^11.3.31",
|
|
89
90
|
"fs-extra": "^10.1.0",
|
|
90
91
|
"lodash": "^4.17.21",
|
|
91
92
|
"lodash-es": "^4.17.21",
|
|
@@ -10,6 +10,7 @@ import styled from 'styled-components';
|
|
|
10
10
|
import { uiColors } from '../../constants/uiColors';
|
|
11
11
|
import { uiFonts } from '../../constants/uiFonts';
|
|
12
12
|
import { Chat, IStyles } from '../Chat/Chat';
|
|
13
|
+
import { Ellipsis } from '../shared/Ellipsis';
|
|
13
14
|
import { SearchCharacter } from './SearchCharacter';
|
|
14
15
|
|
|
15
16
|
export type PrivateChatCharacter = Pick<ICharacter, '_id' | 'name'>;
|
|
@@ -81,13 +82,13 @@ export const ChatRevamp = ({
|
|
|
81
82
|
};
|
|
82
83
|
|
|
83
84
|
return (
|
|
84
|
-
|
|
85
|
+
<ChatContainer>
|
|
85
86
|
<TabContainer>
|
|
86
87
|
{tabs.map((tab, index) => (
|
|
87
88
|
<Tab
|
|
88
89
|
key={`${tab.label}_${index}`}
|
|
89
90
|
active={tab.id === activeTab}
|
|
90
|
-
|
|
91
|
+
onClick={() => onChangeTab(tab.id)}
|
|
91
92
|
>
|
|
92
93
|
{tab.label}
|
|
93
94
|
</Tab>
|
|
@@ -97,56 +98,63 @@ export const ChatRevamp = ({
|
|
|
97
98
|
width={styles?.width || '80%'}
|
|
98
99
|
height={styles?.height || 'auto'}
|
|
99
100
|
>
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
{isPrivate && (
|
|
102
|
+
<RecentChatTabContainer
|
|
103
|
+
isPrivate={isPrivate}
|
|
104
|
+
isOpen={showRecentChats}
|
|
105
|
+
>
|
|
106
|
+
<RecentChatTopBar isOpen={showRecentChats}>
|
|
107
|
+
<BurgerIconContainer
|
|
108
|
+
onClick={() => setShowRecentChats(t => !t)}
|
|
109
|
+
hasUnseenMessages={
|
|
110
|
+
unseenMessageCharacterIds?.length > 0 || false
|
|
111
|
+
}
|
|
112
|
+
>
|
|
113
|
+
<BurgerLineIcon></BurgerLineIcon>
|
|
114
|
+
<BurgerLineIcon></BurgerLineIcon>
|
|
115
|
+
<BurgerLineIcon></BurgerLineIcon>
|
|
116
|
+
</BurgerIconContainer>
|
|
117
|
+
{showRecentChats && (
|
|
118
|
+
<SearchButton onClick={() => showSearchCharacterUI()}>
|
|
119
|
+
<RxMagnifyingGlass size={16} color={uiColors.white} />
|
|
120
|
+
</SearchButton>
|
|
121
|
+
)}
|
|
122
|
+
</RecentChatTopBar>
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
124
|
+
<RecentChatLogContainer isOpen={showRecentChats}>
|
|
125
|
+
{recentChatCharacters?.map(character => (
|
|
126
|
+
<ListElementContainer key={character._id}>
|
|
127
|
+
<ListElement
|
|
128
|
+
active={character._id === recentSelectedChatCharacterId}
|
|
129
|
+
onClick={() => handlePreviousChatCharacterClick(character)}
|
|
130
|
+
>
|
|
131
|
+
<StatusDot
|
|
132
|
+
isUnseen={
|
|
133
|
+
unseenMessageCharacterIds?.includes(character._id) ||
|
|
134
|
+
false
|
|
135
|
+
}
|
|
136
|
+
/>
|
|
137
|
+
<Ellipsis maxLines={1} maxWidth="140px">
|
|
138
|
+
{character.name}
|
|
139
|
+
</Ellipsis>
|
|
140
|
+
</ListElement>
|
|
141
|
+
<CloseButton
|
|
142
|
+
onClick={() => onRemoveRecentChatCharacter?.(character)}
|
|
143
|
+
>
|
|
144
|
+
<RxCross2 size={16} />
|
|
145
|
+
</CloseButton>
|
|
146
|
+
</ListElementContainer>
|
|
147
|
+
))}
|
|
148
|
+
</RecentChatLogContainer>
|
|
149
|
+
</RecentChatTabContainer>
|
|
150
|
+
)}
|
|
143
151
|
{isPrivate && searchCharacterUI ? (
|
|
144
152
|
<SearchCharacter
|
|
145
153
|
onFocus={onFocus}
|
|
146
154
|
onBlur={onBlur}
|
|
147
155
|
onChangeCharacterName={onChangeCharacterName}
|
|
148
156
|
styles={styles}
|
|
149
|
-
recentCharacters={privateChatCharacters}
|
|
157
|
+
recentCharacters={privateChatCharacters as ICharacter[]}
|
|
150
158
|
hideSearchCharacterUI={hideSearchCharacterUI}
|
|
151
159
|
onCharacterClick={onCharacterClick}
|
|
152
160
|
/>
|
|
@@ -168,10 +176,18 @@ export const ChatRevamp = ({
|
|
|
168
176
|
/>
|
|
169
177
|
)}
|
|
170
178
|
</PrivateChatContainer>
|
|
171
|
-
|
|
179
|
+
</ChatContainer>
|
|
172
180
|
);
|
|
173
181
|
};
|
|
174
182
|
|
|
183
|
+
const ChatContainer = styled.div`
|
|
184
|
+
display: flex;
|
|
185
|
+
flex-direction: column;
|
|
186
|
+
gap: 10px;
|
|
187
|
+
max-width: 1200px;
|
|
188
|
+
margin: 0 auto;
|
|
189
|
+
`;
|
|
190
|
+
|
|
175
191
|
const TabContainer = styled.div`
|
|
176
192
|
width: 100%;
|
|
177
193
|
display: flex;
|
|
@@ -185,15 +201,20 @@ const Tab = styled.button<{ active: boolean }>`
|
|
|
185
201
|
all: unset;
|
|
186
202
|
padding: 0.6rem;
|
|
187
203
|
font-size: 0.8rem;
|
|
188
|
-
|
|
189
204
|
border-radius: 5px 5px 0 0;
|
|
190
205
|
border-width: 0.25rem 0.25rem 0 0.25rem;
|
|
191
206
|
border-style: solid;
|
|
192
207
|
border-color: ${props => (props.active ? '#c65102' : uiColors.gray)};
|
|
193
|
-
|
|
194
208
|
background-color: ${props =>
|
|
195
209
|
props.active ? uiColors.orange : 'transparent'};
|
|
196
210
|
color: ${props => (props.active ? 'white' : uiColors.raisinBlack)};
|
|
211
|
+
transition: all 0.3s ease;
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
|
|
214
|
+
&:hover {
|
|
215
|
+
background-color: ${props =>
|
|
216
|
+
props.active ? uiColors.orange : uiColors.lightGray};
|
|
217
|
+
}
|
|
197
218
|
`;
|
|
198
219
|
|
|
199
220
|
const PrivateChatContainer = styled.div<{ width: string; height: string }>`
|
|
@@ -204,6 +225,8 @@ const PrivateChatContainer = styled.div<{ width: string; height: string }>`
|
|
|
204
225
|
height: auto;
|
|
205
226
|
display: flex;
|
|
206
227
|
gap: 10px;
|
|
228
|
+
border-radius: 0 0 10px 10px;
|
|
229
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
207
230
|
`;
|
|
208
231
|
|
|
209
232
|
const RecentChatTabContainer = styled.div<{
|
|
@@ -292,7 +315,8 @@ const ListElementContainer = styled.div`
|
|
|
292
315
|
const ListElement = styled.button<{ active: boolean }>`
|
|
293
316
|
margin: 0.5rem 0 !important;
|
|
294
317
|
font-size: ${uiFonts.size.small} !important;
|
|
295
|
-
padding:
|
|
318
|
+
padding: 8px;
|
|
319
|
+
border-radius: 4px;
|
|
296
320
|
all: unset;
|
|
297
321
|
color: ${props => (props.active ? uiColors.yellow : uiColors.white)};
|
|
298
322
|
width: 100%;
|
|
@@ -300,9 +324,10 @@ const ListElement = styled.button<{ active: boolean }>`
|
|
|
300
324
|
display: flex;
|
|
301
325
|
align-items: center;
|
|
302
326
|
gap: 4px;
|
|
327
|
+
transition: all 0.2s ease;
|
|
303
328
|
|
|
304
329
|
&:hover {
|
|
305
|
-
color:
|
|
330
|
+
background-color: rgba(255, 255, 255, 0.1);
|
|
306
331
|
}
|
|
307
332
|
`;
|
|
308
333
|
|
|
@@ -323,6 +348,12 @@ const CloseButton = styled.button`
|
|
|
323
348
|
transition: all 0.2s ease-in-out;
|
|
324
349
|
background-color: ${uiColors.red};
|
|
325
350
|
color: ${uiColors.white};
|
|
351
|
+
border-radius: 50%;
|
|
352
|
+
padding: 4px;
|
|
353
|
+
display: flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
justify-content: center;
|
|
356
|
+
|
|
326
357
|
&:hover {
|
|
327
358
|
background-color: ${uiColors.white};
|
|
328
359
|
color: ${uiColors.red};
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
import { ICharacter } from '@rpg-engine/shared/dist/types/character.types';
|
|
1
2
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { RxMagnifyingGlass } from 'react-icons/rx';
|
|
3
|
+
import { RxCross2, RxMagnifyingGlass } from 'react-icons/rx';
|
|
3
4
|
import styled from 'styled-components';
|
|
4
5
|
import { uiColors } from '../../constants/uiColors';
|
|
5
6
|
import { uiFonts } from '../../constants/uiFonts';
|
|
6
7
|
import { IStyles } from '../Chat/Chat';
|
|
7
|
-
import {
|
|
8
|
-
import type { PrivateChatCharacter } from './ChatRevamp';
|
|
8
|
+
import { Ellipsis } from '../shared/Ellipsis';
|
|
9
9
|
|
|
10
10
|
interface ISearchCharacterProps {
|
|
11
11
|
onFocus?: () => void;
|
|
12
12
|
onBlur?: () => void;
|
|
13
13
|
onChangeCharacterName: (characterName: string) => void;
|
|
14
14
|
styles?: IStyles;
|
|
15
|
-
recentCharacters?:
|
|
16
|
-
onCharacterClick?: (character:
|
|
15
|
+
recentCharacters?: ICharacter[];
|
|
16
|
+
onCharacterClick?: (character: ICharacter) => void;
|
|
17
17
|
hideSearchCharacterUI: () => void;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export const SearchCharacter = ({
|
|
20
|
+
export const SearchCharacter: React.FC<ISearchCharacterProps> = ({
|
|
21
21
|
onChangeCharacterName,
|
|
22
22
|
onBlur,
|
|
23
23
|
onFocus,
|
|
@@ -25,143 +25,241 @@ export const SearchCharacter = ({
|
|
|
25
25
|
hideSearchCharacterUI,
|
|
26
26
|
onCharacterClick,
|
|
27
27
|
styles = {
|
|
28
|
-
textColor:
|
|
29
|
-
buttonColor:
|
|
30
|
-
buttonBackgroundColor:
|
|
31
|
-
width: '
|
|
28
|
+
textColor: uiColors.white,
|
|
29
|
+
buttonColor: uiColors.orange,
|
|
30
|
+
buttonBackgroundColor: uiColors.darkGray,
|
|
31
|
+
width: '100%',
|
|
32
32
|
height: 'auto',
|
|
33
33
|
},
|
|
34
|
-
}
|
|
34
|
+
}) => {
|
|
35
35
|
const [characterName, setCharacterName] = useState('');
|
|
36
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
36
37
|
const searchCharacterRef = useRef<HTMLInputElement>(null);
|
|
37
38
|
|
|
38
39
|
useEffect(() => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
}, 100);
|
|
44
|
-
|
|
45
|
-
return () => clearTimeout(timer);
|
|
40
|
+
if (searchCharacterRef.current) {
|
|
41
|
+
searchCharacterRef.current.focus();
|
|
42
|
+
}
|
|
46
43
|
}, []);
|
|
47
44
|
|
|
48
|
-
const handleSubmit = (event: React.
|
|
45
|
+
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
49
46
|
event.preventDefault();
|
|
50
|
-
if (!characterName
|
|
47
|
+
if (!characterName.trim()) return;
|
|
51
48
|
onChangeCharacterName(characterName);
|
|
52
49
|
};
|
|
53
50
|
|
|
54
|
-
const handleCharacterClick = (
|
|
55
|
-
character: PrivateChatCharacter
|
|
56
|
-
) => {
|
|
51
|
+
const handleCharacterClick = (character: ICharacter) => {
|
|
57
52
|
if (!onCharacterClick) return;
|
|
58
53
|
setCharacterName('');
|
|
59
54
|
onCharacterClick(character);
|
|
60
55
|
hideSearchCharacterUI();
|
|
61
56
|
};
|
|
62
57
|
|
|
58
|
+
const handleFocus = () => {
|
|
59
|
+
setIsFocused(true);
|
|
60
|
+
onFocus?.();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleBlur = () => {
|
|
64
|
+
setIsFocused(false);
|
|
65
|
+
onBlur?.();
|
|
66
|
+
};
|
|
67
|
+
|
|
63
68
|
return (
|
|
64
|
-
<SearchContainer>
|
|
69
|
+
<SearchContainer width={styles.width || 'auto'}>
|
|
65
70
|
<Form onSubmit={handleSubmit}>
|
|
66
|
-
<
|
|
71
|
+
<InputWrapper isFocused={isFocused}>
|
|
72
|
+
<SearchIcon>
|
|
73
|
+
<RxMagnifyingGlass />
|
|
74
|
+
</SearchIcon>
|
|
67
75
|
<TextField
|
|
68
76
|
value={characterName}
|
|
69
77
|
ref={searchCharacterRef}
|
|
70
|
-
id="characterName"
|
|
71
|
-
name='characterName'
|
|
72
78
|
onChange={e => {
|
|
73
79
|
setCharacterName(e.target.value);
|
|
74
80
|
onChangeCharacterName(e.target.value);
|
|
75
81
|
}}
|
|
76
|
-
placeholder=
|
|
77
|
-
height={20}
|
|
82
|
+
placeholder="Search for a character..."
|
|
78
83
|
type="text"
|
|
79
84
|
autoComplete="off"
|
|
80
|
-
onFocus={
|
|
81
|
-
onBlur={
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
</SearchButton>
|
|
97
|
-
</Column>
|
|
85
|
+
onFocus={handleFocus}
|
|
86
|
+
onBlur={handleBlur}
|
|
87
|
+
/>
|
|
88
|
+
{characterName && (
|
|
89
|
+
<ClearButton onClick={() => setCharacterName('')} type="button">
|
|
90
|
+
<RxCross2 />
|
|
91
|
+
</ClearButton>
|
|
92
|
+
)}
|
|
93
|
+
</InputWrapper>
|
|
94
|
+
<SearchButton
|
|
95
|
+
type="submit"
|
|
96
|
+
buttonColor={styles.buttonColor!}
|
|
97
|
+
buttonBackgroundColor={styles.buttonBackgroundColor!}
|
|
98
|
+
>
|
|
99
|
+
Search
|
|
100
|
+
</SearchButton>
|
|
98
101
|
</Form>
|
|
99
102
|
|
|
100
103
|
{recentCharacters && recentCharacters.length > 0 && (
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
<ResultsContainer>
|
|
105
|
+
<ResultsTitle>Recent Characters</ResultsTitle>
|
|
106
|
+
<ResultsList>
|
|
107
|
+
{recentCharacters.map(character => (
|
|
108
|
+
<ResultItem
|
|
109
|
+
key={character._id}
|
|
110
|
+
onClick={() => handleCharacterClick(character)}
|
|
111
|
+
>
|
|
112
|
+
<CharacterAvatar
|
|
113
|
+
src={character.textureKey || '/default-avatar.png'}
|
|
114
|
+
alt={character.name}
|
|
115
|
+
/>
|
|
116
|
+
<CharacterInfo>
|
|
117
|
+
<CharacterName>
|
|
118
|
+
<Ellipsis maxLines={1} maxWidth="150px">
|
|
119
|
+
{character.name}
|
|
120
|
+
</Ellipsis>
|
|
121
|
+
</CharacterName>
|
|
122
|
+
<CharacterDescription>
|
|
123
|
+
<Ellipsis
|
|
124
|
+
maxLines={1}
|
|
125
|
+
maxWidth="150px"
|
|
126
|
+
>{`Class: ${character.class}, Level: ${character.skills.level}`}</Ellipsis>
|
|
127
|
+
</CharacterDescription>
|
|
128
|
+
</CharacterInfo>
|
|
129
|
+
</ResultItem>
|
|
130
|
+
))}
|
|
131
|
+
</ResultsList>
|
|
132
|
+
</ResultsContainer>
|
|
108
133
|
)}
|
|
109
|
-
|
|
134
|
+
</SearchContainer>
|
|
110
135
|
);
|
|
111
136
|
};
|
|
112
137
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const SearchContainer = styled.div`
|
|
120
|
-
width: 100%;
|
|
121
|
-
`
|
|
138
|
+
const SearchContainer = styled.div<{ width: string }>`
|
|
139
|
+
width: ${({ width }) => width};
|
|
140
|
+
max-width: 600px;
|
|
141
|
+
margin: 0 auto;
|
|
142
|
+
`;
|
|
122
143
|
|
|
123
144
|
const Form = styled.form`
|
|
124
145
|
display: flex;
|
|
125
|
-
|
|
126
|
-
|
|
146
|
+
gap: 10px;
|
|
147
|
+
margin-bottom: 20px;
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
const InputWrapper = styled.div<{ isFocused: boolean }>`
|
|
151
|
+
position: relative;
|
|
152
|
+
flex-grow: 1;
|
|
153
|
+
display: flex;
|
|
127
154
|
align-items: center;
|
|
155
|
+
background-color: ${uiColors.raisinBlack};
|
|
156
|
+
border-radius: 24px;
|
|
157
|
+
padding: 8px 16px;
|
|
158
|
+
transition: all 0.3s ease;
|
|
159
|
+
box-shadow: ${({ isFocused }) =>
|
|
160
|
+
isFocused ? `0 0 0 2px ${uiColors.orange}` : 'none'};
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
const SearchIcon = styled.span`
|
|
164
|
+
color: ${uiColors.gray};
|
|
165
|
+
margin-right: 8px;
|
|
128
166
|
`;
|
|
129
167
|
|
|
130
168
|
const TextField = styled.input`
|
|
131
169
|
width: 100%;
|
|
132
|
-
background-color:
|
|
133
|
-
border: none
|
|
134
|
-
|
|
170
|
+
background-color: transparent;
|
|
171
|
+
border: none;
|
|
172
|
+
color: ${uiColors.white};
|
|
173
|
+
font-size: ${uiFonts.size.medium};
|
|
174
|
+
outline: none;
|
|
175
|
+
|
|
176
|
+
&::placeholder {
|
|
177
|
+
color: ${uiColors.gray};
|
|
178
|
+
}
|
|
135
179
|
`;
|
|
136
180
|
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
181
|
+
const ClearButton = styled.button`
|
|
182
|
+
background: none;
|
|
183
|
+
border: none;
|
|
184
|
+
color: ${uiColors.gray};
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
padding: 0;
|
|
187
|
+
display: flex;
|
|
188
|
+
align-items: center;
|
|
189
|
+
justify-content: center;
|
|
143
190
|
`;
|
|
144
191
|
|
|
145
|
-
const
|
|
192
|
+
const SearchButton = styled.button<{
|
|
193
|
+
buttonColor: string;
|
|
194
|
+
buttonBackgroundColor: string;
|
|
195
|
+
}>`
|
|
196
|
+
background-color: ${({ buttonBackgroundColor }) => buttonBackgroundColor};
|
|
197
|
+
color: ${({ buttonColor }) => buttonColor};
|
|
146
198
|
border: none;
|
|
147
|
-
|
|
148
|
-
|
|
199
|
+
border-radius: 24px;
|
|
200
|
+
padding: 8px 16px;
|
|
201
|
+
font-size: ${uiFonts.size.medium};
|
|
202
|
+
font-weight: bold;
|
|
203
|
+
cursor: pointer;
|
|
204
|
+
transition: all 0.3s ease;
|
|
205
|
+
|
|
206
|
+
&:hover {
|
|
207
|
+
background-color: ${uiColors.orange};
|
|
208
|
+
color: ${uiColors.white};
|
|
209
|
+
}
|
|
210
|
+
`;
|
|
211
|
+
|
|
212
|
+
const ResultsContainer = styled.div`
|
|
213
|
+
background-color: ${uiColors.raisinBlack};
|
|
214
|
+
border-radius: 8px;
|
|
215
|
+
padding: 16px;
|
|
216
|
+
`;
|
|
217
|
+
|
|
218
|
+
const ResultsTitle = styled.h3`
|
|
219
|
+
color: ${uiColors.white};
|
|
220
|
+
font-size: ${uiFonts.size.medium};
|
|
221
|
+
margin-bottom: 12px;
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
const ResultsList = styled.ul`
|
|
225
|
+
list-style-type: none;
|
|
149
226
|
padding: 0;
|
|
227
|
+
margin: 0;
|
|
150
228
|
`;
|
|
151
229
|
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
padding:
|
|
230
|
+
const ResultItem = styled.li`
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
padding: 8px;
|
|
234
|
+
cursor: pointer;
|
|
235
|
+
border-radius: 4px;
|
|
236
|
+
transition: all 0.2s ease;
|
|
156
237
|
|
|
157
238
|
&:hover {
|
|
158
|
-
color: #ff0;
|
|
159
239
|
background-color: ${uiColors.darkGray};
|
|
160
240
|
}
|
|
241
|
+
`;
|
|
161
242
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
243
|
+
const CharacterAvatar = styled.img`
|
|
244
|
+
width: 40px;
|
|
245
|
+
height: 40px;
|
|
246
|
+
border-radius: 50%;
|
|
247
|
+
margin-right: 12px;
|
|
248
|
+
object-fit: cover;
|
|
249
|
+
`;
|
|
250
|
+
|
|
251
|
+
const CharacterInfo = styled.div`
|
|
252
|
+
flex-grow: 1;
|
|
165
253
|
`;
|
|
166
254
|
|
|
255
|
+
const CharacterName = styled.div`
|
|
256
|
+
color: ${uiColors.white};
|
|
257
|
+
font-size: ${uiFonts.size.medium};
|
|
258
|
+
font-weight: bold;
|
|
259
|
+
margin-bottom: 4px;
|
|
260
|
+
`;
|
|
167
261
|
|
|
262
|
+
const CharacterDescription = styled.div`
|
|
263
|
+
color: ${uiColors.gray};
|
|
264
|
+
font-size: ${uiFonts.size.small};
|
|
265
|
+
`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ISpell } from '@rpg-engine/shared';
|
|
2
|
-
import React, { useState } from 'react';
|
|
2
|
+
import React, { useRef, useState } from 'react';
|
|
3
3
|
import { MobileSpellTooltip } from './MobileSpellTooltip';
|
|
4
4
|
import { MagicTooltip } from './SpellTooltip';
|
|
5
5
|
|
|
@@ -7,15 +7,32 @@ interface ISpellInfoWrapperProps {
|
|
|
7
7
|
spell: ISpell;
|
|
8
8
|
children: React.ReactNode;
|
|
9
9
|
scale?: number;
|
|
10
|
+
holdTouchDuration?: number; // New prop for hold touch duration
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export const SpellInfoWrapper: React.FC<ISpellInfoWrapperProps> = ({
|
|
13
14
|
children,
|
|
14
15
|
spell,
|
|
15
16
|
scale,
|
|
17
|
+
holdTouchDuration = 500, // Default hold duration to 500ms
|
|
16
18
|
}) => {
|
|
17
19
|
const [isTooltipVisible, setIsTooltipVisible] = useState(false);
|
|
18
20
|
const [isTooltipMobileVisible, setIsTooltipMobileVisible] = useState(false);
|
|
21
|
+
const touchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
22
|
+
|
|
23
|
+
const handleTouchStart = () => {
|
|
24
|
+
touchTimeoutRef.current = setTimeout(() => {
|
|
25
|
+
setIsTooltipMobileVisible(true);
|
|
26
|
+
setIsTooltipVisible(false);
|
|
27
|
+
}, holdTouchDuration);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const handleTouchEnd = () => {
|
|
31
|
+
if (touchTimeoutRef.current) {
|
|
32
|
+
clearTimeout(touchTimeoutRef.current);
|
|
33
|
+
touchTimeoutRef.current = null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
19
36
|
|
|
20
37
|
return (
|
|
21
38
|
<div
|
|
@@ -23,10 +40,8 @@ export const SpellInfoWrapper: React.FC<ISpellInfoWrapperProps> = ({
|
|
|
23
40
|
if (!isTooltipMobileVisible) setIsTooltipVisible(true);
|
|
24
41
|
}}
|
|
25
42
|
onMouseLeave={setIsTooltipVisible.bind(null, false)}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
setIsTooltipVisible(false);
|
|
29
|
-
}}
|
|
43
|
+
onTouchStart={handleTouchStart}
|
|
44
|
+
onTouchEnd={handleTouchEnd}
|
|
30
45
|
>
|
|
31
46
|
{children}
|
|
32
47
|
|
|
@@ -45,4 +60,4 @@ export const SpellInfoWrapper: React.FC<ISpellInfoWrapperProps> = ({
|
|
|
45
60
|
)}
|
|
46
61
|
</div>
|
|
47
62
|
);
|
|
48
|
-
};
|
|
63
|
+
};
|