@rpg-engine/long-bow 0.7.83 → 0.7.84

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.7.83",
3
+ "version": "0.7.84",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -0,0 +1,62 @@
1
+ import { UserAccountTypes } from '@rpg-engine/shared';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+
5
+ interface IPremiumLabelProps {
6
+ accountType: UserAccountTypes;
7
+ }
8
+
9
+ const PremiumLabel: React.FC<IPremiumLabelProps> = ({ accountType }) => {
10
+ if (accountType === UserAccountTypes.Free) return null;
11
+
12
+ const getBackgroundColor = () => {
13
+ switch (accountType) {
14
+ case UserAccountTypes.PremiumBronze:
15
+ return '#CD7F32';
16
+ case UserAccountTypes.PremiumSilver:
17
+ return '#C0C0C0';
18
+ case UserAccountTypes.PremiumGold:
19
+ return '#FFD700';
20
+ case UserAccountTypes.PremiumUltimate:
21
+ return '#002E99';
22
+ default:
23
+ return 'transparent';
24
+ }
25
+ };
26
+
27
+ const getShortLabel = () => {
28
+ switch (accountType) {
29
+ case UserAccountTypes.PremiumBronze:
30
+ return 'Bronze PA';
31
+ case UserAccountTypes.PremiumSilver:
32
+ return 'Silver PA';
33
+ case UserAccountTypes.PremiumGold:
34
+ return 'Gold PA';
35
+ case UserAccountTypes.PremiumUltimate:
36
+ return 'Ultimate PA';
37
+ default:
38
+ return '';
39
+ }
40
+ };
41
+
42
+ return (
43
+ <StyledLabel backgroundColor={getBackgroundColor()}>
44
+ {getShortLabel()}
45
+ </StyledLabel>
46
+ );
47
+ };
48
+
49
+ const StyledLabel = styled.span<{ backgroundColor: string }>`
50
+ background-color: ${({ backgroundColor }) => backgroundColor};
51
+ color: ${({ backgroundColor }) =>
52
+ backgroundColor === '#002E99' ? 'white' : 'black'};
53
+ font-weight: bold;
54
+ padding: 0 0.4rem;
55
+ border-radius: 3px;
56
+ margin-right: 3px;
57
+ margin-bottom: 2px;
58
+ display: inline-block;
59
+ font-size: 0.5rem !important;
60
+ `;
61
+
62
+ export default PremiumLabel;
@@ -2,7 +2,6 @@ import {
2
2
  getItemTextureKeyPath,
3
3
  IEquipmentSet,
4
4
  ITradeResponseItem,
5
- UserAccountTypes,
6
5
  } from '@rpg-engine/shared';
7
6
  import capitalize from 'lodash/capitalize';
8
7
  import React, { useEffect, useState } from 'react';
@@ -13,6 +12,8 @@ import SelectArrow from '../Arrow/SelectArrow';
13
12
  import { ItemInfoWrapper } from '../Item/Cards/ItemInfoWrapper';
14
13
  import { Ellipsis } from '../shared/Ellipsis';
15
14
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
15
+ import PremiumLabel from './PremiumLabel';
16
+
16
17
  export interface ITradeComponentProps {
17
18
  traderItem: ITradeResponseItem;
18
19
  onQuantityChange: (
@@ -76,40 +77,9 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
76
77
 
77
78
  const renderAccountTypeIndicator = () => {
78
79
  if (isBuy && traderItem.canBePurchasedOnlyByPremiumPlans) {
79
- return traderItem.canBePurchasedOnlyByPremiumPlans.map(accountType => {
80
- if (accountType !== UserAccountTypes.Free) {
81
- let backgroundColor;
82
- let textColor = 'black';
83
-
84
- switch (accountType) {
85
- case UserAccountTypes.PremiumBronze:
86
- backgroundColor = '#CD7F32';
87
- break;
88
- case UserAccountTypes.PremiumSilver:
89
- backgroundColor = '#C0C0C0';
90
- break;
91
- case UserAccountTypes.PremiumGold:
92
- backgroundColor = '#FFD700';
93
- break;
94
- case UserAccountTypes.PremiumUltimate:
95
- backgroundColor = '#002E99';
96
- break;
97
- default:
98
- return null;
99
- }
100
-
101
- return (
102
- <PremiumLabel
103
- backgroundColor={backgroundColor}
104
- textColor={textColor}
105
- key={accountType}
106
- >
107
- {capitalize(accountType) + ' PA'}
108
- </PremiumLabel>
109
- );
110
- }
111
- return null;
112
- });
80
+ return traderItem.canBePurchasedOnlyByPremiumPlans.map(accountType => (
81
+ <PremiumLabel key={accountType} accountType={accountType} />
82
+ ));
113
83
  }
114
84
  return null;
115
85
  };
@@ -137,7 +107,7 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
137
107
  },
138
108
  atlasJSON
139
109
  )}
