@rpg-engine/long-bow 0.8.162 → 0.8.165
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/Store/Store.d.ts +1 -0
- package/dist/components/Store/sections/StorePacksSection.d.ts +1 -1
- package/dist/long-bow.cjs.development.js +84 -33
- 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 +84 -33
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Marketplace/BuyPanel.tsx +8 -4
- package/src/components/Store/Store.tsx +19 -4
- package/src/components/Store/sections/StorePacksSection.tsx +90 -30
- package/src/stories/Features/store/Store.stories.tsx +2 -1
package/package.json
CHANGED
|
@@ -90,7 +90,6 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
90
90
|
dcBalance = 0,
|
|
91
91
|
dcToGoldSwapRate = 0,
|
|
92
92
|
openBuyOrders = [],
|
|
93
|
-
openBuyOrdersTotal = 0,
|
|
94
93
|
openBuyOrdersPage = 1,
|
|
95
94
|
onOpenBuyOrdersPageChange,
|
|
96
95
|
isLoading = false,
|
|
@@ -191,6 +190,11 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
191
190
|
});
|
|
192
191
|
}, [visibleBuyOrders]);
|
|
193
192
|
|
|
193
|
+
const paginatedGroupedBuyOrders = useMemo(() => {
|
|
194
|
+
const start = (openBuyOrdersPage - 1) * BUY_REQUESTS_PER_PAGE;
|
|
195
|
+
return groupedBuyOrders.slice(start, start + BUY_REQUESTS_PER_PAGE);
|
|
196
|
+
}, [groupedBuyOrders, openBuyOrdersPage]);
|
|
197
|
+
|
|
194
198
|
const showSellSection = browseMode === 'sell';
|
|
195
199
|
const showBuySection = browseMode === 'buy';
|
|
196
200
|
const hasVisibleContent =
|
|
@@ -447,7 +451,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
447
451
|
{groupedBuyOrders.length === 0 ? (
|
|
448
452
|
<SectionEmpty>No public buy requests found.</SectionEmpty>
|
|
449
453
|
) : (
|
|
450
|
-
|
|
454
|
+
paginatedGroupedBuyOrders.map(({ bestOrder, otherOrders }) => (
|
|
451
455
|
<GroupedBuyOrderRow
|
|
452
456
|
key={bestOrder._id}
|
|
453
457
|
bestOrder={bestOrder}
|
|
@@ -473,9 +477,9 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
|
|
|
473
477
|
onPageChange={onPageChange}
|
|
474
478
|
/>
|
|
475
479
|
)}
|
|
476
|
-
{showBuySection &&
|
|
480
|
+
{showBuySection && groupedBuyOrders.length > BUY_REQUESTS_PER_PAGE && (
|
|
477
481
|
<Pager
|
|
478
|
-
totalItems={
|
|
482
|
+
totalItems={groupedBuyOrders.length}
|
|
479
483
|
currentPage={openBuyOrdersPage}
|
|
480
484
|
itemsPerPage={BUY_REQUESTS_PER_PAGE}
|
|
481
485
|
onPageChange={onOpenBuyOrdersPageChange ?? (() => {})}
|
|
@@ -10,6 +10,7 @@ import { uiColors } from '../../constants/uiColors';
|
|
|
10
10
|
import { DraggableContainer } from '../DraggableContainer';
|
|
11
11
|
import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
|
|
12
12
|
import { Tabs } from '../shared/Tabs';
|
|
13
|
+
import { LabelPill } from '../shared/LabelPill/LabelPill';
|
|
13
14
|
import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
14
15
|
import { CartView } from './CartView';
|
|
15
16
|
import { useStoreCart } from './hooks/useStoreCart';
|
|
@@ -42,6 +43,7 @@ export interface IStoreProps {
|
|
|
42
43
|
textInputItemKeys?: string[];
|
|
43
44
|
customPacksContent?: React.ReactNode;
|
|
44
45
|
customWalletContent?: React.ReactNode;
|
|
46
|
+
packsBadge?: string;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export const Store: React.FC<IStoreProps> = ({
|
|
@@ -64,6 +66,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
64
66
|
customPacksContent,
|
|
65
67
|
customWalletContent,
|
|
66
68
|
packsTabLabel = 'Packs',
|
|
69
|
+
packsBadge,
|
|
67
70
|
}) => {
|
|
68
71
|
const [selectedPack, setSelectedPack] = useState<IItemPack | null>(null);
|
|
69
72
|
const [activeTab, setActiveTab] = useState<TabId>(() => {
|
|
@@ -87,7 +90,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
87
90
|
const [isCollectingMetadata, setIsCollectingMetadata] = useState(false);
|
|
88
91
|
const [currentMetadataItem, setCurrentMetadataItem] = useState<IProductBlueprint | null>(null);
|
|
89
92
|
|
|
90
|
-
const handleAddPackToCart = (pack: IItemPack) => {
|
|
93
|
+
const handleAddPackToCart = (pack: IItemPack, quantity: number = 1) => {
|
|
91
94
|
const packItem: IProductBlueprint = {
|
|
92
95
|
key: pack.key,
|
|
93
96
|
name: pack.title,
|
|
@@ -105,7 +108,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
105
108
|
maxStackSize: 1,
|
|
106
109
|
isUsable: false,
|
|
107
110
|
};
|
|
108
|
-
handleAddToCart(packItem,
|
|
111
|
+
handleAddToCart(packItem, quantity);
|
|
109
112
|
};
|
|
110
113
|
|
|
111
114
|
const filterItems = (
|
|
@@ -163,7 +166,7 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
163
166
|
const tabIds: TabId[] = tabOrder ?? ['premium', 'packs', 'items'];
|
|
164
167
|
const availableTabIds: TabId[] = tabIds.filter(id => !(hidePremiumTab && id === 'premium'));
|
|
165
168
|
|
|
166
|
-
const tabsMap: Record<string, { id: TabId; title:
|
|
169
|
+
const tabsMap: Record<string, { id: TabId; title: ReactNode; icon: ReactNode; content: ReactNode }> = {
|
|
167
170
|
premium: {
|
|
168
171
|
id: 'premium',
|
|
169
172
|
title: 'Premium',
|
|
@@ -180,7 +183,12 @@ export const Store: React.FC<IStoreProps> = ({
|
|
|
180
183
|
},
|
|
181
184
|
packs: {
|
|
182
185
|
id: 'packs',
|
|
183
|
-
title:
|
|
186
|
+
title: packsBadge ? (
|
|
187
|
+
<TabLabelWithBadge>
|
|
188
|
+
{packsTabLabel}
|
|
189
|
+
<LabelPill background="#f59e0b" borderColor="#f59e0b" color="#000">{packsBadge}</LabelPill>
|
|
190
|
+
</TabLabelWithBadge>
|
|
191
|
+
) : packsTabLabel,
|
|
184
192
|
icon: <Gift width={18} height={18} />,
|
|
185
193
|
content: customPacksContent ?? (
|
|
186
194
|
<StorePacksSection
|
|
@@ -390,6 +398,13 @@ const CartInfo = styled.div`
|
|
|
390
398
|
}
|
|
391
399
|
`;
|
|
392
400
|
|
|
401
|
+
const TabLabelWithBadge = styled.span`
|
|
402
|
+
display: inline-flex;
|
|
403
|
+
align-items: center;
|
|
404
|
+
gap: 5px;
|
|
405
|
+
`;
|
|
406
|
+
|
|
407
|
+
|
|
393
408
|
const LoadingMessage = styled.div`
|
|
394
409
|
text-align: center;
|
|
395
410
|
color: ${uiColors.white};
|
|
@@ -6,20 +6,73 @@ import { CTAButton } from '../../shared/CTAButton/CTAButton';
|
|
|
6
6
|
import { ItemRowWrapper } from '../../shared/ItemRowWrapper';
|
|
7
7
|
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
8
8
|
import { ScrollableContent } from '../../shared/ScrollableContent/ScrollableContent';
|
|
9
|
+
import { SelectArrow } from '../../Arrow/SelectArrow';
|
|
9
10
|
import { usePackFiltering } from '../../../hooks/usePackFiltering';
|
|
11
|
+
import { useQuantityControl } from '../../../hooks/useQuantityControl';
|
|
10
12
|
|
|
11
13
|
interface IStorePacksSectionProps {
|
|
12
14
|
packs: IItemPack[];
|
|
13
|
-
onAddToCart: (pack: IItemPack) => void;
|
|
15
|
+
onAddToCart: (pack: IItemPack, quantity: number) => void;
|
|
14
16
|
onSelectPack?: (pack: IItemPack) => void;
|
|
15
17
|
atlasJSON?: any;
|
|
16
18
|
atlasIMG?: string;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
interface IPackRowItemProps {
|
|
22
|
+
pack: IItemPack;
|
|
23
|
+
onAddToCart: (pack: IItemPack, quantity: number) => void;
|
|
24
|
+
renderPackIcon: (pack: IItemPack) => React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const PackRowItem: React.FC<IPackRowItemProps> = ({ pack, onAddToCart, renderPackIcon }) => {
|
|
28
|
+
const { quantity, handleQuantityChange, handleBlur, incrementQuantity, decrementQuantity, resetQuantity } = useQuantityControl();
|
|
29
|
+
|
|
30
|
+
const handleAdd = () => {
|
|
31
|
+
onAddToCart(pack, quantity);
|
|
32
|
+
resetQuantity();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<PackRow>
|
|
37
|
+
<LeftSection>
|
|
38
|
+
<PackIconContainer>
|
|
39
|
+
{renderPackIcon(pack)}
|
|
40
|
+
</PackIconContainer>
|
|
41
|
+
|
|
42
|
+
<PackDetails>
|
|
43
|
+
<PackName>{pack.title}</PackName>
|
|
44
|
+
<PackPrice>${pack.priceUSD}</PackPrice>
|
|
45
|
+
{pack.description && <PackDescription>{pack.description}</PackDescription>}
|
|
46
|
+
</PackDetails>
|
|
47
|
+
</LeftSection>
|
|
48
|
+
|
|
49
|
+
<Controls>
|
|
50
|
+
<ArrowsContainer>
|
|
51
|
+
<SelectArrow direction="left" onPointerDown={decrementQuantity} size={24} />
|
|
52
|
+
<QuantityInput
|
|
53
|
+
type="number"
|
|
54
|
+
value={quantity}
|
|
55
|
+
onChange={handleQuantityChange}
|
|
56
|
+
onBlur={handleBlur}
|
|
57
|
+
min={1}
|
|
58
|
+
max={99}
|
|
59
|
+
className="rpgui-input"
|
|
60
|
+
/>
|
|
61
|
+
<SelectArrow direction="right" onPointerDown={incrementQuantity} size={24} />
|
|
62
|
+
</ArrowsContainer>
|
|
63
|
+
<CTAButton
|
|
64
|
+
icon={<FaCartPlus />}
|
|
65
|
+
label="Add"
|
|
66
|
+
onClick={handleAdd}
|
|
67
|
+
/>
|
|
68
|
+
</Controls>
|
|
69
|
+
</PackRow>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
19
73
|
export const StorePacksSection: React.FC<IStorePacksSectionProps> = ({
|
|
20
74
|
packs,
|
|
21
75
|
onAddToCart,
|
|
22
|
-
onSelectPack,
|
|
23
76
|
atlasJSON,
|
|
24
77
|
atlasIMG,
|
|
25
78
|
}) => {
|
|
@@ -43,32 +96,14 @@ export const StorePacksSection: React.FC<IStorePacksSectionProps> = ({
|
|
|
43
96
|
|
|
44
97
|
const renderPack = useCallback(
|
|
45
98
|
(pack: IItemPack) => (
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<PackDetails>
|
|
53
|
-
<PackName>{pack.title}</PackName>
|
|
54
|
-
<PackPrice>${pack.priceUSD}</PackPrice>
|
|
55
|
-
{pack.description && <PackDescription>{pack.description}</PackDescription>}
|
|
56
|
-
</PackDetails>
|
|
57
|
-
</LeftSection>
|
|
58
|
-
|
|
59
|
-
<Controls>
|
|
60
|
-
<CTAButton
|
|
61
|
-
icon={<FaCartPlus />}
|
|
62
|
-
label="Add"
|
|
63
|
-
onClick={e => {
|
|
64
|
-
e.stopPropagation();
|
|
65
|
-
onAddToCart(pack);
|
|
66
|
-
}}
|
|
67
|
-
/>
|
|
68
|
-
</Controls>
|
|
69
|
-
</PackRow>
|
|
99
|
+
<PackRowItem
|
|
100
|
+
key={pack.key}
|
|
101
|
+
pack={pack}
|
|
102
|
+
onAddToCart={onAddToCart}
|
|
103
|
+
renderPackIcon={renderPackIcon}
|
|
104
|
+
/>
|
|
70
105
|
),
|
|
71
|
-
[
|
|
106
|
+
[onAddToCart, renderPackIcon]
|
|
72
107
|
);
|
|
73
108
|
|
|
74
109
|
return (
|
|
@@ -87,9 +122,7 @@ export const StorePacksSection: React.FC<IStorePacksSectionProps> = ({
|
|
|
87
122
|
);
|
|
88
123
|
};
|
|
89
124
|
|
|
90
|
-
const PackRow = styled(ItemRowWrapper)
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
`;
|
|
125
|
+
const PackRow = styled(ItemRowWrapper)``;
|
|
93
126
|
|
|
94
127
|
const LeftSection = styled.div`
|
|
95
128
|
display: flex;
|
|
@@ -144,5 +177,32 @@ const PackDescription = styled.div`
|
|
|
144
177
|
const Controls = styled.div`
|
|
145
178
|
display: flex;
|
|
146
179
|
align-items: center;
|
|
180
|
+
gap: 0.5rem;
|
|
147
181
|
flex-shrink: 0;
|
|
148
182
|
`;
|
|
183
|
+
|
|
184
|
+
const ArrowsContainer = styled.div`
|
|
185
|
+
position: relative;
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
width: 120px;
|
|
189
|
+
height: 42px;
|
|
190
|
+
justify-content: space-between;
|
|
191
|
+
`;
|
|
192
|
+
|
|
193
|
+
const QuantityInput = styled.input`
|
|
194
|
+
width: 40px;
|
|
195
|
+
text-align: center;
|
|
196
|
+
margin: 0 auto;
|
|
197
|
+
font-size: 0.875rem;
|
|
198
|
+
background: rgba(0, 0, 0, 0.2);
|
|
199
|
+
color: #ffffff;
|
|
200
|
+
border: none;
|
|
201
|
+
padding: 0.25rem;
|
|
202
|
+
|
|
203
|
+
&::-webkit-inner-spin-button,
|
|
204
|
+
&::-webkit-outer-spin-button {
|
|
205
|
+
-webkit-appearance: none;
|
|
206
|
+
margin: 0;
|
|
207
|
+
}
|
|
208
|
+
`;
|
|
@@ -237,8 +237,9 @@ export const Default: Story = {
|
|
|
237
237
|
atlasIMG={itemsAtlasIMG}
|
|
238
238
|
hidePremiumTab={true}
|
|
239
239
|
tabOrder={['items', 'packs']}
|
|
240
|
-
defaultActiveTab="
|
|
240
|
+
defaultActiveTab="packs"
|
|
241
241
|
textInputItemKeys={['original-greater-life-potion-2', 'original-angelic-sword-1', 'character-name-change']}
|
|
242
|
+
packsBadge="SAVE"
|
|
242
243
|
/>
|
|
243
244
|
),
|
|
244
245
|
};
|