@rpg-engine/long-bow 0.8.141 → 0.8.146

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.
Files changed (64) hide show
  1. package/dist/components/Marketplace/BlueprintSearchModal.d.ts +17 -0
  2. package/dist/components/Marketplace/BlueprintTable.d.ts +9 -0
  3. package/dist/components/Marketplace/BuyOrderDetailsModal.d.ts +17 -0
  4. package/dist/components/Marketplace/BuyOrderPanel.d.ts +24 -0
  5. package/dist/components/Marketplace/BuyOrderRows.d.ts +13 -0
  6. package/dist/components/Marketplace/BuyPanel.d.ts +9 -1
  7. package/dist/components/Marketplace/HistoryPanel.d.ts +18 -0
  8. package/dist/components/Marketplace/ManagmentPanel.d.ts +3 -2
  9. package/dist/components/Marketplace/Marketplace.d.ts +35 -2
  10. package/dist/components/Marketplace/MarketplaceSettingsPanel.d.ts +2 -1
  11. package/dist/components/Store/PaymentMethodModal.d.ts +1 -0
  12. package/dist/components/shared/LabelPill/LabelPill.d.ts +9 -0
  13. package/dist/components/shared/LabelPill/index.d.ts +1 -0
  14. package/dist/components/shared/SegmentedToggle/SegmentedToggle.d.ts +12 -0
  15. package/dist/components/shared/SegmentedToggle/index.d.ts +1 -0
  16. package/dist/index.d.ts +4 -0
  17. package/dist/long-bow.cjs.development.js +11573 -1288
  18. package/dist/long-bow.cjs.development.js.map +1 -1
  19. package/dist/long-bow.cjs.production.min.js +1 -1
  20. package/dist/long-bow.cjs.production.min.js.map +1 -1
  21. package/dist/long-bow.esm.js +11562 -1290
  22. package/dist/long-bow.esm.js.map +1 -1
  23. package/dist/stories/Features/marketplace/BlueprintSearchModal.stories.d.ts +1 -0
  24. package/dist/stories/Features/marketplace/BuyOrderPanel.stories.d.ts +1 -0
  25. package/dist/stories/Features/marketplace/BuyOrderRows.stories.d.ts +1 -0
  26. package/dist/stories/Features/marketplace/HistoryPanel.stories.d.ts +1 -0
  27. package/dist/stories/Features/trading/MarketplaceRows.stories.d.ts +2 -1
  28. package/dist/stories/UI/buttonsAndInputs/SegmentedToggle.stories.d.ts +6 -0
  29. package/dist/stories/UI/text/LabelPill.stories.d.ts +7 -0
  30. package/dist/utils/atlasUtils.d.ts +2 -0
  31. package/package.json +2 -2
  32. package/src/components/Marketplace/BlueprintSearchModal.tsx +295 -0
  33. package/src/components/Marketplace/BlueprintTable.tsx +158 -0
  34. package/src/components/Marketplace/BuyOrderDetailsModal.tsx +306 -0
  35. package/src/components/Marketplace/BuyOrderPanel.tsx +284 -0
  36. package/src/components/Marketplace/BuyOrderRows.tsx +287 -0
  37. package/src/components/Marketplace/BuyPanel.tsx +406 -166
  38. package/src/components/Marketplace/HistoryPanel.tsx +422 -0
  39. package/src/components/Marketplace/ManagmentPanel.tsx +13 -15
  40. package/src/components/Marketplace/Marketplace.tsx +181 -30
  41. package/src/components/Marketplace/MarketplaceBuyModal.tsx +1 -0
  42. package/src/components/Marketplace/MarketplaceRows.tsx +41 -10
  43. package/src/components/Marketplace/MarketplaceSettingsPanel.tsx +4 -3
  44. package/src/components/Store/CartView.tsx +11 -0
  45. package/src/components/Store/PaymentMethodModal.tsx +26 -9
  46. package/src/components/shared/LabelPill/LabelPill.tsx +45 -0
  47. package/src/components/shared/LabelPill/index.ts +1 -0
  48. package/src/components/shared/SegmentedToggle/SegmentedToggle.tsx +61 -0
  49. package/src/components/shared/SegmentedToggle/index.ts +1 -0
  50. package/src/components/shared/SpriteFromAtlas.tsx +7 -2
  51. package/src/index.tsx +4 -0
  52. package/src/mocks/atlas/items/items.json +33998 -25238
  53. package/src/mocks/atlas/items/items.png +0 -0
  54. package/src/mocks/itemContainer.mocks.ts +31 -0
  55. package/src/stories/Features/marketplace/BlueprintSearchModal.stories.tsx +145 -0
  56. package/src/stories/Features/marketplace/BuyOrderPanel.stories.tsx +323 -0
  57. package/src/stories/Features/marketplace/BuyOrderRows.stories.tsx +116 -0
  58. package/src/stories/Features/marketplace/HistoryPanel.stories.tsx +157 -0
  59. package/src/stories/Features/trading/Marketplace.stories.tsx +130 -0
  60. package/src/stories/Features/trading/MarketplaceRows.stories.tsx +11 -0
  61. package/src/stories/UI/buttonsAndInputs/SegmentedToggle.stories.tsx +54 -0
  62. package/src/stories/UI/text/LabelPill.stories.tsx +43 -0
  63. package/src/utils/__test__/atlasUtils.spec.ts +26 -0
  64. package/src/utils/atlasUtils.ts +80 -0