140
- imgScale={2.5}
110
+ imgScale={2}
141
111
  />
142
112
  </ItemInfoWrapper>
143
113
  </SpriteContainer>
@@ -146,7 +116,7 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
146
116
  <ItemNameContainer>
147
117
  <NameValue>
148
118
  <p>
149
- <Ellipsis maxLines={1} maxWidth="250px">
119
+ <Ellipsis maxLines={1} maxWidth="180px">
150
120
  {capitalize(traderItem.name)}
151
121
  </Ellipsis>
152
122
  </p>
@@ -156,17 +126,17 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
156
126
  </ItemNameContainer>
157
127
  <QuantityContainer>
158
128
  <SelectArrow
159
- size={32}
129
+ size={24}
160
130
  className="arrow-selector"
161
131
  direction="left"
162
- onPointerDown={onLeftClick.bind(null, outerQty)}
163
- scale={0.8}
132
+ onPointerDown={() => onLeftClick(outerQty)}
133
+ scale={0.7}
164
134
  />
165
135
  <StyledArrow
166
- size={32}
136
+ size={24}
167
137
  className="arrow-selector"
168
138
  direction="left"
169
- onPointerDown={onLeftClick}
139
+ onPointerDown={() => onLeftClick()}
170
140
  />
171
141
  <QuantityInput
172
142
  type="text"
@@ -175,107 +145,85 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
175
145
  onBlur={handleBlur}
176
146
  />
177
147
  <StyledArrow
178
- size={32}
148
+ size={24}
179
149
  className="arrow-selector"
180
150
  direction="right"
181
- onPointerDown={onRightClick}
151
+ onPointerDown={() => onRightClick()}
182
152
  />
183
153
  <SelectArrow
184
- size={32}
154
+ size={24}
185
155
  className="arrow-selector"
186
156
  direction="right"
187
- onPointerDown={onRightClick.bind(null, outerQty)}
188
- scale={0.8}
157
+ onPointerDown={() => onRightClick(outerQty)}
158
+ scale={0.7}
189
159
  />
190
160
  </QuantityContainer>
191
161
  </ItemWrapper>
192
162
  );
193
163
  };
194
164
 
195
- const PremiumLabel = styled.span<{
196
- backgroundColor: string;
197
- textColor: string;
198
- }>`
199
- background-color: ${({ backgroundColor }) => backgroundColor};
200
- color: ${({ textColor }) => textColor};
201
- font-weight: bold;
202
- padding: 2px 5px;
203
- border-radius: 5px;
204
- margin-right: 5px;
205
- margin-bottom: 5px;
206
- display: inline-block;
207
- `;
208
-
209
165
  const StyledArrow = styled(SelectArrow)`
210
- margin: 40px;
166
+ margin: 0 1.5rem;
211
167
  `;
212
168
 
213
169
  const ItemWrapper = styled.div`
214
170
  width: 100%;
215
- margin: auto;
216
171
  display: flex;
217
172
  justify-content: space-between;
218
- margin-bottom: 1rem;
173
+ margin-bottom: 0.5rem;
174
+ padding: 0.25rem;
219
175
 
220
176
  &:hover {
221
177
  background-color: ${uiColors.darkGray};
222
178
  }
223
- padding: 0.5rem;
224
179
  `;
225
180
 
226
181
  const ItemNameContainer = styled.div`
227
182
  flex: 60%;
183
+ display: flex;
184
+ align-items: center;
228
185
  `;
229
186
 
230
187
  const ItemIconContainer = styled.div`
231
188
  display: flex;
232
189
  justify-content: flex-start;
233
190
  align-items: center;
234
-
235
- flex: 0 0 58px;
191
+ flex: 0 0 40px;
236
192
  `;
237
193
 
238
194
  const SpriteContainer = styled.div`
239
195
  position: relative;
240
196
  top: -0.5rem;
241
- left: 0.5rem;
197
+ left: 0;
242
198
  `;
243
199
 
