@rpg-engine/long-bow 0.8.7 → 0.8.8
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/InformationCenter/sections/bestiary/{BestiarySection.d.ts → InformationCenterBestiarySection.d.ts} +1 -1
- package/dist/components/InformationCenter/sections/faq/{FaqSection.d.ts → InformationCenterFaqSection.d.ts} +1 -1
- package/dist/components/InformationCenter/sections/items/{ItemsSection.d.ts → InformationCenterItemsSection.d.ts} +1 -1
- package/dist/components/InformationCenter/sections/tutorials/{TutorialsSection.d.ts → InformationCenterTutorialsSection.d.ts} +2 -1
- package/dist/components/Item/Inventory/ItemPropertyColorSelector.d.ts +10 -0
- package/dist/components/Item/Inventory/ItemPropertySimpleHandler.d.ts +10 -0
- package/dist/components/Store/CartView.d.ts +15 -0
- package/dist/components/Store/StoreItemDetails.d.ts +16 -0
- package/dist/components/Store/StoreItemRow.d.ts +1 -2
- package/dist/components/Store/StoreTypes.d.ts +33 -4
- package/dist/components/Store/hooks/useStoreCart.d.ts +14 -0
- package/dist/components/Store/sections/StoreItemsSection.d.ts +12 -0
- package/dist/components/Store/sections/StorePacksSection.d.ts +9 -0
- package/dist/components/shared/CTAButton/CTAButton.d.ts +13 -0
- package/dist/components/shared/Card/Card.d.ts +14 -0
- package/dist/components/shared/Ellipsis.d.ts +1 -1
- package/dist/components/shared/PaginatedContent/PaginatedContent.d.ts +3 -1
- package/dist/components/shared/ScrollableContent/ScrollableContent.d.ts +23 -0
- package/dist/components/shared/SearchBar/SearchBar.d.ts +2 -3
- package/dist/components/shared/SearchHeader/SearchHeader.d.ts +17 -0
- package/dist/components/shared/ShoppingCart/CartCard.d.ts +14 -0
- package/dist/components/shared/ShoppingCart/CartCardHorizontal.d.ts +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +104 -43
- 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 +104 -44
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/stories/UI/buttonsAndInputs/CTAButton.stories.d.ts +18 -0
- package/dist/stories/UI/dropdownsAndSelectors/ItemPropertyColorSelector.stories.d.ts +3 -0
- package/package.json +3 -2
- package/src/components/InformationCenter/InformationCenter.tsx +8 -8
- package/src/components/InformationCenter/InformationCenterTabView.tsx +0 -1
- package/src/components/InformationCenter/sections/bestiary/{BestiarySection.tsx → InformationCenterBestiarySection.tsx} +2 -1
- package/src/components/InformationCenter/sections/faq/InformationCenterFaqSection.tsx +81 -0
- package/src/components/InformationCenter/sections/items/{ItemsSection.tsx → InformationCenterItemsSection.tsx} +2 -10
- package/src/components/InformationCenter/sections/tutorials/InformationCenterTutorialsSection.tsx +135 -0
- package/src/components/Item/Inventory/ItemPropertyColorSelector.tsx +75 -0
- package/src/components/Item/Inventory/ItemPropertySimpleHandler.tsx +26 -0
- package/src/components/Item/Inventory/itemContainerHelper.ts +10 -1
- package/src/components/Store/CartView.tsx +271 -0
- package/src/components/Store/Store.tsx +199 -96
- package/src/components/Store/StoreItemDetails.tsx +161 -0
- package/src/components/Store/StoreItemRow.tsx +24 -40
- package/src/components/Store/StoreTypes.ts +38 -4
- package/src/components/Store/hooks/useStoreCart.ts +121 -0
- package/src/components/Store/sections/StoreItemsSection.tsx +52 -0
- package/src/components/Store/sections/StorePacksSection.tsx +89 -0
- package/src/components/Store/sections/images/custom-skin.png +0 -0
- package/src/components/shared/CTAButton/CTAButton.tsx +127 -0
- package/src/components/shared/Card/Card.tsx +107 -0
- package/src/components/shared/Ellipsis.tsx +20 -22
- package/src/components/shared/PaginatedContent/PaginatedContent.tsx +48 -79
- package/src/components/shared/ScrollableContent/ScrollableContent.tsx +160 -0
- package/src/components/shared/SearchBar/SearchBar.tsx +43 -24
- package/src/components/shared/SearchHeader/SearchHeader.tsx +80 -0
- package/src/components/shared/ShoppingCart/CartCard.tsx +116 -0
- package/src/components/shared/ShoppingCart/CartCardHorizontal.tsx +120 -0
- package/src/components/shared/SpriteFromAtlas.tsx +56 -48
- package/src/index.tsx +1 -0
- package/src/stories/Features/store/Store.stories.tsx +54 -4
- package/src/stories/UI/buttonsAndInputs/CTAButton.stories.tsx +77 -0
- package/src/stories/UI/dropdownsAndSelectors/ItemPropertyColorSelector.stories.tsx +77 -0
- package/dist/components/Store/InternalStoreTab.d.ts +0 -15
- package/dist/components/Store/StoreTabContent.d.ts +0 -14
- package/src/components/InformationCenter/sections/faq/FaqSection.tsx +0 -51
- package/src/components/InformationCenter/sections/tutorials/TutorialsSection.tsx +0 -144
- package/src/components/Store/InternalStoreTab.tsx +0 -142
- package/src/components/Store/StoreTabContent.tsx +0 -46
|
@@ -1,52 +1,71 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { FaSearch } from 'react-icons/fa';
|
|
2
3
|
import styled from 'styled-components';
|
|
3
4
|
|
|
4
|
-
interface
|
|
5
|
+
interface ISearchBarProps {
|
|
5
6
|
value: string;
|
|
6
7
|
onChange: (value: string) => void;
|
|
7
8
|
placeholder?: string;
|
|
8
9
|
className?: string;
|
|
9
|
-
autoFocus?: boolean;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export const SearchBar: React.FC<
|
|
12
|
+
export const SearchBar: React.FC<ISearchBarProps> = ({
|
|
13
13
|
value,
|
|
14
14
|
onChange,
|
|
15
|
-
placeholder
|
|
15
|
+
placeholder,
|
|
16
16
|
className,
|
|
17
|
-
autoFocus = false,
|
|
18
17
|
}) => {
|
|
19
18
|
return (
|
|
20
19
|
<Container className={className}>
|
|
21
|
-
<
|
|
20
|
+
<Input
|
|
22
21
|
type="text"
|
|
23
|
-
className="rpgui-input"
|
|
24
|
-
placeholder={placeholder}
|
|
25
22
|
value={value}
|
|
26
23
|
onChange={e => onChange(e.target.value)}
|
|
27
|
-
|
|
24
|
+
placeholder={placeholder}
|
|
25
|
+
className="rpgui-input"
|
|
28
26
|
/>
|
|
27
|
+
<IconContainer>
|
|
28
|
+
<SearchIcon />
|
|
29
|
+
</IconContainer>
|
|
29
30
|
</Container>
|
|
30
31
|
);
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
const Container = styled.div`
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
padding: 8px 12px;
|
|
38
|
-
background-color: rgba(0, 0, 0, 0.3);
|
|
39
|
-
border: none;
|
|
40
|
-
color: white;
|
|
41
|
-
border-radius: 4px;
|
|
35
|
+
position: relative;
|
|
36
|
+
width: 100%;
|
|
37
|
+
`;
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
const Input = styled.input`
|
|
40
|
+
width: 100%;
|
|
41
|
+
padding-right: 2.5rem !important;
|
|
42
|
+
background: rgba(0, 0, 0, 0.2) !important;
|
|
43
|
+
border: 2px solid #f59e0b !important;
|
|
44
|
+
box-shadow: 0 0 10px rgba(245, 158, 11, 0.3);
|
|
45
|
+
color: #ffffff !important;
|
|
46
|
+
font-family: 'Press Start 2P', cursive !important;
|
|
47
|
+
font-size: 0.875rem !important;
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
background-color: rgba(0, 0, 0, 0.4);
|
|
50
|
-
}
|
|
49
|
+
&::placeholder {
|
|
50
|
+
color: rgba(255, 255, 255, 0.5) !important;
|
|
51
51
|
}
|
|
52
52
|
`;
|
|
53
|
+
|
|
54
|
+
const IconContainer = styled.div`
|
|
55
|
+
position: absolute;
|
|
56
|
+
right: 0.75rem;
|
|
57
|
+
top: 50%;
|
|
58
|
+
transform: translateY(-50%);
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
width: 24px;
|
|
63
|
+
height: 24px;
|
|
64
|
+
pointer-events: none;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const SearchIcon = styled(FaSearch)`
|
|
68
|
+
font-size: 1rem;
|
|
69
|
+
color: #f59e0b;
|
|
70
|
+
filter: drop-shadow(0 0 2px rgba(245, 158, 11, 0.3));
|
|
71
|
+
`;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Dropdown, IOptionsProps } from '../../Dropdown';
|
|
4
|
+
import { SearchBar } from '../SearchBar/SearchBar';
|
|
5
|
+
|
|
6
|
+
interface ISearchHeaderProps {
|
|
7
|
+
searchOptions?: {
|
|
8
|
+
value: string;
|
|
9
|
+
onChange: (value: string) => void;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
};
|
|
12
|
+
filterOptions?: {
|
|
13
|
+
options: IOptionsProps[];
|
|
14
|
+
selectedOption: string;
|
|
15
|
+
onOptionChange: (value: string) => void;
|
|
16
|
+
};
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const SearchHeader: React.FC<ISearchHeaderProps> = ({
|
|
21
|
+
searchOptions,
|
|
22
|
+
filterOptions,
|
|
23
|
+
className,
|
|
24
|
+
}) => {
|
|
25
|
+
if (!searchOptions && !filterOptions) return null;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<HeaderContainer className={className}>
|
|
29
|
+
<HeaderContent>
|
|
30
|
+
{searchOptions && (
|
|
31
|
+
<SearchContainer>
|
|
32
|
+
<StyledSearchBar
|
|
33
|
+
value={searchOptions.value}
|
|
34
|
+
onChange={searchOptions.onChange}
|
|
35
|
+
placeholder={searchOptions.placeholder || 'Search...'}
|
|
36
|
+
/>
|
|
37
|
+
</SearchContainer>
|
|
38
|
+
)}
|
|
39
|
+
{filterOptions && (
|
|
40
|
+
<FilterContainer>
|
|
41
|
+
<StyledDropdown
|
|
42
|
+
options={filterOptions.options}
|
|
43
|
+
onChange={filterOptions.onOptionChange}
|
|
44
|
+
width="200px"
|
|
45
|
+
/>
|
|
46
|
+
</FilterContainer>
|
|
47
|
+
)}
|
|
48
|
+
</HeaderContent>
|
|
49
|
+
</HeaderContainer>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const HeaderContainer = styled.div``;
|
|
54
|
+
|
|
55
|
+
const HeaderContent = styled.div`
|
|
56
|
+
display: flex;
|
|
57
|
+
justify-content: space-between;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: 1rem;
|
|
60
|
+
background: rgba(0, 0, 0, 0.2);
|
|
61
|
+
padding: 1rem;
|
|
62
|
+
border-radius: 4px;
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const SearchContainer = styled.div`
|
|
66
|
+
flex: 1;
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const FilterContainer = styled.div`
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: flex-end;
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const StyledSearchBar = styled(SearchBar)`
|
|
75
|
+
width: 100%;
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
const StyledDropdown = styled(Dropdown)`
|
|
79
|
+
min-width: 150px;
|
|
80
|
+
`;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { uiColors } from '../../../constants/uiColors';
|
|
4
|
+
import { Ellipsis } from '../Ellipsis';
|
|
5
|
+
|
|
6
|
+
export interface ICardProps {
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
imageUrl?: string | { src: string; default?: string };
|
|
10
|
+
category?: string;
|
|
11
|
+
onClick?: () => void;
|
|
12
|
+
footer?: React.ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ShoppingCard: React.FC<ICardProps> = ({
|
|
17
|
+
title,
|
|
18
|
+
description,
|
|
19
|
+
imageUrl,
|
|
20
|
+
category,
|
|
21
|
+
onClick,
|
|
22
|
+
footer,
|
|
23
|
+
className,
|
|
24
|
+
}) => {
|
|
25
|
+
const getImageSrc = () => {
|
|
26
|
+
if (!imageUrl) return '/placeholder-thumbnail.png';
|
|
27
|
+
if (typeof imageUrl === 'string') return imageUrl;
|
|
28
|
+
return imageUrl.default || imageUrl.src;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<CardContainer onClick={onClick} className={className}>
|
|
33
|
+
{imageUrl && (
|
|
34
|
+
<CardThumbnail>
|
|
35
|
+
<img src={getImageSrc()} alt={title} />
|
|
36
|
+
</CardThumbnail>
|
|
37
|
+
)}
|
|
38
|
+
<CardContent>
|
|
39
|
+
<CardTitle>
|
|
40
|
+
<Ellipsis maxLines={1} maxWidth="100%">
|
|
41
|
+
{title}
|
|
42
|
+
</Ellipsis>
|
|
43
|
+
</CardTitle>
|
|
44
|
+
<CardDescription>
|
|
45
|
+
<Ellipsis maxLines={5} maxWidth="100%">
|
|
46
|
+
{description}
|
|
47
|
+
</Ellipsis>
|
|
48
|
+
</CardDescription>
|
|
49
|
+
{category && <CardCategory>{category}</CardCategory>}
|
|
50
|
+
{footer && <CardFooter>{footer}</CardFooter>}
|
|
51
|
+
</CardContent>
|
|
52
|
+
</CardContainer>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const CardContainer = styled.div<{ onClick?: () => void }>`
|
|
57
|
+
background: rgba(0, 0, 0, 0.3);
|
|
58
|
+
border-radius: 4px;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
border: 1px solid ${uiColors.darkGray};
|
|
61
|
+
cursor: ${props => (props.onClick ? 'pointer' : 'default')};
|
|
62
|
+
transition: transform 0.2s ease;
|
|
63
|
+
|
|
64
|
+
&:hover {
|
|
65
|
+
transform: ${props => (props.onClick ? 'translateY(-2px)' : 'none')};
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const CardThumbnail = styled.div`
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 168px;
|
|
72
|
+
background: rgba(0, 0, 0, 0.2);
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
|
|
75
|
+
img {
|
|
76
|
+
width: 100%;
|
|
77
|
+
height: 100%;
|
|
78
|
+
object-fit: cover;
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
const CardContent = styled.div`
|
|
83
|
+
padding: 12px;
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const CardTitle = styled.h3`
|
|
87
|
+
margin: 0;
|
|
88
|
+
font-size: 0.6rem;
|
|
89
|
+
color: ${uiColors.yellow};
|
|
90
|
+
font-family: 'Press Start 2P', cursive;
|
|
91
|
+
margin-bottom: 8px;
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
const CardDescription = styled.p`
|
|
95
|
+
margin: 0;
|
|
96
|
+
font-size: 0.55rem;
|
|
97
|
+
color: ${uiColors.lightGray};
|
|
98
|
+
font-family: 'Press Start 2P', cursive;
|
|
99
|
+
margin-bottom: 8px;
|
|
100
|
+
line-height: 1.4;
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const CardCategory = styled.span`
|
|
104
|
+
font-size: 0.5rem;
|
|
105
|
+
color: ${uiColors.yellow};
|
|
106
|
+
font-family: 'Press Start 2P', cursive;
|
|
107
|
+
background: rgba(255, 255, 255, 0.1);
|
|
108
|
+
padding: 4px 8px;
|
|
109
|
+
border-radius: 4px;
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
const CardFooter = styled.div`
|
|
113
|
+
margin-top: 12px;
|
|
114
|
+
padding-top: 12px;
|
|
115
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
116
|
+
`;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { uiColors } from '../../../constants/uiColors';
|
|
4
|
+
import { Ellipsis } from '../Ellipsis';
|
|
5
|
+
|
|
6
|
+
export interface ICardProps {
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
imageUrl?: string | { src: string; default?: string };
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
footer?: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ShoppingCardHorizontal: React.FC<ICardProps> = ({
|
|
16
|
+
title,
|
|
17
|
+
description,
|
|
18
|
+
imageUrl,
|
|
19
|
+
onClick,
|
|
20
|
+
footer,
|
|
21
|
+
className,
|
|
22
|
+
}) => {
|
|
23
|
+
const getImageSrc = () => {
|
|
24
|
+
if (!imageUrl) return '/placeholder-thumbnail.png';
|
|
25
|
+
if (typeof imageUrl === 'string') return imageUrl;
|
|
26
|
+
return imageUrl.default || imageUrl.src;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<CardContainer onClick={onClick} className={className}>
|
|
31
|
+
<LeftSection>
|
|
32
|
+
{imageUrl && (
|
|
33
|
+
<CardThumbnail>
|
|
34
|
+
<img src={getImageSrc()} alt={title} />
|
|
35
|
+
</CardThumbnail>
|
|
36
|
+
)}
|
|
37
|
+
</LeftSection>
|
|
38
|
+
<RightSection>
|
|
39
|
+
<CardTitle>
|
|
40
|
+
<Ellipsis maxLines={1} maxWidth="100%">
|
|
41
|
+
{title}
|
|
42
|
+
</Ellipsis>
|
|
43
|
+
</CardTitle>
|
|
44
|
+
<CardDescription>
|
|
45
|
+
<Ellipsis maxLines={3} maxWidth="100%">
|
|
46
|
+
{description}
|
|
47
|
+
</Ellipsis>
|
|
48
|
+
</CardDescription>
|
|
49
|
+
{footer && (
|
|
50
|
+
<CardFooter onClick={e => e.stopPropagation()}>{footer}</CardFooter>
|
|
51
|
+
)}
|
|
52
|
+
</RightSection>
|
|
53
|
+
</CardContainer>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const CardContainer = styled.div<{ onClick?: () => void }>`
|
|
58
|
+
display: flex;
|
|
59
|
+
background: rgba(0, 0, 0, 0.3);
|
|
60
|
+
border-radius: 4px;
|
|
61
|
+
border: 1px solid ${uiColors.darkGray};
|
|
62
|
+
cursor: ${props => (props.onClick ? 'pointer' : 'default')};
|
|
63
|
+
transition: transform 0.2s ease;
|
|
64
|
+
|
|
65
|
+
&:hover {
|
|
66
|
+
transform: ${props => (props.onClick ? 'translateY(-2px)' : 'none')};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
max-width: 380px;
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const LeftSection = styled.div`
|
|
73
|
+
width: 120px;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const RightSection = styled.div`
|
|
78
|
+
flex: 1;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
padding: 12px;
|
|
82
|
+
min-width: 0;
|
|
83
|
+
max-width: 100%;
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const CardThumbnail = styled.div`
|
|
87
|
+
width: 100%;
|
|
88
|
+
height: 100%;
|
|
89
|
+
background: rgba(0, 0, 0, 0.2);
|
|
90
|
+
overflow: hidden;
|
|
91
|
+
|
|
92
|
+
img {
|
|
93
|
+
width: 100%;
|
|
94
|
+
height: 100%;
|
|
95
|
+
object-fit: cover;
|
|
96
|
+
}
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
const CardTitle = styled.h3`
|
|
100
|
+
margin: 0;
|
|
101
|
+
font-size: 0.6rem;
|
|
102
|
+
color: ${uiColors.yellow};
|
|
103
|
+
font-family: 'Press Start 2P', cursive;
|
|
104
|
+
margin-bottom: 8px;
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
const CardDescription = styled.p`
|
|
108
|
+
margin: 0;
|
|
109
|
+
font-size: 0.55rem;
|
|
110
|
+
color: ${uiColors.lightGray};
|
|
111
|
+
font-family: 'Press Start 2P', cursive;
|
|
112
|
+
line-height: 1.4;
|
|
113
|
+
margin-bottom: 8px;
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const CardFooter = styled.div`
|
|
117
|
+
margin-top: auto;
|
|
118
|
+
padding-top: 8px;
|
|
119
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
120
|
+
`;
|
|
@@ -37,43 +37,42 @@ export const SpriteFromAtlas: React.FC<IProps> = ({
|
|
|
37
37
|
centered,
|
|
38
38
|
borderRadius,
|
|
39
39
|
}) => {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
//! 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).
|
|
41
|
+
//!Due to React's limitations, we cannot import it from the public folder directly!
|
|
42
|
+
const spriteData =
|
|
43
|
+
atlasJSON?.frames?.[spriteKey] ||
|
|
44
|
+
atlasJSON?.frames?.['others/no-image.png'];
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
if (!spriteData) {
|
|
47
|
+
console.error(
|
|
48
|
+
`SpriteFromAtlas: Could not find sprite with key ${spriteKey} in atlasJSON.`
|
|
49
|
+
);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
centered={centered}
|
|
71
|
-
borderRadius={borderRadius}
|
|
72
|
-
tintColor={tintColor}
|
|
73
|
-
/>
|
|
74
|
-
</Container>
|
|
75
|
-
);
|
|
76
|
-
};
|
|
53
|
+
return (
|
|
54
|
+
<Container
|
|
55
|
+
width={width}
|
|
56
|
+
height={height}
|
|
57
|
+
hasHover={grayScale}
|
|
58
|
+
onPointerDown={onPointerDown}
|
|
59
|
+
style={containerStyle}
|
|
60
|
+
>
|
|
61
|
+
<ImgSprite
|
|
62
|
+
className={`sprite-from-atlas-img ${imgClassname || ''}`}
|
|
63
|
+
atlasIMG={atlasIMG}
|
|
64
|
+
frame={spriteData.frame}
|
|
65
|
+
scale={imgScale}
|
|
66
|
+
grayScale={grayScale}
|
|
67
|
+
opacity={opacity}
|
|
68
|
+
style={imgStyle}
|
|
69
|
+
centered={centered}
|
|
70
|
+
borderRadius={borderRadius}
|
|
71
|
+
tintColor={tintColor}
|
|
72
|
+
/>
|
|
73
|
+
</Container>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
77
76
|
|
|
78
77
|
interface IImgSpriteProps {
|
|
79
78
|
atlasIMG: any;
|
|
@@ -112,21 +111,30 @@ const ImgSprite = styled.div<IImgSpriteProps>`
|
|
|
112
111
|
width: ${props => props.frame.w}px;
|
|
113
112
|
height: ${props => props.frame.h}px;
|
|
114
113
|
background-image: url(${props => props.atlasIMG});
|
|
115
|
-
background-position: -${props => props.frame.x}px -${props =>
|
|
114
|
+
background-position: -${props => props.frame.x}px -${props =>
|
|
115
|
+
props.frame.y}px;
|
|
116
116
|
transform: scale(${props => props.scale});
|
|
117
117
|
position: relative;
|
|
118
118
|
top: ${props => (props.centered ? '0' : '8px')};
|
|
119
119
|
left: ${props => (props.centered ? '0' : '8px')};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
${props =>
|
|
121
|
+
props.tintColor &&
|
|
122
|
+
`
|
|
123
|
+
&::after {
|
|
124
|
+
content: '';
|
|
125
|
+
position: absolute;
|
|
126
|
+
top: 0;
|
|
127
|
+
left: 0;
|
|
128
|
+
width: ${props.frame.w}px;
|
|
129
|
+
height: ${props.frame.h}px;
|
|
130
|
+
background-color: ${props.tintColor};
|
|
131
|
+
mask-image: url(${props.atlasIMG});
|
|
132
|
+
mask-position: -${props.frame.x}px -${props.frame.y}px;
|
|
133
|
+
-webkit-mask-image: url(${props.atlasIMG});
|
|
134
|
+
-webkit-mask-position: -${props.frame.x}px -${props.frame.y}px;
|
|
135
|
+
mix-blend-mode: color;
|
|
136
|
+
${props.grayScale ? 'filter: grayscale(100%);' : 'none'}
|
|
137
|
+
}
|
|
138
|
+
`}
|
|
131
139
|
opacity: ${props => props.opacity};
|
|
132
|
-
`;
|
|
140
|
+
`;
|
package/src/index.tsx
CHANGED
|
@@ -22,6 +22,7 @@ export * from './components/Input';
|
|
|
22
22
|
export * from './components/InternalTabs/InternalTabs';
|
|
23
23
|
export { ErrorBoundary } from './components/Item/Inventory/ErrorBoundary';
|
|
24
24
|
export * from './components/Item/Inventory/ItemContainer';
|
|
25
|
+
export * from './components/Item/Inventory/ItemPropertySimpleHandler';
|
|
25
26
|
export * from './components/Item/Inventory/ItemQuantitySelectorModal';
|
|
26
27
|
export * from './components/Item/Inventory/ItemSlot';
|
|
27
28
|
export * from './components/itemSelector/ItemSelector';
|
|
@@ -2,8 +2,9 @@ import { UserAccountTypes } from '@rpg-engine/shared';
|
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { RPGUIRoot } from '../../../components/RPGUI/RPGUIRoot';
|
|
5
|
+
import customSkinImage from '../../../components/Store/sections/images/custom-skin.png';
|
|
5
6
|
import { Store } from '../../../components/Store/Store';
|
|
6
|
-
import { IStoreItem } from '../../../components/Store/StoreTypes';
|
|
7
|
+
import { IItemPack, IPurchase, IStoreItem } from '../../../components/Store/StoreTypes';
|
|
7
8
|
import itemsAtlasJSON from '../../../mocks/atlas/items/items.json';
|
|
8
9
|
import itemsAtlasIMG from '../../../mocks/atlas/items/items.png';
|
|
9
10
|
import { mockItems } from '../../../mocks/informationCenter.mocks';
|
|
@@ -85,15 +86,64 @@ const duplicatedItems: IStoreItem[] = [
|
|
|
85
86
|
})),
|
|
86
87
|
];
|
|
87
88
|
|
|
89
|
+
// Mock packs data
|
|
90
|
+
const mockPacks: IItemPack[] = [
|
|
91
|
+
// Regular Packs
|
|
92
|
+
{
|
|
93
|
+
key: 'starter-pack',
|
|
94
|
+
title: 'Starter Pack',
|
|
95
|
+
description: 'Perfect for beginners! Get essential items to start your adventure.',
|
|
96
|
+
priceUSD: 4.99,
|
|
97
|
+
image: customSkinImage,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: 'custom-skin-pack',
|
|
101
|
+
title: 'Custom Skin Pack',
|
|
102
|
+
description: 'Get a unique look with this custom skin pack! Stand out from the crowd.',
|
|
103
|
+
priceUSD: 7.99,
|
|
104
|
+
image: customSkinImage,
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
key: 'ultimate-pack',
|
|
109
|
+
title: '👑 Ultimate Premium Pack',
|
|
110
|
+
description: 'The most exclusive collection of items, effects, and perks. Includes everything from previous tiers plus legendary items!',
|
|
111
|
+
priceUSD: 99.99,
|
|
112
|
+
image: customSkinImage,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
key: 'gold-pack',
|
|
116
|
+
title: '🥇 Gold Premium Pack',
|
|
117
|
+
description: 'The ultimate premium experience with rare items, unique effects, and exclusive content. For true champions!',
|
|
118
|
+
priceUSD: 49.99,
|
|
119
|
+
image: customSkinImage,
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: 'silver-pack',
|
|
123
|
+
title: '🥈 Silver Premium Pack',
|
|
124
|
+
description: 'Enhanced premium features with more exclusive items and special effects. For dedicated adventurers.',
|
|
125
|
+
priceUSD: 24.99,
|
|
126
|
+
image: customSkinImage,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
key: 'bronze-pack',
|
|
130
|
+
title: '🥉 Bronze Premium Pack',
|
|
131
|
+
description: 'Start your premium journey with exclusive items and basic perks. Perfect for casual players.',
|
|
132
|
+
priceUSD: 14.99,
|
|
133
|
+
image: customSkinImage,
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
];
|
|
137
|
+
|
|
88
138
|
// Create the story with the static mock data
|
|
89
139
|
export const Default: Story = {
|
|
90
140
|
render: () => (
|
|
91
141
|
<Store
|
|
92
142
|
items={duplicatedItems}
|
|
93
|
-
|
|
143
|
+
packs={mockPacks}
|
|
94
144
|
userAccountType={UserAccountTypes.Free}
|
|
95
|
-
onPurchase={(
|
|
96
|
-
console.log('
|
|
145
|
+
onPurchase={(purchase: IPurchase) => {
|
|
146
|
+
console.log('Purchase details:', purchase);
|
|
97
147
|
}}
|
|
98
148
|
atlasJSON={itemsAtlasJSON}
|
|
99
149
|
atlasIMG={itemsAtlasIMG}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Meta, Story } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FaPlus, FaShoppingBag, FaShoppingCart, FaTrash } from 'react-icons/fa';
|
|
4
|
+
import { RPGUIRoot } from '../../../components/RPGUI/RPGUIRoot';
|
|
5
|
+
import { CTAButton } from '../../../components/shared/CTAButton/CTAButton';
|
|
6
|
+
|
|
7
|
+
interface ICTAButtonProps {
|
|
8
|
+
icon: React.ReactNode;
|
|
9
|
+
label?: React.ReactNode;
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
fullWidth?: boolean;
|
|
12
|
+
textColor?: string;
|
|
13
|
+
iconColor?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const meta: Meta = {
|
|
18
|
+
title: 'UI/Buttons & Inputs/CTAButton',
|
|
19
|
+
component: CTAButton,
|
|
20
|
+
argTypes: {
|
|
21
|
+
icon: { control: false },
|
|
22
|
+
label: { control: 'text' },
|
|
23
|
+
fullWidth: { control: 'boolean' },
|
|
24
|
+
textColor: { control: 'color' },
|
|
25
|
+
iconColor: { control: 'color' },
|
|
26
|
+
disabled: { control: 'boolean' },
|
|
27
|
+
},
|
|
28
|
+
decorators: [
|
|
29
|
+
Story => (
|
|
30
|
+
<RPGUIRoot>
|
|
31
|
+
<div style={{ padding: '2rem', background: '#333' }}>
|
|
32
|
+
<Story />
|
|
33
|
+
</div>
|
|
34
|
+
</RPGUIRoot>
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default meta;
|
|
40
|
+
|
|
41
|
+
const Template: Story<ICTAButtonProps> = args => <CTAButton {...args} />;
|
|
42
|
+
|
|
43
|
+
export const CartButton = Template.bind({});
|
|
44
|
+
CartButton.args = {
|
|
45
|
+
icon: <FaShoppingCart />,
|
|
46
|
+
label: '3',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const DeleteButton = Template.bind({});
|
|
50
|
+
DeleteButton.args = {
|
|
51
|
+
icon: <FaTrash />,
|
|
52
|
+
textColor: '#ef4444',
|
|
53
|
+
iconColor: '#ef4444',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const AddButton = Template.bind({});
|
|
57
|
+
AddButton.args = {
|
|
58
|
+
icon: <FaPlus />,
|
|
59
|
+
label: 'Add Item',
|
|
60
|
+
textColor: '#22c55e',
|
|
61
|
+
iconColor: '#22c55e',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const CheckoutButton = Template.bind({});
|
|
65
|
+
CheckoutButton.args = {
|
|
66
|
+
icon: <FaShoppingBag />,
|
|
67
|
+
label: 'Complete Purchase',
|
|
68
|
+
fullWidth: true,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const DisabledCheckoutButton = Template.bind({});
|
|
72
|
+
DisabledCheckoutButton.args = {
|
|
73
|
+
icon: <FaShoppingBag />,
|
|
74
|
+
label: 'Complete Purchase',
|
|
75
|
+
fullWidth: true,
|
|
76
|
+
disabled: true,
|
|
77
|
+
};
|