@@ -1,16 +1,29 @@
1
- import { IEquipmentSet, IItem, IMarketplaceItem } from '@rpg-engine/shared';
1
+ import {
2
+ IEquipmentSet,
3
+ IItem,
4
+ IMarketplaceBlueprintSearchRequest,
5
+ IMarketplaceBlueprintSummary,
6
+ IMarketplaceBuyOrderItem,
7
+ IMarketplaceItem,
8
+ IMarketplaceTransaction,
9
+ } from '@rpg-engine/shared';
10
+ import { Clock } from 'pixelarticons/react/Clock';
2
11
  import { Settings2 } from 'pixelarticons/react/Settings2';
12
+ import { ShoppingBag } from 'pixelarticons/react/ShoppingBag';
13
+ import { ShoppingCart } from 'pixelarticons/react/ShoppingCart';
3
14
  import { Store } from 'pixelarticons/react/Store';
4
- import { User } from 'pixelarticons/react/User';
5
15
  import React, { useState } from 'react';
6
16
  import styled from 'styled-components';
7
17
  import { DraggableContainer } from '../DraggableContainer';
8
18
  import { Pager } from '../Pager';
9
19
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
10
20
  import { Tabs } from '../shared/Tabs';
11
- import { MarketplacePaymentMethod } from './MarketplaceBuyModal';
21
+ import { BlueprintSearchModal } from './BlueprintSearchModal';
22
+ import { BuyOrderPanel } from './BuyOrderPanel';
12
23
  import { BuyPanel } from './BuyPanel';
24
+ import { HistoryPanel } from './HistoryPanel';
13
25
  import { ManagmentPanel } from './ManagmentPanel';
26
+ import { MarketplacePaymentMethod } from './MarketplaceBuyModal';
14
27
  import { MarketplaceAcceptedCurrency, MarketplaceSettingsPanel } from './MarketplaceSettingsPanel';
15
28
 
