@rpg-engine/long-bow 0.5.47 → 0.5.49

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.
@@ -0,0 +1,5 @@
1
+ /// <reference types="react" />
2
+ import type { Meta } from "@storybook/react";
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ export declare const Default: () => JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.5.47",
3
+ "version": "0.5.49",
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.9.13",
86
+ "@rpg-engine/shared": "^0.9.19",
87
87
  "dayjs": "^1.11.2",
88
88
  "font-awesome": "^4.7.0",
89
89
  "fs-extra": "^10.1.0",
@@ -4,7 +4,7 @@ import {
4
4
  IPrivateChatMessage,
5
5
  ITradeChatMessage,
6
6
  } from '@rpg-engine/shared';
7
- import React, { useEffect, useState } from 'react';
7
+ import React, { useState } from 'react';
8
8
  import { RxCross2, RxMagnifyingGlass } from 'react-icons/rx';
9
9
  import styled from 'styled-components';
10
10
  import { uiColors } from '../../constants/uiColors';
@@ -38,6 +38,9 @@ export interface IChatRevampProps {
38
38
  onRemoveRecentChatCharacter?: (character: PrivateChatCharacter) => void;
39
39
  unseenMessageCharacterIds?: string[];
40
40
  onSendTradeMessage: (message: string) => void;
41
+ searchCharacterUI: boolean;
42
+ hideSearchCharacterUI: () => void;
43
+ showSearchCharacterUI: () => void;
41
44
  }
42
45
 