244
200
  const NameValue = styled.div`
245
201
  p {
246
- font-size: 0.75rem;
202
+ font-size: 0.6rem;
247
203
  margin: 0;
204
+ line-height: 1.2;
248
205
  }
249
206
  `;
250
207
 
251
- interface IContainerProps {
252
- percentageWidth?: number;
253
- minWidth?: number;
254
- style?: Record<string, any>;
255
- }
256
-
257
- const QuantityContainer = styled.div<IContainerProps>`
258
- position: relative;
208
+ const QuantityContainer = styled.div`
259
209
  display: flex;
260
-
261
- min-width: 100px;
210
+ min-width: 90px;
262
211
  width: 40%;
263
- justify-content: center;
212
+ justify-content: flex-end;
264
213
  align-items: center;
265
-
266
- flex: 40%;
214
+ flex: 30%;
215
+ gap: 2px;
216
+ position: relative;
267
217
  `;
268
218
 
269
219
  const QuantityInput = styled.input`
270
- width: 40px;
220
+ width: 30px;
271
221
  text-align: center;
272
222
  background-color: ${uiColors.darkGray};
273
223
  color: white;
274
- border: 1px solid ${uiColors.lightGray};
275
- padding: 2px;
276
- font-size: ${uiFonts.size.small};
277
224
  border: none;
278
-
225
+ padding: 1px;
226
+ font-size: ${uiFonts.size.small};
279
227
  position: relative;