16
29
  export interface IMarketPlaceProps {
@@ -32,12 +45,13 @@ export interface IMarketPlaceProps {
32
45
  scale?: number;
33
46
  equipmentSet?: IEquipmentSet | null;
34
47
  onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
48
+ onFulfillBuyOrder?: (buyOrderId: string) => void;
35
49
  onMarketPlaceItemRemove?: (marketPlaceItemId: string) => void;
36
50
  availableGold: number;
37
51
  selectedItemToSell: IItem | null;
38
52
  onSelectedItemToSellRemove: (item: IItem) => void;
39
53
  onYourPanelToggle: (yourPanel: boolean) => void;
40
- onAddItemToMarketplace: (item: IItem, price: number) => void;
54
+ onAddItemToMarketplace: (item: IItem, price: number, acceptedCurrency?: MarketplaceAcceptedCurrency) => void;
41
55
  characterId: string;
42
56
  enableHotkeys?: () => void;
43
57
  disableHotkeys?: () => void;
@@ -50,15 +64,91 @@ export interface IMarketPlaceProps {
50
64
  dcToGoldSwapRate?: number;
51
65
  acceptedCurrency?: MarketplaceAcceptedCurrency;
52
66
  onAcceptedCurrencyChange?: (value: MarketplaceAcceptedCurrency) => void;
67
+ onActiveTabChange?: (tab: string) => void;
68
+
69
+ // Buy Order props
70
+ buyOrderSelectedBlueprint?: IMarketplaceBlueprintSummary;
71
+ buyOrderQuantity?: number;
72
+ buyOrderMaxPrice?: number;
73
+ buyOrderSelectedRarity?: string;
74
+ onBuyOrderQuantityChange?: (quantity: number) => void;
75
+ onBuyOrderMaxPriceChange?: (price: number) => void;
76
+ onBuyOrderRarityChange?: (rarity: string) => void;
77
+ onPlaceBuyOrder?: () => void;
78
+ onClearBuyOrderBlueprint?: () => void;
79
+ yourBuyOrders?: IMarketplaceBuyOrderItem[];
80
+ yourBuyOrdersTotal?: number;
81
+ yourBuyOrdersPage?: number;
82
+ onYourBuyOrdersPageChange?: (page: number) => void;
83
+ onCancelBuyOrder?: (buyOrderId: string) => void;
84
+ openBuyOrders?: IMarketplaceBuyOrderItem[];
85
+ openBuyOrdersTotal?: number;
86
+ openBuyOrdersPage?: number;
87
+ onOpenBuyOrdersPageChange?: (page: number) => void;
88
+
89
+ // Blueprint Search props
90
+ onBlueprintSearch?: (request: IMarketplaceBlueprintSearchRequest) => void;
91
+ onBlueprintSelect?: (blueprint: IMarketplaceBlueprintSummary) => void;
92
+ blueprintSearchResults?: IMarketplaceBlueprintSummary[];
93
+ blueprintSearchTotalCount?: number;
94
+ blueprintSearchCurrentPage?: number;
95
+ blueprintSearchIsLoading?: boolean;
96
+
97
+ // History props
98
+ historyTransactions?: IMarketplaceTransaction[];
99
+ historyTotalCount?: number;
100
+ historyCurrentPage?: number;
101
+ historyItemsPerPage?: number;
102
+ historySelectedType?: string;
103
+ onHistoryTypeChange?: (type: string) => void;
104
+ onHistoryPageChange?: (page: number) => void;
53
105
  }
54
106
 
55
- type ActiveTab = 'marketplace' | 'your-panel' | 'settings';
107
+ type ActiveTab = 'marketplace' | 'sell' | 'buy-orders' | 'history' | 'settings';
56
108
 
57
109
  export const Marketplace: React.FC<IMarketPlaceProps> = props => {
58
- const { onClose, scale, onYourPanelToggle, acceptedCurrency: acceptedCurrencyProp, onAcceptedCurrencyChange } = props;
110
+ const {
111
+ onClose,
112
+ scale,
113
+ onYourPanelToggle,
114
+ onActiveTabChange,
115
+ acceptedCurrency: acceptedCurrencyProp,
116
+ onAcceptedCurrencyChange,
117
+ // Buy Order
118
+ buyOrderSelectedBlueprint,
119
+ buyOrderQuantity = 0,
120
+ buyOrderMaxPrice = 0,
121
+ buyOrderSelectedRarity = '',
122
+ onBuyOrderQuantityChange,
123
+ onBuyOrderMaxPriceChange,
124
+ onBuyOrderRarityChange,
125
+ onPlaceBuyOrder,
126
+ onClearBuyOrderBlueprint,
127
+ yourBuyOrders = [],
128
+ yourBuyOrdersTotal = 0,
129
+ yourBuyOrdersPage = 1,
130
+ onYourBuyOrdersPageChange,
131
+ onCancelBuyOrder,
132
+ // Blueprint Search
133
+ onBlueprintSearch,
134
+ onBlueprintSelect,
135
+ blueprintSearchResults = [],
136
+ blueprintSearchTotalCount = 0,
137
+ blueprintSearchCurrentPage = 1,
138
+ blueprintSearchIsLoading = false,
139
+ // History
140
+ historyTransactions = [],
141
+ historyTotalCount = 0,
142
+ historyCurrentPage = 1,
143
+ historyItemsPerPage = 10,
144
+ historySelectedType = 'All',
145
+ onHistoryTypeChange,
146
+ onHistoryPageChange,
147
+ } = props;
59
148
 
60
149
  const [activeTab, setActiveTab] = useState<ActiveTab>('marketplace');
61
- const [acceptedCurrency, setAcceptedCurrency] = useState<MarketplaceAcceptedCurrency>(acceptedCurrencyProp ?? 'gold_or_dc');
150
+ const [acceptedCurrency, setAcceptedCurrency] = useState<MarketplaceAcceptedCurrency>(acceptedCurrencyProp ?? MarketplaceAcceptedCurrency.GoldOrDc);
151
+ const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
62
152
 
63
153
  const handleCurrencyChange = (value: MarketplaceAcceptedCurrency) => {
64
154
  setAcceptedCurrency(value);
@@ -68,9 +158,17 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
68
158
  const handleTabChange = (tabId: string) => {
69
159
  const tab = tabId as ActiveTab;
70
160
  setActiveTab(tab);
71
- onYourPanelToggle(tab === 'your-panel');
161
+ onYourPanelToggle(tab === 'sell');
162
+ onActiveTabChange?.(tab);
163
+ };
164
+
165
+ const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
166
+ setIsBlueprintSearchOpen(false);
167
+ onBlueprintSelect?.(blueprint);
72
168
  };
73
169
 
170
+ const showSharedPager = activeTab === 'sell';
171
+
74
172
  return (
75
173
  <DraggableContainer
76
174
  type={RPGUIContainerTypes.Framed}
@@ -89,9 +187,19 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
89
187
  icon: <Store width={18} height={18} />,
90
188
  },
91
189
  {
92
- id: 'your-panel',
93
- label: 'Your Panel',
94
- icon: <User width={18} height={18} />,
190
+ id: 'sell',
191
+ label: 'Sell',
192
+ icon: <ShoppingBag width={18} height={18} />,
193
+ },
194
+ {
195
+ id: 'buy-orders',
196
+ label: 'Buy',
197
+ icon: <ShoppingCart width={18} height={18} />,
198
+ },
199
+ {
200
+ id: 'history',
201
+ label: 'History',
202
+ icon: <Clock width={18} height={18} />,
95
203
  },
96
204
  {
97
205
  id: 'settings',
@@ -103,30 +211,77 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
103
211
  onTabChange={handleTabChange}
104
212
  />
105
213
 
106
- {activeTab === 'your-panel' && (
107
- <>
108
- <ManagmentPanel {...props} />
214
+ {activeTab === 'marketplace' && (
215
+ <BuyPanel {...props} />
216
+ )}
109
217
 
110
- <PagerContainer>
111
- <Pager {...props} />
112
- </PagerContainer>
113
- </>
218
+ {activeTab === 'sell' && (
219
+ <ManagmentPanel {...props} acceptedCurrency={acceptedCurrency} />
114
220
  )}
115
- {activeTab === 'marketplace' && (
116
- <>
117
- <BuyPanel {...props} />
118
221
 
119
- <PagerContainer>
120
- <Pager {...props} />
121
- </PagerContainer>
222
+ {activeTab === 'buy-orders' && (
223
+ <>
224
+ <BuyOrderPanel
225
+ atlasJSON={props.atlasJSON}
226
+ atlasIMG={props.atlasIMG}
227
+ selectedBlueprint={buyOrderSelectedBlueprint}
228
+ onOpenBlueprintSearch={() => setIsBlueprintSearchOpen(true)}
229
+ onCloseDetails={onClearBuyOrderBlueprint}
230
+ onQuantityChange={onBuyOrderQuantityChange ?? (() => {})}
231
+ onMaxPriceChange={onBuyOrderMaxPriceChange ?? (() => {})}
232
+ onRarityChange={onBuyOrderRarityChange ?? (() => {})}
233
+ onPlaceBuyOrder={onPlaceBuyOrder ?? (() => {})}
234
+ currentQuantity={buyOrderQuantity}
235
+ currentMaxPrice={buyOrderMaxPrice}
236
+ selectedRarity={buyOrderSelectedRarity}
237
+ yourBuyOrders={yourBuyOrders}
238
+ yourBuyOrdersTotal={yourBuyOrdersTotal}
239
+ yourBuyOrdersPage={yourBuyOrdersPage}
240
+ onYourBuyOrdersPageChange={onYourBuyOrdersPageChange ?? (() => {})}
241
+ onCancelBuyOrder={onCancelBuyOrder ?? (() => {})}
242
+ />
243
+ <BlueprintSearchModal
244
+ isOpen={isBlueprintSearchOpen}
245
+ onClose={() => setIsBlueprintSearchOpen(false)}
246
+ onSelect={handleBlueprintSelect}
247
+ onSearch={onBlueprintSearch ?? (() => {})}
248
+ blueprints={blueprintSearchResults}
249
+ totalCount={blueprintSearchTotalCount}
250
+ currentPage={blueprintSearchCurrentPage}
251
+ isLoading={blueprintSearchIsLoading}
252
+ atlasJSON={props.atlasJSON}
253
+ atlasIMG={props.atlasIMG}
254
+ />
122
255
  </>
123
256
  )}
257
+
258
+ {activeTab === 'history' && (
259
+ <HistoryPanel
260
+ transactions={historyTransactions}
261
+ totalCount={historyTotalCount}
262
+ currentPage={historyCurrentPage}
263
+ itemsPerPage={historyItemsPerPage}
264
+ selectedType={historySelectedType}
265
+ onTypeChange={onHistoryTypeChange ?? (() => {})}
266
+ onPageChange={onHistoryPageChange ?? (() => {})}
267
+ atlasJSON={props.atlasJSON}
268
+ atlasIMG={props.atlasIMG}
269
+ dcToGoldSwapRate={props.dcToGoldSwapRate}
270
+ />
271
+ )}
272
+
124
273
  {activeTab === 'settings' && (
125
274
  <MarketplaceSettingsPanel
126
275
  acceptedCurrency={acceptedCurrency}
127
276
  onAcceptedCurrencyChange={handleCurrencyChange}
128
277
  />
129
278
  )}
279
+
280
+ {showSharedPager && (
281
+ <PagerContainer>
282
+ <Pager {...props} />
283
+ </PagerContainer>
284
+ )}
130
285
  </DraggableContainer>
131
286
  );
132
287
  };
@@ -136,10 +291,6 @@ const PagerContainer = styled.div`
136
291
  justify-content: center;
137
292
  align-items: center;
138
293
  width: 95%;
139
- margin: 15px auto 0 auto;
140
- padding: 12px 16px;
141
- background: rgba(0, 0, 0, 0.3);
142
- border-top: 1px solid rgba(255, 255, 255, 0.1);
143
- border-radius: 6px;
144
- box-sizing: border-box;
294
+ margin: 6px auto 0 auto;
295
+ padding: 4px 10px;
145
296
  `;
@@ -152,6 +152,7 @@ const CloseButton = styled.button`
152
152
  padding: 4px;
153
153
  display: flex;
154
154
  align-items: center;
155
+ pointer-events: auto;
155
156
 
156
157
  &:hover {
157
158
  color: #ffffff;
@@ -15,6 +15,7 @@ import { onRenderGems } from '../Item/Inventory/ItemGem';
15
15
  import { rarityColor } from '../Item/Inventory/ItemSlotRarity';
16
16
  import { CTAButton } from '../shared/CTAButton/CTAButton';
17
17
  import { Ellipsis } from '../shared/Ellipsis';
18
+ import { SimpleTooltip } from '../shared/SimpleTooltip';
18
19
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
19
20
 
20
21
  export interface IMarketPlaceRowsPropos {
@@ -92,17 +93,31 @@ export const MarketplaceRows: React.FC<IMarketPlaceRowsPropos> = ({
92
93
  <PriceRow>
93
94
  <GoldPriceRow>
94
95
  <GoldIcon>
95
- <SpriteFromAtlas
96
- atlasIMG={atlasIMG}
97
- atlasJSON={atlasJSON}
98
- spriteKey="others/gold-coin-qty-5.png"
99
- imgScale={1}
100
- />
96
+ <SimpleTooltip content="Gold Coin" direction="top">
97
+ <SpriteFromAtlas
98
+ atlasIMG={atlasIMG}
99
+ atlasJSON={atlasJSON}
100
+ spriteKey="others/gold-coin-qty-5.png"
101
+ imgScale={1}
102
+ />
103
+ </SimpleTooltip>
101
104
  </GoldIcon>
102
105
  <GoldPrice>{itemPrice}</GoldPrice>
103
106
  </GoldPriceRow>
104
107
  {dcEquivalentPrice !== undefined && (
105
- <DCPrice>({formatDCAmount(dcEquivalentPrice)} DC)</DCPrice>
108
+ <DCPriceRow>
109
+ <DCCoinWrapper>
110
+ <SimpleTooltip content="Definya Coin" direction="top">
111
+ <SpriteFromAtlas
112
+ atlasIMG={atlasIMG}
113
+ atlasJSON={atlasJSON}
114
+ spriteKey="others/definya-coin.png"
115
+ imgScale={1}
116
+ />
117
+ </SimpleTooltip>
118
+ </DCCoinWrapper>
119
+ <DCPrice>{formatDCAmount(dcEquivalentPrice)}</DCPrice>
120
+ </DCPriceRow>
106
121
  )}
107
122
  </PriceRow>
108
123
  </ItemDetails>
@@ -327,14 +342,30 @@ const GoldIcon = styled.span`
327
342
 
328
343
  const GoldPrice = styled.span`
329
344
  font-family: 'Press Start 2P', cursive;
330
- font-size: 0.6rem;
345
+ font-size: 0.6rem !important;
331
346
  color: #fef08a;
332
347
  line-height: 1;
333
348
  `;
334
349
 
350
+ const DCPriceRow = styled.div`
351
+ display: flex;
352
+ align-items: center;
353
+ gap: 0.3rem;
354
+ margin-left: 0.5rem;
355
+ `;
356
+
357
+ const DCCoinWrapper = styled.span`
358
+ display: flex;
359
+ align-items: center;
360
+ justify-content: center;
361
+ position: relative;
362
+ top: -0.6rem;
363
+ left: -0.5rem;
364
+ `;
365
+
335
366
  const DCPrice = styled.span`
336
367
  font-family: 'Press Start 2P', cursive;
337
- font-size: 0.55rem;
368
+ font-size: 0.6rem !important;
338
369
  color: rgba(254, 240, 138, 0.65);
339
370
  white-space: nowrap;
340
371
  `;
@@ -348,7 +379,7 @@ const QuantityContainer = styled.p`
348
379
  position: absolute;
349
380
  display: block;
350
381
  top: 15px;
351
- left: 25px;
382
+ left: -8px;
352
383
  font-size: ${uiFonts.size.xsmall} !important;
353
384
  `;
354
385
 
@@ -1,7 +1,8 @@
1
+ import { MarketplaceAcceptedCurrency } from '@rpg-engine/shared';
1
2
  import React from 'react';
2
3
  import styled from 'styled-components';
3
4
 
4
- export type MarketplaceAcceptedCurrency = 'gold' | 'gold_or_dc';
5
+ export { MarketplaceAcceptedCurrency };
5
6
 
6
7
  export interface IMarketplaceSettingsPanelProps {
7
8
  acceptedCurrency: MarketplaceAcceptedCurrency;
@@ -10,12 +11,12 @@ export interface IMarketplaceSettingsPanelProps {
10
11
 
11
12
  const CURRENCY_OPTIONS: { value: MarketplaceAcceptedCurrency; label: string; description: string }[] = [
12
13
  {
13
- value: 'gold_or_dc',
14
+ value: MarketplaceAcceptedCurrency.GoldOrDc,
14
15
  label: 'Gold or DC',
15
16
  description: 'Accept both Gold and Definya Coin as payment',
16
17
  },
17
18
  {
18
- value: 'gold',
19
+ value: MarketplaceAcceptedCurrency.Gold,
19
20
  label: 'Gold only',
20
21
  description: 'Only accept Gold as payment',
21
22
  },
@@ -61,6 +61,11 @@ export const CartView: React.FC<ICartViewProps> = ({
61
61
  0
62
62
  );
63
63
 
64
+ const dcTotal = cartItems.reduce(
65
+ (sum, ci) => sum + (ci.item.dcPrice ?? 0) * ci.quantity,
66
+ 0
67
+ );
68
+
64
69
  const formatPrice = (price: number) => price.toFixed(2);
65
70
 
66
71
  const handlePurchase = async () => {
@@ -154,6 +159,12 @@ export const CartView: React.FC<ICartViewProps> = ({
154
159
  <span>Total:</span>
155
160
  <span>${formatPrice(total)}</span>
156
161
  </TotalRow>
162
+ {dcTotal > 0 && (
163
+ <TotalRow>
164
+ <span>DC:</span>
165
+ <span>{dcTotal.toLocaleString()} DC</span>
166
+ </TotalRow>
167
+ )}
157
168
  {error && <ErrorMessage>{error}</ErrorMessage>}
158
169
  </TotalInfo>
159
170
  <CTAButton
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useState } from 'react';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
2
  import { FaTimes } from 'react-icons/fa';
3
3
  import styled from 'styled-components';
4
4
  import ModalPortal from '../Abstractions/ModalPortal';
@@ -6,6 +6,7 @@ import { Button, ButtonTypes } from '../Button';
6
6
 
7
7
  export interface IPaymentMethodModalProps {
8
8
  dcBalance: number;
9
+ dcRequired?: number;
9
10
  onPayWithDC: () => void;
10
11
  onPayWithCard: () => void;
11
12
  onClose: () => void;
@@ -15,12 +16,21 @@ type PaymentMethod = 'dc' | 'card';
15
16
 
16
17
  export const PaymentMethodModal: React.FC<IPaymentMethodModalProps> = ({
17
18
  dcBalance,
19
+ dcRequired,
18
20
  onPayWithDC,
19
21
  onPayWithCard,
20
22
  onClose,
21
23
  }) => {
22
24
  const [selected, setSelected] = useState<PaymentMethod>('card');
23
25
 
26
+ const dcDisabled = dcRequired !== undefined && dcBalance < dcRequired;
27
+
28
+ useEffect(() => {
29
+ if (dcDisabled && selected === 'dc') {
30
+ setSelected('card');
31
+ }
32
+ }, [dcDisabled, selected]);
33
+
24
34
  const stopPropagation = useCallback(
25
35
  (e: React.MouseEvent | React.TouchEvent | React.PointerEvent) => {
26
36
  e.stopPropagation();
@@ -29,12 +39,17 @@ export const PaymentMethodModal: React.FC<IPaymentMethodModalProps> = ({
29
39
  );
30
40
 
31
41
  const handleConfirm = useCallback(() => {
32
- if (selected === 'dc') {
42
+ if (selected === 'dc' && !dcDisabled) {
33
43
  onPayWithDC();
34
44
  } else {
35
45
  onPayWithCard();
36
46
  }
37
- }, [selected, onPayWithDC, onPayWithCard]);
47
+ }, [selected, dcDisabled, onPayWithDC, onPayWithCard]);
48
+
49
+ const dcSubText =
50
+ dcRequired !== undefined
51
+ ? `${dcBalance.toLocaleString()} DC available · ${dcRequired.toLocaleString()} DC needed`
52
+ : `${dcBalance.toLocaleString()} DC available`;
38
53
 
39
54
  return (
40
55
  <ModalPortal>
@@ -66,12 +81,13 @@ export const PaymentMethodModal: React.FC<IPaymentMethodModalProps> = ({
66
81
 
67
82
  <RadioOption
68
83
  $selected={selected === 'dc'}
69
- onPointerDown={() => setSelected('dc')}
84
+ $disabled={dcDisabled}
85
+ onPointerDown={dcDisabled ? undefined : () => setSelected('dc')}
70
86
  >
71
87
  <RadioCircle $selected={selected === 'dc'} />
72
88
  <OptionText>
73
89
  <OptionLabel>Definya Coin</OptionLabel>
74
- <OptionSub>{dcBalance.toLocaleString()} DC available</OptionSub>
90
+ <OptionSub>{dcSubText}</OptionSub>
75
91
  </OptionText>
76
92
  </RadioOption>
77
93
  </Options>
@@ -157,7 +173,7 @@ const Options = styled.div`
157
173
  gap: 8px;
158
174
  `;
159
175
 
160
- const RadioOption = styled.div<{ $selected: boolean }>`
176
+ const RadioOption = styled.div<{ $selected: boolean; $disabled?: boolean }>`
161
177
  display: flex;
162
178
  align-items: center;
163
179
  gap: 12px;
@@ -165,11 +181,12 @@ const RadioOption = styled.div<{ $selected: boolean }>`
165
181
  border: 1px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.15)')};
166
182
  border-radius: 6px;
167
183
  background: ${({ $selected }) => ($selected ? 'rgba(245,158,11,0.1)' : 'transparent')};
168
- cursor: pointer;
169
- transition: border-color 0.15s, background 0.15s;
184
+ cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')};
185
+ opacity: ${({ $disabled }) => ($disabled ? 0.4 : 1)};
186
+ transition: border-color 0.15s, background 0.15s, opacity 0.15s;
170
187
 
171
188
  &:hover {
172
- border-color: #f59e0b;
189
+ border-color: ${({ $disabled }) => ($disabled ? 'rgba(255,255,255,0.15)' : '#f59e0b')};
173
190
  }
174
191
  `;
175
192
 
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ export interface ILabelPillProps {
5
+ children: React.ReactNode;
6
+ background?: string;
7
+ borderColor?: string;
8
+ color?: string;
9
+ className?: string;
10
+ }
11
+
12
+ export const LabelPill: React.FC<ILabelPillProps> = ({
13
+ children,
14
+ background = 'rgba(255,255,255,0.08)',
15
+ borderColor = 'rgba(255,255,255,0.08)',
16
+ color = '#fff',
17
+ className,
18
+ }) => {
19
+ return (
20
+ <Pill
21
+ className={className}
22
+ $background={background}
23
+ $borderColor={borderColor}
24
+ $color={color}
25
+ >
26
+ {children}
27
+ </Pill>
28
+ );
29
+ };
30
+
31
+ const Pill = styled.span<{
32
+ $background: string;
33
+ $borderColor: string;
34
+ $color: string;
35
+ }>`
36
+ font-family: 'Press Start 2P', cursive;
37
+ font-size: 0.45rem !important;
38
+ color: ${({ $color }) => $color};
39
+ text-transform: uppercase;
40
+ white-space: nowrap;
41
+ background: ${({ $background }) => $background};
42
+ padding: 2px 6px;
43
+ border-radius: 8px;
44
+ border: 1px solid ${({ $borderColor }) => $borderColor};
45
+ `;
@@ -0,0 +1 @@
1
+ export * from './LabelPill';
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ export interface ISegmentedToggleOption {
5
+ id: string;
6
+ label: React.ReactNode;
7
+ }
8
+
9
+ export interface ISegmentedToggleProps {
10
+ options: ISegmentedToggleOption[];
11
+ activeId: string;
12
+ onChange: (id: string) => void;
13
+ className?: string;
14
+ }
15
+
16
+ export const SegmentedToggle: React.FC<ISegmentedToggleProps> = ({
17
+ options,
18
+ activeId,
19
+ onChange,
20
+ className,
21
+ }) => {
22
+ return (
23
+ <Container className={className}>
24
+ {options.map(option => (
25
+ <OptionButton
26
+ key={option.id}
27
+ type="button"
28
+ $active={option.id === activeId}
29
+ onClick={() => onChange(option.id)}
30
+ >
31
+ {option.label}
32
+ </OptionButton>
33
+ ))}
34
+ </Container>
35
+ );
36
+ };
37
+
38
+ const Container = styled.div`
39
+ display: inline-flex;
40
+ align-items: center;
41
+ gap: 8px;
42
+ flex-wrap: wrap;
43
+ `;
44
+
45
+ const OptionButton = styled.button<{ $active: boolean }>`
46
+ border: 1px solid ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.75)' : 'rgba(255, 255, 255, 0.08)')};
47
+ background: ${({ $active }) => ($active ? 'rgba(245, 158, 11, 0.16)' : 'rgba(0, 0, 0, 0.18)')};
48
+ color: ${({ $active }) => ($active ? '#fde68a' : '#cfcfcf')};
49
+ border-radius: 999px;
50
+ padding: 8px 12px;
51
+ cursor: pointer;
52
+ font-size: 0.56rem;
53
+ text-transform: uppercase;
54
+ letter-spacing: 0.8px;
55
+ transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
56
+
57
+ &:hover {
58
+ border-color: rgba(245, 158, 11, 0.45);
59
+ color: #fde68a;
60
+ }
61
+ `;
@@ -0,0 +1 @@
1
+ export * from './SegmentedToggle';
@@ -1,6 +1,10 @@
1
1
  import { GRID_HEIGHT, GRID_WIDTH } from '@rpg-engine/shared';
2
2
  import React from 'react';
3
3
  import styled, { css } from 'styled-components';
4
+ import {
5
+ NO_IMAGE_SPRITE_KEY,
6
+ resolveAtlasSpriteKey,
7
+ } from '../../utils/atlasUtils';
4
8
  import { toUppercaseHexColor } from '../../utils/colorUtils';
5
9
 
6
10
  interface IProps {
@@ -41,9 +45,10 @@ export const SpriteFromAtlas: React.FC<IProps> = ({
41
45
  //! If an item is not showing, remember that you MUST run yarn atlas:copy everytime you add a new item to the atlas (it will sync our public folder atlas with src/atlas).
42
46
  //!Due to React's limitations, we cannot import it from the public folder directly!
43
47
 
48
+ const resolvedSpriteKey = resolveAtlasSpriteKey(atlasJSON, spriteKey);
44
49
  const spriteData =
45
- atlasJSON?.frames?.[spriteKey] ||
46
- atlasJSON?.frames?.['others/no-image.png'];
50
+ (resolvedSpriteKey && atlasJSON?.frames?.[resolvedSpriteKey]) ||
51
+ atlasJSON?.frames?.[NO_IMAGE_SPRITE_KEY];
47
52
 
48
53
  if (!spriteData) {
49
54
  console.error(
package/src/index.tsx CHANGED
@@ -38,6 +38,10 @@ export * from './components/Marketplace/Marketplace';
38
38
  export * from './components/Marketplace/MarketplaceBuyModal';
39
39
  export * from './components/Marketplace/MarketplaceRows';
40
40
  export * from './components/Marketplace/MarketplaceSettingsPanel';
41
+ export * from './components/Marketplace/BuyOrderPanel';
42
+ export * from './components/Marketplace/BuyOrderRows';
43
+ export * from './components/Marketplace/HistoryPanel';
44
+ export * from './components/Marketplace/BlueprintSearchModal';
41
45
  export * from './components/Multitab/TabBody';
42
46
  export * from './components/Multitab/TabsContainer';
43
47
  export * from './components/NPCDialog/NPCDialog';