43
46
  export const ChatRevamp = ({
@@ -60,14 +63,12 @@ export const ChatRevamp = ({
60
63
  onRemoveRecentChatCharacter,
61
64
  unseenMessageCharacterIds = [],
62
65
  onSendTradeMessage,
66
+ searchCharacterUI,
67
+ hideSearchCharacterUI,
68
+ showSearchCharacterUI,
63
69
  }: IChatRevampProps) => {
64
- const [showSearchCharacterUI, setShowSearchCharacterUI] = useState(true);
65
70
  const [showRecentChats, setShowRecentChats] = useState(false);
66
71
 
67
- useEffect(() => {
68
- setShowSearchCharacterUI(true);
69
- }, [activeTab]);
70
-
71
72
  const isPrivate = activeTab === 'private';
72
73
  const isTrade = activeTab === 'trade';
73
74
 
@@ -76,7 +77,7 @@ export const ChatRevamp = ({
76
77
  ) => {
77
78
  if (!onPreviousChatCharacterClick) return;
78
79
  onPreviousChatCharacterClick(character);
79
- setShowSearchCharacterUI(false);
80
+ hideSearchCharacterUI();
80
81
  };
81
82
 
82
83
  return (
@@ -107,9 +108,7 @@ export const ChatRevamp = ({
107
108
  <BurgerLineIcon></BurgerLineIcon>
108
109
  </BurgerIconContainer>
109
110
  {showRecentChats && (
110
- <SearchButton
111
- onPointerDown={() => setShowSearchCharacterUI(true)}
112
- >
111
+ <SearchButton onPointerDown={() => showSearchCharacterUI()}>
113
112
  <RxMagnifyingGlass size={16} color={uiColors.white} />
114
113
  </SearchButton>
115
114
  )}
@@ -141,14 +140,14 @@ export const ChatRevamp = ({
141
140
  ))}
142
141
  </RecentChatLogContainer>
143
142
  </RecentChatTabContainer>
144
- {isPrivate && showSearchCharacterUI ? (
143
+ {isPrivate && searchCharacterUI ? (
145
144
  <SearchCharacter
146
145
  onFocus={onFocus}
147
146
  onBlur={onBlur}
148
147
  onChangeCharacterName={onChangeCharacterName}
149
148
  styles={styles}
150
149
  recentCharacters={privateChatCharacters}
151
- setShowSearchCharacter={setShowSearchCharacterUI}
150
+ hideSearchCharacterUI={hideSearchCharacterUI}
152
151
  onCharacterClick={onCharacterClick}
153
152
  />
154
153
  ) : (
@@ -13,8 +13,8 @@ interface ISearchCharacterProps {
13
13
  onChangeCharacterName: (characterName: string) => void;
14
14
  styles?: IStyles;
15
15
  recentCharacters?: PrivateChatCharacter[];
16
- setShowSearchCharacter: (show: boolean) => void;
17
16
  onCharacterClick?: (character: PrivateChatCharacter) => void;
17
+ hideSearchCharacterUI: () => void;
18
18
  }
19
19
 
20
20
  export const SearchCharacter = ({
@@ -22,7 +22,7 @@ export const SearchCharacter = ({
22
22
  onBlur,
23
23
  onFocus,
24
24
  recentCharacters,
25
- setShowSearchCharacter,
25
+ hideSearchCharacterUI,
26
26
  onCharacterClick,
27
27
  styles = {
28
28
  textColor: '#c65102',
@@ -57,8 +57,8 @@ export const SearchCharacter = ({
57
57
  if (!onCharacterClick) return;
58
58
  setCharacterName('');
59
59
  onCharacterClick(character);
60
- setShowSearchCharacter(false);
61
- };
60
+ hideSearchCharacterUI();
61
+ };
62
62
 
63
63
  return (
64
64
  <SearchContainer>
@@ -0,0 +1,164 @@
1
+ import { ICharacter } from '@rpg-engine/shared';
2
+ import React, { useState } from 'react';
3
+ import styled from 'styled-components';
4
+ import { uiColors } from '../../constants/uiColors';
5
+ import { uiFonts } from '../../constants/uiFonts';
6
+ import { Button, ButtonTypes } from '../Button';
7
+ import { DraggableContainer } from '../DraggableContainer';
8
+ import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
9
+ import { SearchFriend } from './SearchFriend';
10
+
11
+ export type IFriend = Pick<ICharacter, 'name' | '_id' | 'isOnline'>;
12
+
13
+ interface IFriendListProps {
14
+ scale?: number;
15
+ friends: IFriend[];
16
+ friendRequests: IFriend[];
17
+ searchedCharacters: IFriend[];
18
+ onFocus?: () => void;
19
+ onBlur?: () => void;
20
+ onClose: () => void;
21
+ onOpenPrivateMessage: (character: IFriend) => void;
22
+ onRemoveFriend: (character: IFriend) => void;
23
+ onSendFriendRequest: (character: IFriend) => void;
24
+ onSearch: (characterName: string) => void;
25
+ onAcceptRequest: (character: IFriend) => void;
26
+ onRejectRequest: (character: IFriend) => void;
27
+ }
28
+
29
+ export const FriendList = (props: IFriendListProps) => {
30
+ const [isAddFriendUI, setIsAddFriendUI] = useState(false);
31
+ const {
32
+ scale,
33
+ friends,
34
+ friendRequests,
35
+ searchedCharacters,
36
+ onBlur,
37
+ onFocus,
38
+ onClose,
39
+ onSearch,
40
+ onAcceptRequest,
41
+ onSendFriendRequest,
42
+ onRemoveFriend,
43
+ onOpenPrivateMessage,
44
+ onRejectRequest,
45
+ } = props;
46
+ return (
47
+ <DraggableContainer
48
+ type={RPGUIContainerTypes.Framed}
49
+ onCloseButton={() => {
50
+ if (onClose) onClose();
51
+ }}
52
+ width="800px"
53
+ cancelDrag="#FriendListContainer, .rpgui-dropdown-imp, input, .empty-slot, button"
54
+ scale={scale}
55
+ >
56
+ {isAddFriendUI ? (
57
+ <SearchFriend
58
+ searchedCharacters={searchedCharacters}
59
+ onSendFriendRequest={onSendFriendRequest}
60
+ onAcceptRequest={onAcceptRequest}
61
+ onRejectRequest={onRejectRequest}
62
+ friendRequests={friendRequests}
63
+ onSearch={onSearch}
64
+ onBlur={onBlur}
65
+ onFocus={onFocus}
66
+ />
67
+ ) : (
68
+ <ListContainer>
69
+ <FriendsTitle>Friends</FriendsTitle>
70
+ {friends.map(friend => (
71
+ <ListElement key={friend._id}>
72
+ <div>
73
+ <IsOnlineBadge $isOnline={friend.isOnline} />
74
+ {friend.name}
75
+ </div>
76
+
77
+ <MessageAndDeleteContainer>
78
+ <Button
79
+ buttonType={ButtonTypes.RPGUIButton}
80
+ onPointerDown={() => onOpenPrivateMessage(friend)}
81
+ >
82
+ Message
83
+ </Button>
84
+ <Button
85
+ buttonType={ButtonTypes.RPGUIButton}
86
+ onPointerDown={() => onRemoveFriend(friend)}
87
+ >
88
+ Remove
89
+ </Button>
90
+ </MessageAndDeleteContainer>
91
+ </ListElement>
92
+ ))}
93
+ </ListContainer>
94
+ )}
95
+
96
+ <ButtonContainer>
97
+ <Button
98
+ buttonType={ButtonTypes.RPGUIButton}
99
+ onClick={() => setIsAddFriendUI(t => !t)}
100
+ >
101
+ {isAddFriendUI ? 'Back' : 'Add New Friend'}
102
+ </Button>
103
+ </ButtonContainer>
104
+ </DraggableContainer>
105
+ );
106
+ };
107
+
108
+ const FriendsTitle = styled.h2`
109
+ font-size: ${uiFonts.size.medium};
110
+ margin: 0;
111
+ padding: 0;
112
+ text-align: center;
113
+ `;
114
+
115
+ const ListContainer = styled.ul`
116
+ border: none;
117
+ overflow-y: scroll;
118
+ list-style: none;
119
+ padding: 0;
120
+ width: 100%;
121
+ height: 50vh;
122
+
123
+ @media (max-width: 768px) {
124
+ max-height: 90wh;
125
+ }
126
+ `;
127
+
128
+ const ListElement = styled.li`
129
+ margin: 0.5rem 0 !important;
130
+ font-size: ${uiFonts.size.small};
131
+ padding: 0.5rem 4px;
132
+ display: flex;
133
+ align-items: center;
134
+ justify-content: space-between;
135
+ border-radius: 4px;
136
+
137
+ & div {
138
+ display: flex;
139
+ align-items: center;
140
+ }
141
+ `;
142
+
143
+ const IsOnlineBadge = styled.div<{ $isOnline: boolean }>`
144
+ width: 10px;
145
+ height: 10px;
146
+ border-radius: 50%;
147
+ background-color: ${({ $isOnline }) =>
148
+ $isOnline ? uiColors.lightGreen : uiColors.red};
149
+ margin-right: 1rem;
150
+ `;
151
+
152
+ const ButtonContainer = styled.div`
153
+ display: flex;
154
+ justify-content: center;
155
+ align-items: center;
156
+ width: 100%;
157
+ margin-top: 1rem;
158
+ `;
159
+
160
+ const MessageAndDeleteContainer = styled.div`
161
+ display: flex;
162
+ justify-content: space-between;
163
+ gap: 0.5rem;
164
+ `;
@@ -0,0 +1,157 @@
1
+ import React, { useState } from 'react';
2
+ import styled from 'styled-components';
3
+ import { uiFonts } from '../../constants/uiFonts';
4
+ import { Button, ButtonTypes } from '../Button';
5
+ import { Column } from '../shared/Column';
6
+ import type { IFriend } from './FriendList';
7
+
8
+ interface ISearchFriendProp {
9
+ searchedCharacters: IFriend[];
10
+ friendRequests: IFriend[];
11
+ onFocus?: () => void;
12
+ onBlur?: () => void;
13
+ onSearch: (characterName: string) => void;
14
+ onSendFriendRequest: (character: IFriend) => void;
15
+ onAcceptRequest: (character: IFriend) => void;
16
+ onRejectRequest: (character: IFriend) => void;
17
+ }
18
+
19
+ export const SearchFriend = (props: ISearchFriendProp) => {
20
+ const {
21
+ searchedCharacters,
22
+ friendRequests,
23
+ onBlur,
24
+ onFocus,
25
+ onSearch,
26
+ onSendFriendRequest,
27
+ onAcceptRequest,
28
+ onRejectRequest,
29
+ } = props;
30
+ const [characterName, setCharacterName] = useState('');
31
+
32
+ const handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
33
+ event.preventDefault();
34
+ if (!characterName || characterName.trim() === '') return;
35
+ onSearch(characterName);
36
+ };
37
+
38
+ return (
39
+ <>
40
+ <Form onSubmit={handleSubmit}>
41
+ <Column flex={70}>
42
+ <TextField
43
+ value={characterName}
44
+ id="characterName"
45
+ name="characterName"
46
+ onChange={e => {
47
+ setCharacterName(e.target.value);
48
+ }}
49
+ height={20}
50
+ type="text"
51
+ autoComplete="off"
52
+ onFocus={onFocus}
53
+ onBlur={onBlur}
54
+ onPointerDown={onFocus}
55
+ autoFocus
56
+ />
57
+ </Column>
58
+ <Column justifyContent="flex-end">
59
+ <Button type="submit" buttonType={ButtonTypes.RPGUIButton}>
60
+ Search
61
+ </Button>
62
+ </Column>
63
+ </Form>
64
+ <ListContainer>
65
+ {searchedCharacters.map(character => (
66
+ <ListElement key={character._id}>
67
+ <p>{character.name}</p>
68
+
69
+ <Button
70
+ buttonType={ButtonTypes.RPGUIButton}
71
+ onPointerDown={() => onSendFriendRequest(character)}
72
+ >
73
+ Add Friend
74
+ </Button>
75
+ </ListElement>
76
+ ))}
77
+ </ListContainer>
78
+ <FriendRequestTitle>
79
+ Friend Requests <span>({friendRequests.length})</span>
80
+ </FriendRequestTitle>
81
+ {friendRequests.length > 0 && (
82
+ <ListContainer>
83
+ {friendRequests.map(character => (
84
+ <ListElement key={character._id}>
85
+ <p>{character.name}</p>
86
+
87
+ <AcceptRejectButtonContainer>
88
+ <Button
89
+ buttonType={ButtonTypes.RPGUIButton}
90
+ onPointerDown={() => onAcceptRequest(character)}
91
+ >
92
+ Accept
93
+ </Button>
94
+ <Button
95
+ buttonType={ButtonTypes.RPGUIButton}
96
+ onPointerDown={() => onRejectRequest(character)}
97
+ >
98
+ Reject
99
+ </Button>
100
+ </AcceptRejectButtonContainer>
101
+ </ListElement>
102
+ ))}
103
+ </ListContainer>
104
+ )}
105
+ </>
106
+ );
107
+ };
108
+
109
+ const Form = styled.form`
110
+ display: flex;
111
+ width: 100%;
112
+ justify-content: center;
113
+ align-items: center;
114
+ margin-top: 1rem;
115
+ `;
116
+
117
+ const TextField = styled.input`
118
+ width: 100%;
119
+ background-color: rgba(0, 0, 0, 0.25) !important;
120
+ border: none !important;
121
+ max-height: 28px !important;
122
+ `;
123
+
124
+ const ListContainer = styled.ul`
125
+ width: 100%;
126
+ c4height: 50vh;
127
+ border: none;
128
+ overflow-y: scroll;
129
+ list-style: none;
130
+ padding: 0;
131
+
132
+ @media (max-width: 768px) {
133
+ max-height: 90wh;
134
+ }
135
+ `;
136
+
137
+ const ListElement = styled.li`
138
+ margin: 0.5rem 0 !important;
139
+ font-size: ${uiFonts.size.small};
140
+ padding: 0.5rem 2px;
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: space-between;
144
+ `;
145
+
146
+ const FriendRequestTitle = styled.h3`
147
+ font-size: ${uiFonts.size.small} !important;
148
+ margin-top: 0.5rem !important;
149
+ text-align: center;
150
+ gap: 0.5rem;
151
+ `;
152
+
153
+ const AcceptRejectButtonContainer = styled.div`
154
+ display: flex;
155
+ justify-content: space-between;
156
+ gap: 0.5rem;
157
+ `;
@@ -46,6 +46,7 @@ const skillProps = {
46
46
  color: uiColors.blue,
47
47
  values: {
48
48
  fishing: 'foods/fish.png',
49
+ farming: 'foods/cabbage.png',
49
50
  mining: 'crafting-resources/iron-ingot.png',
50
51
  lumberjacking: 'crafting-resources/greater-wooden-log.png',
51
52
  blacksmithing: 'hammers/hammer.png',
@@ -74,6 +75,7 @@ const skillNameMap: SkillMap = {
74
75
  shielding: 'Shielding',
75
76
  dagger: 'Dagger',
76
77
  fishing: 'Fishing',
78
+ farming: 'Farming',
77
79
  mining: 'Mining',
78
80
  lumberjacking: 'Lumberjacking',
79
81
  blacksmithing: 'Blacksmithing',
package/src/index.tsx CHANGED
@@ -10,6 +10,7 @@ export * from './components/DraggableContainer';
10
10
  export * from './components/Dropdown';
11
11
  export * from './components/DropdownSelectorContainer';
12
12
  export * from './components/Equipment/EquipmentSet';
13
+ export * from './components/Friends/FriendList';
13
14
  export * from './components/HistoryDialog';
14
15
  export * from './components/ImageCarousel/ImageCarousel';
15
16
  export * from './components/Input';
@@ -13,7 +13,7 @@ export const skillMock = {
13
13
  level: 2,
14
14
  skillPoints: 22,
15
15
  skillPointsToNextLevel: getSPForLevel(2.4),
16
- buffAndDebuff: -10
16
+ buffAndDebuff: -10,
17
17
  },
18
18
  magicResistance: {
19
19
  type: SkillType.BasicAttributes,
@@ -88,6 +88,12 @@ export const skillMock = {
88
88
  skillPoints: 5,
89
89
  skillPointsToNextLevel: 80,
90
90
  },
91
+ farming: {
92
+ type: SkillType.Gathering,
93
+ level: 1,
94
+ skillPoints: 5,
95
+ skillPointsToNextLevel: 80,
96
+ },
91
97
  mining: {
92
98
  type: SkillType.Crafting,
93
99
  level: 1,
@@ -0,0 +1,46 @@
1
+ import type { Meta } from "@storybook/react";
2
+ import React from 'react';
3
+ import { FriendList } from "../components/Friends/FriendList";
4
+ import { RPGUIRoot } from "../components/RPGUI/RPGUIRoot";
5
+
6
+ const meta :Meta = {
7
+ title: 'Friend System/Friend List',
8
+ component: FriendList
9
+ };
10
+
11
+ export default meta;
12
+
13
+
14
+ const friends = Array.from({ length: 15 }, (_, i) => ({
15
+ _id: i.toString(),
16
+ name: `Friend ${i}`,
17
+ isOnline: i % 2 === 0,
18
+ }));
19
+
20
+ const characters = Array.from({ length: 1 }, (_, i) => ({
21
+ _id: i.toString(),
22
+ name: `Character ${i}`,
23
+ isOnline: i % 2 === 0,
24
+ }));
25
+
26
+ const Template = () => {
27
+ return <RPGUIRoot>
28
+ <FriendList
29
+ friends={friends}
30
+ friendRequests={characters}
31
+ searchedCharacters={characters}
32
+ onClose={() => console.log('close')}
33
+ onOpenPrivateMessage={(character) => console.log(character)}
34
+ onRemoveFriend={(character) => console.log(character)}
35
+ onSendFriendRequest={(character) => console.log(character)}
36
+ onSearch={(characterName) => console.log(characterName)}
37
+ onAcceptRequest={(character) => console.log(character)}
38
+ onRejectRequest={(character) => console.log(character)}
39
+ onBlur={() => console.log('blur')}
40
+ onFocus={() => console.log('focus')}
41
+ />
42
+ </RPGUIRoot>;
43
+ }
44
+
45
+
46
+ export const Default = Template.bind({});