280
- left: -3px;
228
+ right: 4px;
281
229
  `;
@@ -4,12 +4,13 @@ import {
4
4
  ITradeResponseItem,
5
5
  TradeTransactionType,
6
6
  } from '@rpg-engine/shared';
7
- import React, { useState } from 'react';
7
+ import React from 'react';
8
8
  import styled from 'styled-components';
9
9
  import { Button, ButtonTypes } from '../Button';
10
10
  import { DraggableContainer } from '../DraggableContainer';
11
11
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
12
12
  import { TradingItemRow } from './TradingItemRow';
13
+ import { useTradingGold } from './useTradingGold';
13
14
 
14
15
  export interface ITradingMenu {
15
16
  traderItems: ITradeResponseItem[];
@@ -34,187 +35,127 @@ export const TradingMenu: React.FC<ITradingMenu> = ({
34
35
  equipmentSet,
35
36
  scale,
36
37
  }) => {
37
- const [sum, setSum] = useState(0);
38
- const [qtyMap, setQtyMap] = useState(new Map());
39
-
40
- const onQuantityChange = (item: ITradeResponseItem, selectedQty: number) => {
41
- setQtyMap(new Map(qtyMap.set(item.key, selectedQty)));
42
-
43
- let newSum = 0;
44
- traderItems.forEach(item => {
45
- const qty = qtyMap.get(item.key);
46
- if (qty) newSum += qty * item.price;
47
- setSum(newSum);
48
- });
49
- };
50
-
51
- const isBuy = () => {
52
- return type == 'buy';
53
- };
54
-
55
- const hasGoldForSale = () => {
56
- if (isBuy()) {
57
- return !(sum > characterAvailableGold);
58
- }
59
- return true;
60
- };
61
-
62
- const getFinalGold = () => {
63
- if (isBuy()) {
64
- return characterAvailableGold - sum;
65
- } else {
66
- return characterAvailableGold + sum;
67
- }
68
- };
69
-
70
- const Capitalize = (word: string) => {
71
- return word[0].toUpperCase() + word.substring(1);
72
- };
73
-
74
- const onConfirmClick = () => {
75
- const items: ITradeRequestItem[] = [];
76
-
77
- traderItems.forEach(item => {
78
- const qty = qtyMap.get(item.key);
79
- if (qty) {
80
- items.push(Object.assign({}, item, { qty: qty }));
81
- }
82
- });
83
-
84
- onConfirm(items);
85
- };
38
+ const {
39
+ sum,
40
+ qtyMap,
41
+ isBuy,
42
+ hasGoldForSale,
43
+ getFinalGold,
44
+ getTradeItems,
45
+ updateQuantity,
46
+ } = useTradingGold({ characterAvailableGold, type, traderItems });
86
47
 
87
48
  return (
88
49
  <DraggableContainer
89
50
  type={RPGUIContainerTypes.Framed}
90
- onCloseButton={() => {
91
- if (onClose) onClose();
92
- }}
93
- width="600px"
51
+ onCloseButton={onClose}
52
+ width="500px"
94
53
  cancelDrag="#TraderContainer"
95
54
  scale={scale}
96
55
  >
97
- <>
98
- <div style={{ width: '100%' }}>
99
- <Title>{Capitalize(type)} Menu</Title>
100
- <hr className="golden" />
101
- </div>
102
- <TradingComponentScrollWrapper id="TraderContainer">
56
+ <Container>
57
+ <Title>{type.charAt(0).toUpperCase() + type.slice(1)} Menu</Title>
58
+ <hr className="golden" />
59
+
60
+ <ScrollWrapper id="TraderContainer">
103
61
  {traderItems.map((tradeItem, index) => (
104
- <ItemWrapper key={`${tradeItem.key}_${index}`}>
105
- <TradingItemRow
106
- atlasIMG={atlasIMG}
107
- atlasJSON={atlasJSON}
108
- onQuantityChange={onQuantityChange}
109
- traderItem={tradeItem}
110
- selectedQty={qtyMap.get(tradeItem.key) ?? 0}
111
- equipmentSet={equipmentSet}
112
- scale={scale}
113
- isBuy={isBuy()}
114
- />
115
- </ItemWrapper>
62
+ <TradingItemRow
63
+ key={`${tradeItem.key}_${index}`}
64
+ atlasIMG={atlasIMG}
65
+ atlasJSON={atlasJSON}
66
+ onQuantityChange={updateQuantity}
67
+ traderItem={tradeItem}
68
+ selectedQty={qtyMap.get(tradeItem.key) ?? 0}
69
+ equipmentSet={equipmentSet}
70
+ scale={scale}
71
+ isBuy={isBuy()}
72
+ />
116
73
  ))}
117
- </TradingComponentScrollWrapper>
118
- <GoldWrapper>
119
- <p>Available Gold:</p>
120
- <p>${characterAvailableGold.toFixed(2)}</p>
121
- </GoldWrapper>
122
- <TotalWrapper>
123
- <p>Total:</p>
124
- <p>${sum}</p>
125
- </TotalWrapper>
126
- {!hasGoldForSale() ? (
127
- <AlertWrapper>
128
- <p> Sorry, not enough money.</p>
129
- </AlertWrapper>
130
- ) : (
131
- <GoldWrapper>
132
- <p>Final Gold:</p>
133
- <p>${getFinalGold().toFixed(2)}</p>
134
- </GoldWrapper>
135
- )}
74
+ </ScrollWrapper>
75
+
76
+ <InfoSection>
77
+ <GoldInfo>
78
+ <p>Available Gold:</p>
79
+ <p>${characterAvailableGold.toFixed(2)}</p>
80
+ </GoldInfo>
81
+ <GoldInfo>
82
+ <p>Total:</p>
83
+ <p>${sum}</p>
84
+ </GoldInfo>
85
+ {!hasGoldForSale() ? (
86
+ <AlertText>Sorry, not enough money.</AlertText>
87
+ ) : (
88
+ <GoldInfo>
89
+ <p>Final Gold:</p>
90
+ <p>${getFinalGold().toFixed(2)}</p>
91
+ </GoldInfo>
92
+ )}
93
+ </InfoSection>
136
94
 
137
95
  <ButtonWrapper>
138
96
  <Button
139
97
  buttonType={ButtonTypes.RPGUIButton}
140
98
  disabled={!hasGoldForSale()}
141
- onPointerDown={() => onConfirmClick()}
99
+ onPointerDown={() => onConfirm(getTradeItems())}
142
100
  >
143
101
  Confirm
144
102
  </Button>
145
- <Button
146
- buttonType={ButtonTypes.RPGUIButton}
147
- onPointerDown={() => onClose()}
148
- >
103
+ <Button buttonType={ButtonTypes.RPGUIButton} onPointerDown={onClose}>
149
104
  Cancel
150
105
  </Button>
151
106
  </ButtonWrapper>
152
- </>
107
+ </Container>
153
108
  </DraggableContainer>
154
109
  );
155
110
  };
156
111
 
112
+ const Container = styled.div`
113
+ width: 100%;
114
+ `;
115
+
157
116
  const Title = styled.h1`
158
- z-index: 22;
159
- font-size: 0.6rem;
117
+ font-size: 0.7rem !important;
160
118
  color: yellow !important;
