@rpg-engine/long-bow 0.8.203 → 0.8.205
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/Item/Inventory/ErrorBoundary.d.ts +2 -0
- package/dist/components/Marketplace/BuyOrderPanel.d.ts +2 -2
- package/dist/components/Marketplace/BuyOrderRows.d.ts +4 -4
- package/dist/components/Marketplace/HistoryPanel.d.ts +2 -2
- package/dist/components/Marketplace/MarketplaceRows.d.ts +2 -0
- package/dist/components/shared/MMORPGNumber.d.ts +6 -0
- package/dist/long-bow.cjs.development.js +465 -34317
- 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 +466 -34318
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/utils/numberUtils.d.ts +7 -0
- package/package.json +1 -1
- package/src/components/DCWallet/DCWalletContent.tsx +5 -3
- package/src/components/Item/Inventory/ErrorBoundary.tsx +25 -8
- package/src/components/Item/Inventory/ItemPropertyColorSelector.tsx +3 -1
- package/src/components/Marketplace/BuyOrderPanel.tsx +2 -2
- package/src/components/Marketplace/BuyOrderRows.tsx +13 -7
- package/src/components/Marketplace/CharacterDetailModal.tsx +99 -17
- package/src/components/Marketplace/CharacterListingModal.tsx +2 -1
- package/src/components/Marketplace/GroupedRowContainer.tsx +10 -10
- package/src/components/Marketplace/HistoryPanel.tsx +5 -5
- package/src/components/Marketplace/MarketplaceBuyModal.tsx +4 -4
- package/src/components/Marketplace/MarketplaceRows.tsx +13 -7
- package/src/components/Marketplace/__test__/CharacterDetailModal.spec.tsx +137 -0
- package/src/components/Store/CartView.tsx +2 -1
- package/src/components/Store/FeaturedBanner.tsx +1 -0
- package/src/components/Store/PaymentMethodModal.tsx +11 -3
- package/src/components/Store/StoreCharacterSkinRow.tsx +9 -3
- package/src/components/Store/StoreItemDetails.tsx +9 -1
- package/src/components/Store/StoreItemRow.tsx +13 -3
- package/src/components/Store/sections/StorePacksSection.tsx +7 -0
- package/src/components/Store/sections/StoreRedeemSection.tsx +2 -1
- package/src/components/shared/DCRateStrip.tsx +3 -2
- package/src/components/shared/MMORPGNumber.tsx +22 -0
- package/src/stories/Features/marketplace/CharacterDetailModal.stories.tsx +69 -15
- package/src/stories/Features/marketplace/CharacterMarketplace.stories.tsx +105 -11
- package/src/stories/Features/store/Store.stories.tsx +125 -35
- package/src/stories/Features/trading/Marketplace.stories.tsx +8 -4
- package/src/utils/__test__/atlasUtils.spec.ts +15 -0
- package/src/utils/atlasUtils.ts +78 -0
- package/src/utils/numberUtils.ts +31 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a number using modern gaming standards (k for thousands, M for millions, B for billions).
|
|
3
|
+
* @param num The number to format
|
|
4
|
+
* @param useKNotation Whether to use the k/M/B notation
|
|
5
|
+
* @returns Formatted string
|
|
6
|
+
*/
|
|
7
|
+
export declare const formatMMORPGNumber: (num: number, useKNotation?: boolean) => string;
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import { InternalTabs } from '../InternalTabs/InternalTabs';
|
|
|
5
5
|
import { DCRateStrip } from '../shared/DCRateStrip';
|
|
6
6
|
import { DCHistoryPanel, IDCTransaction } from './DCHistoryPanel';
|
|
7
7
|
import { DCTransferPanel, IDCTransferCharacterResult } from './DCTransferPanel';
|
|
8
|
+
import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
|
|
8
9
|
|
|
9
10
|
export interface IDCWalletContentProps {
|
|
10
11
|
dcBalance: number;
|
|
@@ -44,7 +45,6 @@ export const DCWalletContent: React.FC<IDCWalletContentProps> = ({
|
|
|
44
45
|
searchResults,
|
|
45
46
|
}) => {
|
|
46
47
|
const usdValue = (dcBalance / DC_TO_USD).toFixed(2);
|
|
47
|
-
const goldValue = (dcBalance * DC_TO_GOLD_SWAP_RATE).toLocaleString();
|
|
48
48
|
|
|
49
49
|
const tabs = [
|
|
50
50
|
{
|
|
@@ -86,8 +86,10 @@ export const DCWalletContent: React.FC<IDCWalletContentProps> = ({
|
|
|
86
86
|
<BalanceTop>
|
|
87
87
|
<BalanceBlock>
|
|
88
88
|
<BalanceLabel>DC BALANCE</BalanceLabel>
|
|
89
|
-
<BalanceAmount
|
|
90
|
-
<BalanceEquiv
|
|
89
|
+
<BalanceAmount><MMORPGNumber value={dcBalance} /> <BalanceDC>DC</BalanceDC></BalanceAmount>
|
|
90
|
+
<BalanceEquiv>
|
|
91
|
+
≈ ${usdValue} USD · <MMORPGNumber value={dcBalance * DC_TO_GOLD_SWAP_RATE} /> Gold
|
|
92
|
+
</BalanceEquiv>
|
|
91
93
|
</BalanceBlock>
|
|
92
94
|
{onBuyDC && (
|
|
93
95
|
<BuyButton onPointerDown={onBuyDC} role="button" tabIndex={0} title="Buy Definya Coins">
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { Component, ErrorInfo, ReactNode } from 'react';
|
|
2
|
-
import atlasJSON from '../../../mocks/atlas/items/items.json';
|
|
3
|
-
import atlasIMG from '../../../mocks/atlas/items/items.png';
|
|
4
2
|
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
5
3
|
|
|
6
4
|
interface Props {
|
|
7
5
|
children?: ReactNode;
|
|
6
|
+
atlasJSON?: any;
|
|
7
|
+
atlasIMG?: any;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
interface State {
|
|
@@ -27,13 +27,30 @@ export class ErrorBoundary extends Component<Props, State> {
|
|
|
27
27
|
|
|
28
28
|
public render() {
|
|
29
29
|
if (this.state.hasError) {
|
|
30
|
+
if (this.props.atlasJSON && this.props.atlasIMG) {
|
|
31
|
+
return (
|
|
32
|
+
<SpriteFromAtlas
|
|
33
|
+
atlasIMG={this.props.atlasIMG}
|
|
34
|
+
atlasJSON={this.props.atlasJSON}
|
|
35
|
+
spriteKey={'others/no-image.png'}
|
|
36
|
+
imgScale={3}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
30
40
|
return (
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
<div style={{
|
|
42
|
+
width: '32px',
|
|
43
|
+
height: '32px',
|
|
44
|
+
border: '1px dashed #ef4444',
|
|
45
|
+
display: 'flex',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
justifyContent: 'center',
|
|
48
|
+
color: '#ef4444',
|
|
49
|
+
fontSize: '8px',
|
|
50
|
+
textAlign: 'center'
|
|
51
|
+
}}>
|
|
52
|
+
ERR
|
|
53
|
+
</div>
|
|
37
54
|
);
|
|
38
55
|
}
|
|
39
56
|
|
|
@@ -7,6 +7,8 @@ import { ConfirmModal } from '../../ConfirmModal';
|
|
|
7
7
|
import { DraggableContainer } from '../../DraggableContainer';
|
|
8
8
|
import { RPGUIContainerTypes } from '../../RPGUI/RPGUIContainer';
|
|
9
9
|
|
|
10
|
+
import { MMORPGNumber } from '../../../components/shared/MMORPGNumber';
|
|
11
|
+
|
|
10
12
|
interface IColorSelectorProps {
|
|
11
13
|
selectedColor: string;
|
|
12
14
|
isOpen: boolean;
|
|
@@ -59,7 +61,7 @@ export const ColorSelector: React.FC<IColorSelectorProps> = ({
|
|
|
59
61
|
<p>
|
|
60
62
|
Cost:
|
|
61
63
|
<CostDisplay>
|
|
62
|
-
{costWarning?.cost
|
|
64
|
+
<MMORPGNumber value={costWarning?.cost || 0} />
|
|
63
65
|
{costWarning?.currency || ' gold'}
|
|
64
66
|
</CostDisplay>
|
|
65
67
|
</p>
|
|
@@ -15,8 +15,8 @@ import { BuyOrderDetailsModal } from './BuyOrderDetailsModal';
|
|
|
15
15
|
import { BuyOrderRow } from './BuyOrderRows';
|
|
16
16
|
|
|
17
17
|
export interface IBuyOrderPanelProps {
|
|
18
|
-
atlasJSON
|
|
19
|
-
atlasIMG
|
|
18
|
+
atlasJSON: any;
|
|
19
|
+
atlasIMG: any;
|
|
20
20
|
selectedBlueprint?: IMarketplaceBlueprintSummary;
|
|
21
21
|
onOpenBlueprintSearch: () => void;
|
|
22
22
|
onCloseDetails?: () => void;
|
|
@@ -12,11 +12,12 @@ import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
|
12
12
|
import { Ellipsis } from '../shared/Ellipsis';
|
|
13
13
|
import { SimpleTooltip } from '../shared/SimpleTooltip';
|
|
14
14
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
15
|
+
import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
|
|
15
16
|
|
|
16
17
|
export interface IBuyOrderRowProps {
|
|
17
18
|
buyOrder: IMarketplaceBuyOrderItem;
|
|
18
|
-
atlasJSON
|
|
19
|
-
atlasIMG
|
|
19
|
+
atlasJSON: any;
|
|
20
|
+
atlasIMG: any;
|
|
20
21
|
isOwn?: boolean;
|
|
21
22
|
onCancel?: (buyOrderId: string) => void;
|
|
22
23
|
onFulfill?: (buyOrderId: string) => void;
|
|
@@ -25,7 +26,10 @@ export interface IBuyOrderRowProps {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const BUY_ORDER_DURATION_WEEKS = 4;
|
|
28
|
-
type BuyOrderWithQuantityFallback = IMarketplaceBuyOrderItem & {
|
|
29
|
+
type BuyOrderWithQuantityFallback = IMarketplaceBuyOrderItem & {
|
|
30
|
+
quantity?: number;
|
|
31
|
+
itemTexturePath?: string | null;
|
|
32
|
+
};
|
|
29
33
|
|
|
30
34
|
// Format "Active" → "ACTIVE", "Fulfilled" → "FULFILLED"
|
|
31
35
|
const formatStatusLabel = (status: string): string => {
|
|
@@ -74,7 +78,9 @@ export const BuyOrderRow: React.FC<IBuyOrderRowProps> = ({
|
|
|
74
78
|
const stackQty = (buyOrder as BuyOrderWithQuantityFallback).stackQty ?? (buyOrder as BuyOrderWithQuantityFallback).quantity;
|
|
75
79
|
const timeRemaining =
|
|
76
80
|
buyOrder.status === MarketplaceBuyOrderStatus.Active ? getTimeRemaining(buyOrder.createdAt) : null;
|
|
77
|
-
const
|
|
81
|
+
const textureLookupKey =
|
|
82
|
+
(buyOrder as BuyOrderWithQuantityFallback).itemTexturePath ?? buyOrder.itemBlueprintKey;
|
|
83
|
+
const spriteKey = resolveAtlasSpriteKey(atlasJSON, textureLookupKey);
|
|
78
84
|
const rarityGlow = buyOrder.itemRarity ? RARITY_COLORS[buyOrder.itemRarity] || null : null;
|
|
79
85
|
|
|
80
86
|
return (
|
|
@@ -119,7 +125,7 @@ export const BuyOrderRow: React.FC<IBuyOrderRowProps> = ({
|
|
|
119
125
|
</SimpleTooltip>
|
|
120
126
|
</GoldIcon>
|
|
121
127
|
)}
|
|
122
|
-
<GoldPrice
|
|
128
|
+
<GoldPrice><MMORPGNumber value={buyOrder.maxPrice} /></GoldPrice>
|
|
123
129
|
</GoldPriceRow>
|
|
124
130
|
{showRequestTag && (
|
|
125
131
|
<LabelPill
|
|
@@ -182,8 +188,8 @@ export const BuyOrderRow: React.FC<IBuyOrderRowProps> = ({
|
|
|
182
188
|
export interface IGroupedBuyOrderRowProps {
|
|
183
189
|
bestOrder: IMarketplaceBuyOrderItem;
|
|
184
190
|
otherOrders: IMarketplaceBuyOrderItem[];
|
|
185
|
-
atlasJSON
|
|
186
|
-
atlasIMG
|
|
191
|
+
atlasJSON: any;
|
|
192
|
+
atlasIMG: any;
|
|
187
193
|
isOwn?: boolean;
|
|
188
194
|
onCancel?: (buyOrderId: string) => void;
|
|
189
195
|
onFulfill?: (buyOrderId: string) => void;
|
|
@@ -34,6 +34,15 @@ const RARITY_COLORS: Record<string, string> = {
|
|
|
34
34
|
const rarityColor = (rarity?: string) =>
|
|
35
35
|
RARITY_COLORS[(rarity ?? '').toLowerCase()] ?? RARITY_COLORS.common;
|
|
36
36
|
|
|
37
|
+
const formatEquipmentSlot = (slot?: string) => {
|
|
38
|
+
if (!slot) return 'Unknown';
|
|
39
|
+
|
|
40
|
+
return slot
|
|
41
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
42
|
+
.replace(/[_-]+/g, ' ')
|
|
43
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
44
|
+
};
|
|
45
|
+
|
|
37
46
|
export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
|
|
38
47
|
listing,
|
|
39
48
|
isOpen,
|
|
@@ -103,7 +112,11 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
|
|
|
103
112
|
>
|
|
104
113
|
<Header>
|
|
105
114
|
<Title>Character Details</Title>
|
|
106
|
-
<CloseButton
|
|
115
|
+
<CloseButton
|
|
116
|
+
onPointerDown={handleClose}
|
|
117
|
+
aria-label="Close"
|
|
118
|
+
type="button"
|
|
119
|
+
>
|
|
107
120
|
<FaTimes />
|
|
108
121
|
</CloseButton>
|
|
109
122
|
</Header>
|
|
@@ -157,9 +170,26 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
|
|
|
157
170
|
<EquipmentList>
|
|
158
171
|
{snap.equipment.map((eq, i) => (
|
|
159
172
|
<EquipmentRow key={i}>
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
173
|
+
<EquipmentSprite>
|
|
174
|
+
<SpriteFromAtlas
|
|
175
|
+
atlasIMG={atlasIMG}
|
|
176
|
+
atlasJSON={atlasJSON}
|
|
177
|
+
spriteKey={eq.itemKey}
|
|
178
|
+
imgScale={2}
|
|
179
|
+
width={32}
|
|
180
|
+
height={32}
|
|
181
|
+
centered
|
|
182
|
+
/>
|
|
183
|
+
</EquipmentSprite>
|
|
184
|
+
<EquipMeta>
|
|
185
|
+
<EquipName>{eq.itemName}</EquipName>
|
|
186
|
+
<EquipDetails>
|
|
187
|
+
<EquipSlot>{formatEquipmentSlot(eq.slot)}</EquipSlot>
|
|
188
|
+
<RarityBadge $rarity={eq.rarity}>
|
|
189
|
+
{eq.rarity || 'Common'}
|
|
190
|
+
</RarityBadge>
|
|
191
|
+
</EquipDetails>
|
|
192
|
+
</EquipMeta>
|
|
163
193
|
</EquipmentRow>
|
|
164
194
|
))}
|
|
165
195
|
</EquipmentList>
|
|
@@ -239,9 +269,17 @@ const ModalContent = styled.div`
|
|
|
239
269
|
pointer-events: auto;
|
|
240
270
|
animation: ${scaleIn} 0.15s ease-out;
|
|
241
271
|
|
|
242
|
-
&::-webkit-scrollbar {
|
|
243
|
-
|
|
244
|
-
|
|
272
|
+
&::-webkit-scrollbar {
|
|
273
|
+
width: 6px;
|
|
274
|
+
}
|
|
275
|
+
&::-webkit-scrollbar-track {
|
|
276
|
+
background: rgba(0, 0, 0, 0.2);
|
|
277
|
+
border-radius: 4px;
|
|
278
|
+
}
|
|
279
|
+
&::-webkit-scrollbar-thumb {
|
|
280
|
+
background: rgba(245, 158, 11, 0.3);
|
|
281
|
+
border-radius: 4px;
|
|
282
|
+
}
|
|
245
283
|
`;
|
|
246
284
|
|
|
247
285
|
const Header = styled.div`
|
|
@@ -268,7 +306,9 @@ const CloseButton = styled.button`
|
|
|
268
306
|
display: flex;
|
|
269
307
|
align-items: center;
|
|
270
308
|
|
|
271
|
-
&:hover {
|
|
309
|
+
&:hover {
|
|
310
|
+
color: #fff;
|
|
311
|
+
}
|
|
272
312
|
`;
|
|
273
313
|
|
|
274
314
|
const HeroSection = styled.div`
|
|
@@ -285,8 +325,8 @@ const SpriteContainer = styled.div`
|
|
|
285
325
|
width: 96px;
|
|
286
326
|
height: 96px;
|
|
287
327
|
flex-shrink: 0;
|
|
288
|
-
background: rgba(255,255,255,0.03);
|
|
289
|
-
border: 1px solid rgba(255,255,255,0.06);
|
|
328
|
+
background: rgba(255, 255, 255, 0.03);
|
|
329
|
+
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
290
330
|
border-radius: 6px;
|
|
291
331
|
`;
|
|
292
332
|
|
|
@@ -320,7 +360,9 @@ const ModeBadge = styled.span<{ $hardcore?: boolean }>`
|
|
|
320
360
|
font-family: 'Press Start 2P', cursive !important;
|
|
321
361
|
font-size: 0.35rem !important;
|
|
322
362
|
color: ${({ $hardcore }) => ($hardcore ? '#ef4444' : '#6b7280')} !important;
|
|
323
|
-
border: 1px solid
|
|
363
|
+
border: 1px solid
|
|
364
|
+
${({ $hardcore }) =>
|
|
365
|
+
$hardcore ? 'rgba(239,68,68,0.4)' : 'rgba(107,114,128,0.3)'};
|
|
324
366
|
border-radius: 3px;
|
|
325
367
|
padding: 2px 6px;
|
|
326
368
|
text-transform: uppercase;
|
|
@@ -330,7 +372,7 @@ const ModeBadge = styled.span<{ $hardcore?: boolean }>`
|
|
|
330
372
|
|
|
331
373
|
const Divider = styled.hr`
|
|
332
374
|
border: none;
|
|
333
|
-
border-top: 1px solid rgba(255,255,255,0.06);
|
|
375
|
+
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
|
334
376
|
margin: 0;
|
|
335
377
|
flex-shrink: 0;
|
|
336
378
|
`;
|
|
@@ -354,6 +396,10 @@ const MetaColumns = styled.div`
|
|
|
354
396
|
grid-template-columns: 1fr 1fr;
|
|
355
397
|
gap: 16px;
|
|
356
398
|
align-items: start;
|
|
399
|
+
|
|
400
|
+
@media (max-width: 640px) {
|
|
401
|
+
grid-template-columns: 1fr;
|
|
402
|
+
}
|
|
357
403
|
`;
|
|
358
404
|
|
|
359
405
|
const SkillsList = styled.div`
|
|
@@ -393,9 +439,35 @@ const EquipmentRow = styled.div`
|
|
|
393
439
|
align-items: center;
|
|
394
440
|
gap: 8px;
|
|
395
441
|
padding: 5px 8px;
|
|
396
|
-
background: rgba(255,255,255,0.02);
|
|
397
|
-
border: 1px solid rgba(255,255,255,0.05);
|
|
442
|
+
background: rgba(255, 255, 255, 0.02);
|
|
443
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
398
444
|
border-radius: 4px;
|
|
445
|
+
min-width: 0;
|
|
446
|
+
`;
|
|
447
|
+
|
|
448
|
+
const EquipmentSprite = styled.div`
|
|
449
|
+
display: flex;
|
|
450
|
+
align-items: center;
|
|
451
|
+
justify-content: center;
|
|
452
|
+
width: 32px;
|
|
453
|
+
height: 32px;
|
|
454
|
+
flex-shrink: 0;
|
|
455
|
+
`;
|
|
456
|
+
|
|
457
|
+
const EquipMeta = styled.div`
|
|
458
|
+
display: flex;
|
|
459
|
+
flex-direction: column;
|
|
460
|
+
gap: 4px;
|
|
461
|
+
min-width: 0;
|
|
462
|
+
flex: 1;
|
|
463
|
+
`;
|
|
464
|
+
|
|
465
|
+
const EquipDetails = styled.div`
|
|
466
|
+
display: flex;
|
|
467
|
+
align-items: center;
|
|
468
|
+
justify-content: space-between;
|
|
469
|
+
gap: 8px;
|
|
470
|
+
min-width: 0;
|
|
399
471
|
`;
|
|
400
472
|
|
|
401
473
|
const EquipSlot = styled.span`
|
|
@@ -403,8 +475,7 @@ const EquipSlot = styled.span`
|
|
|
403
475
|
font-size: 0.34rem !important;
|
|
404
476
|
color: #6b7280 !important;
|
|
405
477
|
text-transform: capitalize;
|
|
406
|
-
min-width:
|
|
407
|
-
flex-shrink: 0;
|
|
478
|
+
min-width: 0;
|
|
408
479
|
`;
|
|
409
480
|
|
|
410
481
|
const EquipName = styled.span`
|
|
@@ -412,6 +483,10 @@ const EquipName = styled.span`
|
|
|
412
483
|
font-size: 0.38rem !important;
|
|
413
484
|
color: #d1d5db !important;
|
|
414
485
|
flex: 1;
|
|
486
|
+
min-width: 0;
|
|
487
|
+
overflow: hidden;
|
|
488
|
+
text-overflow: ellipsis;
|
|
489
|
+
white-space: nowrap;
|
|
415
490
|
`;
|
|
416
491
|
|
|
417
492
|
const RarityBadge = styled.span<{ $rarity?: string }>`
|
|
@@ -445,6 +520,11 @@ const FooterActions = styled.div`
|
|
|
445
520
|
align-items: center;
|
|
446
521
|
justify-content: space-between;
|
|
447
522
|
gap: 12px;
|
|
523
|
+
|
|
524
|
+
@media (max-width: 640px) {
|
|
525
|
+
flex-direction: column;
|
|
526
|
+
align-items: stretch;
|
|
527
|
+
}
|
|
448
528
|
`;
|
|
449
529
|
|
|
450
530
|
const PriceDisplay = styled.div`
|
|
@@ -473,7 +553,9 @@ const BuyBtn = styled(CTAButton)`
|
|
|
473
553
|
padding: 10px 18px;
|
|
474
554
|
height: 34px;
|
|
475
555
|
|
|
476
|
-
span {
|
|
556
|
+
span {
|
|
557
|
+
font-size: 0.6rem;
|
|
558
|
+
}
|
|
477
559
|
`;
|
|
478
560
|
|
|
479
561
|
const PendingNotice = styled.span`
|
|
@@ -9,6 +9,7 @@ import { Input } from '../Input';
|
|
|
9
9
|
import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
10
10
|
import { DCRateStrip } from '../shared/DCRateStrip';
|
|
11
11
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
12
|
+
import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
|
|
12
13
|
|
|
13
14
|
export interface ICharacterListingModalProps {
|
|
14
15
|
isOpen: boolean;
|
|
@@ -176,7 +177,7 @@ export const CharacterListingModal: React.FC<ICharacterListingModalProps> = ({
|
|
|
176
177
|
centered
|
|
177
178
|
/>
|
|
178
179
|
</DCCoinWrapper>
|
|
179
|
-
<PricePreviewAmount
|
|
180
|
+
<PricePreviewAmount><MMORPGNumber value={Number(price)} /> DC</PricePreviewAmount>
|
|
180
181
|
</PricePreview>
|
|
181
182
|
)}
|
|
182
183
|
<DCRateStrip />
|
|
@@ -57,29 +57,29 @@ const GroupMeta = styled.div<{ $rightInset: number }>`
|
|
|
57
57
|
transform: translateY(-50%);
|
|
58
58
|
display: flex;
|
|
59
59
|
align-items: center;
|
|
60
|
-
gap:
|
|
61
|
-
|
|
62
|
-
background: linear-gradient(to left, rgba(25, 23, 23, 0.85), rgba(25, 23, 23, 0));
|
|
60
|
+
gap: 16px;
|
|
61
|
+
background: transparent;
|
|
63
62
|
pointer-events: none;
|
|
64
|
-
z-index:
|
|
63
|
+
z-index: 10;
|
|
65
64
|
`;
|
|
66
65
|
|
|
67
66
|
const OfferBadge = styled.span`
|
|
68
67
|
font-family: 'Press Start 2P', cursive;
|
|
69
68
|
font-size: 0.5rem;
|
|
70
|
-
color:
|
|
71
|
-
background:
|
|
72
|
-
padding: 2px 6px;
|
|
73
|
-
border-radius: 8px;
|
|
69
|
+
color: #fbbf24;
|
|
70
|
+
background: transparent;
|
|
74
71
|
white-space: nowrap;
|
|
72
|
+
letter-spacing: 0.5px;
|
|
73
|
+
text-shadow: 0 0 8px rgba(251, 191, 36, 0.4);
|
|
75
74
|
`;
|
|
76
75
|
|
|
77
76
|
const Chevron = styled.span<{ $expanded: boolean }>`
|
|
78
77
|
display: inline-block;
|
|
79
|
-
font-size: 0.
|
|
80
|
-
color:
|
|
78
|
+
font-size: 0.8rem;
|
|
79
|
+
color: #fbbf24;
|
|
81
80
|
transition: transform 0.2s ease;
|
|
82
81
|
transform: rotate(${({ $expanded }) => ($expanded ? '90deg' : '0deg')});
|
|
82
|
+
text-shadow: 0 0 5px rgba(251, 191, 36, 0.3);
|
|
83
83
|
`;
|
|
84
84
|
|
|
85
85
|
const SubRows = styled.div`
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
formatDCAmount,
|
|
3
2
|
goldToDC,
|
|
4
3
|
IMarketplaceTransaction,
|
|
5
4
|
MarketplaceTransactionType,
|
|
@@ -7,6 +6,7 @@ import {
|
|
|
7
6
|
import React from 'react';
|
|
8
7
|
import styled from 'styled-components';
|
|
9
8
|
import { uiColors } from '../../constants/uiColors';
|
|
9
|
+
import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
|
|
10
10
|
import { Dropdown } from '../Dropdown';
|
|
11
11
|
import { IOptionsProps } from '../Dropdown';
|
|
12
12
|
import { Pager } from '../Pager';
|
|
@@ -22,8 +22,8 @@ export interface IHistoryPanelProps {
|
|
|
22
22
|
selectedType: string;
|
|
23
23
|
onTypeChange: (type: string) => void;
|
|
24
24
|
onPageChange: (page: number) => void;
|
|
25
|
-
atlasJSON
|
|
26
|
-
atlasIMG
|
|
25
|
+
atlasJSON: any;
|
|
26
|
+
atlasIMG: any;
|
|
27
27
|
dcToGoldSwapRate?: number;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -177,7 +177,7 @@ export const HistoryPanel: React.FC<IHistoryPanelProps> = ({
|
|
|
177
177
|
)}
|
|
178
178
|
<DCPrice $type={tx.type}>
|
|
179
179
|
{getGoldSign(tx.type)}
|
|
180
|
-
{
|
|
180
|
+
<MMORPGNumber value={getDCEquivalentPrice(tx.goldAmount)} />
|
|
181
181
|
</DCPrice>
|
|
182
182
|
</DCPriceRow>
|
|
183
183
|
) : (
|
|
@@ -197,7 +197,7 @@ export const HistoryPanel: React.FC<IHistoryPanelProps> = ({
|
|
|
197
197
|
)}
|
|
198
198
|
<GoldAmount $type={tx.type}>
|
|
199
199
|
{getGoldSign(tx.type)}
|
|
200
|
-
{tx.goldAmount}g
|
|
200
|
+
<MMORPGNumber value={tx.goldAmount} />g
|
|
201
201
|
</GoldAmount>
|
|
202
202
|
</GoldPriceRow>
|
|
203
203
|
)}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { formatDCAmount } from '@rpg-engine/shared';
|
|
2
1
|
import React, { useCallback, useState } from 'react';
|
|
3
2
|
import { FaTimes } from 'react-icons/fa';
|
|
4
3
|
import styled from 'styled-components';
|
|
5
4
|
import ModalPortal from '../Abstractions/ModalPortal';
|
|
6
5
|
import { Button, ButtonTypes } from '../Button';
|
|
6
|
+
import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
|
|
7
7
|
|
|
8
8
|
export type MarketplacePaymentMethod = 'gold' | 'dc';
|
|
9
9
|
|
|
@@ -60,7 +60,7 @@ export const MarketplaceBuyModal: React.FC<IMarketplaceBuyModalProps> = ({
|
|
|
60
60
|
<RadioCircle $selected={selected === 'gold'} />
|
|
61
61
|
<OptionText>
|
|
62
62
|
<OptionLabel>Gold</OptionLabel>
|
|
63
|
-
<OptionSub
|
|
63
|
+
<OptionSub><MMORPGNumber value={goldPrice} /> gold</OptionSub>
|
|
64
64
|
</OptionText>
|
|
65
65
|
</RadioOption>
|
|
66
66
|
|
|
@@ -73,10 +73,10 @@ export const MarketplaceBuyModal: React.FC<IMarketplaceBuyModalProps> = ({
|
|
|
73
73
|
<OptionText>
|
|
74
74
|
<OptionLabel $disabled={!hasSufficientDC}>Definya Coin</OptionLabel>
|
|
75
75
|
<OptionSub>
|
|
76
|
-
{
|
|
76
|
+
<MMORPGNumber value={dcEquivalentPrice} /> DC
|
|
77
77
|
{' '}
|
|
78
78
|
<BalanceHint $insufficient={!hasSufficientDC}>
|
|
79
|
-
({
|
|
79
|
+
(<MMORPGNumber value={dcBalance} /> available)
|
|
80
80
|
</BalanceHint>
|
|
81
81
|
</OptionSub>
|
|
82
82
|
</OptionText>
|
|
@@ -20,6 +20,7 @@ import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
|
20
20
|
import { Ellipsis } from '../shared/Ellipsis';
|
|
21
21
|
import { SimpleTooltip } from '../shared/SimpleTooltip';
|
|
22
22
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
23
|
+
import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
|
|
23
24
|
|
|
24
25
|
export interface IMarketPlaceRowsPropos {
|
|
25
26
|
atlasJSON: any;
|
|
@@ -34,6 +35,8 @@ export interface IMarketPlaceRowsPropos {
|
|
|
34
35
|
onMarketPlaceItemRemove?: () => void;
|
|
35
36
|
onDCCoinClick?: () => void;
|
|
36
37
|
disabled?: boolean;
|
|
38
|
+
/** If true, adds padding-right to details to avoid overlapping with grouped badges */
|
|
39
|
+
$isGrouped?: boolean;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
@@ -49,6 +52,7 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
49
52
|
onMarketPlaceItemRemove,
|
|
50
53
|
onDCCoinClick,
|
|
51
54
|
disabled,
|
|
55
|
+
$isGrouped,
|
|
52
56
|
}) => {
|
|
53
57
|
const renderGems = (item: IItem) => {
|
|
54
58
|
return item.attachedGems && onRenderGems(item);
|
|
@@ -91,7 +95,7 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
91
95
|
</ItemInfoWrapper>
|
|
92
96
|
</SpriteContainer>
|
|
93
97
|
|
|
94
|
-
<ItemDetails>
|
|
98
|
+
<ItemDetails $isGrouped={$isGrouped}>
|
|
95
99
|
<ItemName>
|
|
96
100
|
<Ellipsis maxLines={1} maxWidth="200px" fontSize="10px">
|
|
97
101
|
{item.name}
|
|
@@ -110,7 +114,7 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
|
|
|
110
114
|
/>
|
|
111
115
|
</SimpleTooltip>
|
|
112
116
|
</GoldIcon>
|
|
113
|
-
<GoldPrice
|
|
117
|
+
<GoldPrice><MMORPGNumber value={itemPrice} /></GoldPrice>
|
|
114
118
|
</GoldPriceRow>
|
|
115
119
|
)}
|
|
116
120
|
{dcEquivalentPrice !== undefined && (
|
|
@@ -174,7 +178,7 @@ export const GroupedMarketplaceRow: React.FC<IGroupedMarketplaceRowProps> = ({
|
|
|
174
178
|
onBuy,
|
|
175
179
|
onDCCoinClick,
|
|
176
180
|
}) => {
|
|
177
|
-
const makeRow = (listing: IMarketplaceItem) => {
|
|
181
|
+
const makeRow = (listing: IMarketplaceItem, isMain: boolean = false) => {
|
|
178
182
|
const listingCurrency = listing.acceptedCurrency || MarketplaceAcceptedCurrency.GoldOrDc;
|
|
179
183
|
const isDcOnly = listingCurrency === MarketplaceAcceptedCurrency.Dc;
|
|
180
184
|
const showDcPrice = isDcOnly || (dcToGoldSwapRate > 0 && listingCurrency !== MarketplaceAcceptedCurrency.Gold);
|
|
@@ -192,16 +196,17 @@ export const GroupedMarketplaceRow: React.FC<IGroupedMarketplaceRowProps> = ({
|
|
|
192
196
|
onMarketPlaceItemBuy={() => onBuy(listing._id)}
|
|
193
197
|
onDCCoinClick={isDcOnly ? undefined : onDCCoinClick}
|
|
194
198
|
disabled={listing.owner === characterId}
|
|
199
|
+
$isGrouped={isMain}
|
|
195
200
|
/>
|
|
196
201
|
);
|
|
197
202
|
};
|
|
198
203
|
|
|
199
204
|
return (
|
|
200
205
|
<GroupedRowContainer
|
|
201
|
-
mainRow={makeRow(bestListing)}
|
|
202
|
-
subRows={otherListings.map(makeRow)}
|
|
206
|
+
mainRow={makeRow(bestListing, true)}
|
|
207
|
+
subRows={otherListings.map(l => makeRow(l, false))}
|
|
203
208
|
badgeLabel="offers"
|
|
204
|
-
metaRightInset={
|
|
209
|
+
metaRightInset={180}
|
|
205
210
|
/>
|
|
206
211
|
);
|
|
207
212
|
};
|
|
@@ -221,12 +226,13 @@ const SpriteContainer = styled.div`
|
|
|
221
226
|
min-width: 44px; /* Ensure wide stack quantities don't overlap ItemDetails */
|
|
222
227
|
`;
|
|
223
228
|
|
|
224
|
-
const ItemDetails = styled.div
|
|
229
|
+
const ItemDetails = styled.div<{ $isGrouped?: boolean }>`
|
|
225
230
|
display: flex;
|
|
226
231
|
flex-direction: column;
|
|
227
232
|
gap: 0.2rem;
|
|
228
233
|
min-width: 0;
|
|
229
234
|
margin-left: 1rem;
|
|
235
|
+
padding-right: ${({ $isGrouped }) => ($isGrouped ? '280px' : '0')};
|
|
230
236
|
`;
|
|
231
237
|
|
|
232
238
|
const ItemName = styled.div`
|