119
+ text-align: center;
161
120
  `;
162
121
 
163
- const TradingComponentScrollWrapper = styled.div`
122
+ const ScrollWrapper = styled.div`
164
123
  overflow-y: scroll;
165
- height: 390px;
124
+ height: 250px;
166
125
  width: 100%;
167
- margin-top: 1rem;
126
+ margin-top: 0.3rem;
127
+ overflow-x: hidden;
128
+ padding: 0 0.3rem;
168
129
  `;
169
130
 
170
- const ItemWrapper = styled.div`
171
- margin: auto;
172
- display: flex;
173
- justify-content: space-between;
131
+ const InfoSection = styled.div`
132
+ margin-top: 0.3rem;
133
+ padding: 0 0.5rem;
174
134
  `;
175
135
 
176
- const TotalWrapper = styled.div`
177
- margin-top: 1rem;
136
+ const GoldInfo = styled.div`
178
137
  display: flex;
179
138
  justify-content: space-between;
180
- height: 20px;
181
- p {
182
- color: white !important;
183
- }
139
+ height: 16px;
184
140
  width: 100%;
185
- margin-left: 0.8rem;
186
- `;
187
-
188
- const GoldWrapper = styled.div`
189
- margin-top: 1rem;
190
- display: flex;
191
- justify-content: space-between;
192
- height: 20px;
141
+ margin: 0.5rem 0;
193
142
  p {
194
143
  color: yellow !important;
144
+ margin: 0;
145
+ font-size: 0.6rem;
195
146
  }
196
- width: 100%;
197
- margin-left: 0.8rem;
198
147
  `;
199
148
 
200
- const AlertWrapper = styled.div`
201
- margin-top: 1rem;
202
- display: flex;
203
- width: 100%;
204
- justify-content: center;
205
- height: 20px;
206
- p {
207
- color: red !important;
208
- }
149
+ const AlertText = styled.p`
150
+ color: red !important;
151
+ text-align: center;
152
+ margin: 0.3rem 0;
153
+ font-size: 0.5rem;
209
154
  `;
210
155
 
211
156
  const ButtonWrapper = styled.div`
212
157
  display: flex;
213
158
  justify-content: space-around;
214
- padding-top: 20px;
215
159
  width: 100%;
216
- margin-top: 1rem;
217
- button {
218
- padding: 0px 50px;
219
- }
160
+ margin-top: 0.5rem;
220
161
  `;
@@ -0,0 +1,64 @@
1
+ import {
2
+ ITradeRequestItem,
3
+ ITradeResponseItem,
4
+ TradeTransactionType,
5
+ } from '@rpg-engine/shared';
6
+ import { useState } from 'react';
7
+
8
+ interface ITradingGoldHook {
9
+ characterAvailableGold: number;
10
+ type: TradeTransactionType;
11
+ traderItems: ITradeResponseItem[];
12
+ }
13
+
14
+ export const useTradingGold = ({
15
+ characterAvailableGold,
16
+ type,
17
+ traderItems,
18
+ }: ITradingGoldHook) => {
19
+ const [sum, setSum] = useState(0);
20
+ const [qtyMap, setQtyMap] = useState(new Map<string, number>());
21
+
22
+ const isBuy = () => type === 'buy';
23
+
24
+ const hasGoldForSale = () =>
25
+ isBuy() ? !(sum > characterAvailableGold) : true;
26
+
27
+ const getFinalGold = () =>
28
+ isBuy() ? characterAvailableGold - sum : characterAvailableGold + sum;
29
+
30
+ const getTradeItems = (): ITradeRequestItem[] => {
31
+ const items: ITradeRequestItem[] = [];
32
+ traderItems.forEach(item => {
33
+ const qty = qtyMap.get(item.key);
34
+ if (qty) {
35
+ items.push({
36
+ ...item,
37
+ qty,
38
+ });
39
+ }
40
+ });
41
+ return items;
42
+ };
43
+
44
+ const updateQuantity = (item: ITradeResponseItem, selectedQty: number) => {
45
+ setQtyMap(new Map(qtyMap.set(item.key, selectedQty)));
46
+
47
+ let newSum = 0;
48
+ qtyMap.forEach((qty, key) => {
49
+ const item = traderItems.find(i => i.key === key);
50
+ if (item) newSum += qty * item.price;
51
+ });
52
+ setSum(newSum);
53
+ };
54
+
55
+ return {
56
+ sum,
57
+ qtyMap,
58
+ isBuy,
59
+ hasGoldForSale,
60
+ getFinalGold,
61
+ getTradeItems,
62
+ updateQuantity,
63
+ };
64
+ };