@rpg-engine/long-bow 0.8.30 → 0.8.32
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/BestiaryAdvancedFilters.d.ts +13 -0
- package/dist/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.d.ts +2 -2
- package/dist/components/InformationCenter/sections/items/ItemsAdvancedFilters.d.ts +11 -0
- package/dist/components/shared/AdvancedFilters/AdvancedFilters.d.ts +23 -0
- package/dist/components/shared/PaginatedContent/PaginatedContent.d.ts +1 -0
- package/dist/components/shared/SearchBar/SearchBar.d.ts +1 -0
- package/dist/components/shared/SearchHeader/SearchHeader.d.ts +1 -0
- package/dist/hooks/useTooltipPosition.d.ts +15 -0
- package/dist/long-bow.cjs.development.js +758 -509
- 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 +665 -416
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/InformationCenter/InformationCenter.tsx +5 -19
- package/src/components/InformationCenter/sections/bestiary/BestiaryAdvancedFilters.tsx +95 -0
- package/src/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.tsx +124 -84
- package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.tsx +31 -7
- package/src/components/InformationCenter/sections/items/InformationCenterItemsSection.tsx +76 -78
- package/src/components/InformationCenter/sections/items/ItemsAdvancedFilters.tsx +80 -0
- package/src/components/InformationCenter/shared/BaseInformationDetails.tsx +34 -11
- package/src/components/Item/Cards/ItemInfo.tsx +1 -18
- package/src/components/Item/Inventory/ItemSlot.tsx +3 -15
- package/src/components/Item/Inventory/ItemSlotRenderer.tsx +2 -6
- package/src/components/shared/AdvancedFilters/AdvancedFilters.tsx +279 -0
- package/src/components/shared/Collapsible/Collapsible.tsx +1 -1
- package/src/components/shared/PaginatedContent/PaginatedContent.tsx +1 -0
- package/src/components/shared/SearchBar/SearchBar.tsx +15 -5
- package/src/components/shared/SearchHeader/SearchHeader.tsx +2 -0
- package/src/hooks/useTooltipPosition.ts +73 -0
- package/src/mocks/itemContainer.mocks.ts +0 -7
- package/dist/components/Item/Inventory/ItemSlotQuality.d.ts +0 -2
- package/src/components/Item/Inventory/ItemSlotQuality.ts +0 -18
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AiFillCaretRight,
|
|
4
|
+
AiFillFilter,
|
|
5
|
+
AiOutlineFilter,
|
|
6
|
+
} from 'react-icons/ai';
|
|
7
|
+
import styled from 'styled-components';
|
|
8
|
+
import { Dropdown } from '../../Dropdown';
|
|
9
|
+
import { Input } from '../../Input';
|
|
10
|
+
|
|
11
|
+
export interface IFilterOption {
|
|
12
|
+
id: number;
|
|
13
|
+
value: string;
|
|
14
|
+
option: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IFilterSection {
|
|
18
|
+
type: 'range' | 'dropdown';
|
|
19
|
+
label: string;
|
|
20
|
+
key: string;
|
|
21
|
+
options?: IFilterOption[];
|
|
22
|
+
value?: string | [number | undefined, number | undefined];
|
|
23
|
+
onChange: (value: any) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface IAdvancedFiltersProps {
|
|
27
|
+
isOpen: boolean;
|
|
28
|
+
onToggle: () => void;
|
|
29
|
+
sections: IFilterSection[];
|
|
30
|
+
onClearAll: () => void;
|
|
31
|
+
hasActiveFilters: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const AdvancedFilters: React.FC<IAdvancedFiltersProps> = ({
|
|
35
|
+
isOpen,
|
|
36
|
+
onToggle,
|
|
37
|
+
sections,
|
|
38
|
+
onClearAll,
|
|
39
|
+
hasActiveFilters,
|
|
40
|
+
}) => {
|
|
41
|
+
const renderFilterSection = (section: IFilterSection) => {
|
|
42
|
+
switch (section.type) {
|
|
43
|
+
case 'range':
|
|
44
|
+
const rangeValue = section.value as [
|
|
45
|
+
number | undefined,
|
|
46
|
+
number | undefined
|
|
47
|
+
];
|
|
48
|
+
return (
|
|
49
|
+
<FilterSection key={section.key}>
|
|
50
|
+
<Label>{section.label}</Label>
|
|
51
|
+
<RangeInputs>
|
|
52
|
+
<Input
|
|
53
|
+
type="number"
|
|
54
|
+
min={0}
|
|
55
|
+
placeholder="Min"
|
|
56
|
+
value={rangeValue[0] || ''}
|
|
57
|
+
onChange={e =>
|
|
58
|
+
section.onChange([
|
|
59
|
+
e.target.value ? Number(e.target.value) : undefined,
|
|
60
|
+
rangeValue[1],
|
|
61
|
+
])
|
|
62
|
+
}
|
|
63
|
+
/>
|
|
64
|
+
<AiFillCaretRight />
|
|
65
|
+
<Input
|
|
66
|
+
type="number"
|
|
67
|
+
min={0}
|
|
68
|
+
placeholder="Max"
|
|
69
|
+
value={rangeValue[1] || ''}
|
|
70
|
+
onChange={e =>
|
|
71
|
+
section.onChange([
|
|
72
|
+
rangeValue[0],
|
|
73
|
+
e.target.value ? Number(e.target.value) : undefined,
|
|
74
|
+
])
|
|
75
|
+
}
|
|
76
|
+
/>
|
|
77
|
+
</RangeInputs>
|
|
78
|
+
</FilterSection>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
case 'dropdown':
|
|
82
|
+
return (
|
|
83
|
+
<FilterSection key={section.key}>
|
|
84
|
+
<Label>{section.label}</Label>
|
|
85
|
+
<Dropdown
|
|
86
|
+
options={section.options || []}
|
|
87
|
+
onChange={section.onChange}
|
|
88
|
+
width="100%"
|
|
89
|
+
/>
|
|
90
|
+
</FilterSection>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
default:
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Container>
|
|
100
|
+
<FilterButton onClick={onToggle} $hasActiveFilters={hasActiveFilters}>
|
|
101
|
+
{hasActiveFilters ? (
|
|
102
|
+
<AiFillFilter size={20} />
|
|
103
|
+
) : (
|
|
104
|
+
<AiOutlineFilter size={20} />
|
|
105
|
+
)}
|
|
106
|
+
<FilterCount $visible={hasActiveFilters}>
|
|
107
|
+
{
|
|
108
|
+
sections.filter(section => {
|
|
109
|
+
if (section.type === 'range') {
|
|
110
|
+
const rangeValue = section.value as [
|
|
111
|
+
number | undefined,
|
|
112
|
+
number | undefined
|
|
113
|
+
];
|
|
114
|
+
return (
|
|
115
|
+
rangeValue[0] !== undefined || rangeValue[1] !== undefined
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return section.value !== 'all';
|
|
119
|
+
}).length
|
|
120
|
+
}
|
|
121
|
+
</FilterCount>
|
|
122
|
+
</FilterButton>
|
|
123
|
+
|
|
124
|
+
{isOpen && (
|
|
125
|
+
<FiltersPanel>
|
|
126
|
+
<FilterHeader>
|
|
127
|
+
<FilterTitle>Advanced Filters</FilterTitle>
|
|
128
|
+
</FilterHeader>
|
|
129
|
+
|
|
130
|
+
{sections.map(renderFilterSection)}
|
|
131
|
+
|
|
132
|
+
{hasActiveFilters && (
|
|
133
|
+
<ClearFiltersButton onClick={onClearAll}>
|
|
134
|
+
Clear All Filters
|
|
135
|
+
</ClearFiltersButton>
|
|
136
|
+
)}
|
|
137
|
+
</FiltersPanel>
|
|
138
|
+
)}
|
|
139
|
+
</Container>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const Container = styled.div`
|
|
144
|
+
position: relative;
|
|
145
|
+
margin-left: 0.5rem;
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
const FilterButton = styled.button<{ $hasActiveFilters: boolean }>`
|
|
149
|
+
position: relative;
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
justify-content: center;
|
|
153
|
+
width: 36px;
|
|
154
|
+
height: 36px;
|
|
155
|
+
background: transparent;
|
|
156
|
+
border: none;
|
|
157
|
+
color: ${({ $hasActiveFilters }) =>
|
|
158
|
+
$hasActiveFilters ? '#ffd700' : 'rgba(255, 255, 255, 0.8)'};
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
transition: all 0.2s;
|
|
161
|
+
|
|
162
|
+
&:hover {
|
|
163
|
+
color: ${({ $hasActiveFilters }) =>
|
|
164
|
+
$hasActiveFilters ? '#ffd700' : '#ffffff'};
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
const FilterCount = styled.div<{ $visible: boolean }>`
|
|
169
|
+
position: absolute;
|
|
170
|
+
top: -4px;
|
|
171
|
+
right: -4px;
|
|
172
|
+
background: #ffd700;
|
|
173
|
+
color: #000;
|
|
174
|
+
border-radius: 50%;
|
|
175
|
+
width: 16px;
|
|
176
|
+
height: 16px;
|
|
177
|
+
font-size: 10px;
|
|
178
|
+
font-weight: bold;
|
|
179
|
+
display: flex;
|
|
180
|
+
align-items: center;
|
|
181
|
+
justify-content: center;
|
|
182
|
+
opacity: ${({ $visible }) => ($visible ? 1 : 0)};
|
|
183
|
+
transform: scale(${({ $visible }) => ($visible ? 1 : 0.5)});
|
|
184
|
+
transition: all 0.2s;
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
const FiltersPanel = styled.div`
|
|
188
|
+
position: absolute;
|
|
189
|
+
top: calc(100% + 0.75rem);
|
|
190
|
+
right: -8px;
|
|
191
|
+
background: #1a1a1a;
|
|
192
|
+
border: 1px solid #333;
|
|
193
|
+
border-radius: 6px;
|
|
194
|
+
padding: 1rem;
|
|
195
|
+
z-index: 1000;
|
|
196
|
+
min-width: 280px;
|
|
197
|
+
display: flex;
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
gap: 1rem;
|
|
200
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
201
|
+
|
|
202
|
+
&:before {
|
|
203
|
+
content: '';
|
|
204
|
+
position: absolute;
|
|
205
|
+
top: -6px;
|
|
206
|
+
right: 16px;
|
|
207
|
+
width: 12px;
|
|
208
|
+
height: 12px;
|
|
209
|
+
background: #1a1a1a;
|
|
210
|
+
border-left: 1px solid #333;
|
|
211
|
+
border-top: 1px solid #333;
|
|
212
|
+
transform: rotate(45deg);
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
const FilterHeader = styled.div`
|
|
217
|
+
display: flex;
|
|
218
|
+
align-items: center;
|
|
219
|
+
justify-content: space-between;
|
|
220
|
+
padding-bottom: 0.75rem;
|
|
221
|
+
margin-bottom: 0.5rem;
|
|
222
|
+
border-bottom: 1px solid #333;
|
|
223
|
+
`;
|
|
224
|
+
|
|
225
|
+
const FilterTitle = styled.div`
|
|
226
|
+
font-weight: 600;
|
|
227
|
+
color: #ffd700;
|
|
228
|
+
font-size: 0.875rem;
|
|
229
|
+
`;
|
|
230
|
+
|
|
231
|
+
const FilterSection = styled.div`
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
gap: 0.5rem;
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
const Label = styled.div`
|
|
238
|
+
color: #999;
|
|
239
|
+
font-size: 0.75rem;
|
|
240
|
+
text-transform: uppercase;
|
|
241
|
+
letter-spacing: 0.05em;
|
|
242
|
+
`;
|
|
243
|
+
|
|
244
|
+
const RangeInputs = styled.div`
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
gap: 0.5rem;
|
|
248
|
+
|
|
249
|
+
input {
|
|
250
|
+
width: 80px;
|
|
251
|
+
background: #262626 !important;
|
|
252
|
+
border: 1px solid #333 !important;
|
|
253
|
+
color: #fff !important;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
svg {
|
|
257
|
+
color: #666;
|
|
258
|
+
font-size: 0.875rem;
|
|
259
|
+
}
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
const ClearFiltersButton = styled.button`
|
|
263
|
+
width: 100%;
|
|
264
|
+
background: transparent;
|
|
265
|
+
color: #666;
|
|
266
|
+
border: none;
|
|
267
|
+
padding: 0.75rem 0;
|
|
268
|
+
margin-top: 0.5rem;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
font-size: 0.75rem;
|
|
271
|
+
transition: all 0.2s;
|
|
272
|
+
border-top: 1px solid #333;
|
|
273
|
+
text-transform: uppercase;
|
|
274
|
+
letter-spacing: 0.05em;
|
|
275
|
+
|
|
276
|
+
&:hover {
|
|
277
|
+
color: #ffd700;
|
|
278
|
+
}
|
|
279
|
+
`;
|
|
@@ -7,6 +7,7 @@ interface ISearchBarProps {
|
|
|
7
7
|
onChange: (value: string) => void;
|
|
8
8
|
placeholder?: string;
|
|
9
9
|
className?: string;
|
|
10
|
+
rightElement?: React.ReactNode;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export const SearchBar: React.FC<ISearchBarProps> = ({
|
|
@@ -14,7 +15,10 @@ export const SearchBar: React.FC<ISearchBarProps> = ({
|
|
|
14
15
|
onChange,
|
|
15
16
|
placeholder,
|
|
16
17
|
className,
|
|
18
|
+
rightElement,
|
|
17
19
|
}) => {
|
|
20
|
+
const hasRightElement = Boolean(rightElement);
|
|
21
|
+
|
|
18
22
|
return (
|
|
19
23
|
<Container className={className}>
|
|
20
24
|
<Input
|
|
@@ -23,9 +27,11 @@ export const SearchBar: React.FC<ISearchBarProps> = ({
|
|
|
23
27
|
onChange={e => onChange(e.target.value)}
|
|
24
28
|
placeholder={placeholder}
|
|
25
29
|
className="rpgui-input"
|
|
30
|
+
$hasRightElement={hasRightElement}
|
|
26
31
|
/>
|
|
27
32
|
<IconContainer>
|
|
28
33
|
<SearchIcon />
|
|
34
|
+
{rightElement}
|
|
29
35
|
</IconContainer>
|
|
30
36
|
</Container>
|
|
31
37
|
);
|
|
@@ -36,9 +42,10 @@ const Container = styled.div`
|
|
|
36
42
|
width: 100%;
|
|
37
43
|
`;
|
|
38
44
|
|
|
39
|
-
const Input = styled.input
|
|
45
|
+
const Input = styled.input<{ $hasRightElement: boolean }>`
|
|
40
46
|
width: 100%;
|
|
41
|
-
padding-right:
|
|
47
|
+
padding-right: ${props =>
|
|
48
|
+
props.$hasRightElement ? '6rem' : '2.5rem'} !important;
|
|
42
49
|
background: rgba(0, 0, 0, 0.2) !important;
|
|
43
50
|
border: 2px solid #f59e0b !important;
|
|
44
51
|
box-shadow: 0 0 10px rgba(245, 158, 11, 0.3);
|
|
@@ -58,10 +65,13 @@ const IconContainer = styled.div`
|
|
|
58
65
|
transform: translateY(-50%);
|
|
59
66
|
display: flex;
|
|
60
67
|
align-items: center;
|
|
61
|
-
|
|
62
|
-
width: 24px;
|
|
63
|
-
height: 24px;
|
|
68
|
+
gap: 0.5rem;
|
|
64
69
|
pointer-events: none;
|
|
70
|
+
z-index: 1;
|
|
71
|
+
|
|
72
|
+
> * {
|
|
73
|
+
pointer-events: auto;
|
|
74
|
+
}
|
|
65
75
|
`;
|
|
66
76
|
|
|
67
77
|
const SearchIcon = styled(FaSearch)`
|
|
@@ -8,6 +8,7 @@ interface ISearchHeaderProps {
|
|
|
8
8
|
value: string;
|
|
9
9
|
onChange: (value: string) => void;
|
|
10
10
|
placeholder?: string;
|
|
11
|
+
rightElement?: React.ReactNode;
|
|
11
12
|
};
|
|
12
13
|
filterOptions?: {
|
|
13
14
|
options: IOptionsProps[];
|
|
@@ -33,6 +34,7 @@ export const SearchHeader: React.FC<ISearchHeaderProps> = ({
|
|
|
33
34
|
value={searchOptions.value}
|
|
34
35
|
onChange={searchOptions.onChange}
|
|
35
36
|
placeholder={searchOptions.placeholder || 'Search...'}
|
|
37
|
+
rightElement={searchOptions.rightElement}
|
|
36
38
|
/>
|
|
37
39
|
</SearchContainer>
|
|
38
40
|
)}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const TOOLTIP_WIDTH = 300;
|
|
4
|
+
const TOOLTIP_OFFSET = 10;
|
|
5
|
+
const MIN_VISIBLE_HEIGHT = 100;
|
|
6
|
+
|
|
7
|
+
interface ITooltipPosition {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ITooltipState<T> {
|
|
13
|
+
item: T | null;
|
|
14
|
+
position: ITooltipPosition;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const useTooltipPosition = <T>() => {
|
|
18
|
+
const [tooltipState, setTooltipState] = useState<ITooltipState<T> | null>(
|
|
19
|
+
null
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const calculateTooltipPosition = (rect: DOMRect): ITooltipPosition => {
|
|
23
|
+
const viewportWidth = window.innerWidth;
|
|
24
|
+
const viewportHeight = window.innerHeight;
|
|
25
|
+
|
|
26
|
+
// Try to position to the right first
|
|
27
|
+
let x = rect.right + TOOLTIP_OFFSET;
|
|
28
|
+
|
|
29
|
+
// If it would overflow right, try positioning to the left
|
|
30
|
+
if (x + TOOLTIP_WIDTH > viewportWidth - TOOLTIP_OFFSET) {
|
|
31
|
+
x = rect.left - TOOLTIP_WIDTH - TOOLTIP_OFFSET;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// If left positioning would go off screen, position relative to viewport
|
|
35
|
+
if (x < TOOLTIP_OFFSET) {
|
|
36
|
+
x = TOOLTIP_OFFSET;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Position vertically aligned with the top of the element
|
|
40
|
+
let y = rect.top;
|
|
41
|
+
|
|
42
|
+
// Ensure tooltip doesn't go above viewport
|
|
43
|
+
if (y < TOOLTIP_OFFSET) {
|
|
44
|
+
y = TOOLTIP_OFFSET;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Ensure some part of tooltip is always visible if element is near bottom
|
|
48
|
+
if (y > viewportHeight - MIN_VISIBLE_HEIGHT) {
|
|
49
|
+
y = viewportHeight - MIN_VISIBLE_HEIGHT;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { x, y };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleMouseEnter = (item: T, event: React.MouseEvent) => {
|
|
56
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
57
|
+
setTooltipState({
|
|
58
|
+
item,
|
|
59
|
+
position: calculateTooltipPosition(rect),
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const handleMouseLeave = () => {
|
|
64
|
+
setTooltipState(null);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
tooltipState,
|
|
69
|
+
handleMouseEnter,
|
|
70
|
+
handleMouseLeave,
|
|
71
|
+
TOOLTIP_WIDTH,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IItem,
|
|
3
3
|
IItemContainer,
|
|
4
|
-
ItemQualityLevel,
|
|
5
4
|
ItemRarities,
|
|
6
5
|
ItemSlotType,
|
|
7
6
|
ItemSubType,
|
|
@@ -82,7 +81,6 @@ export const items: IItem[] = [
|
|
|
82
81
|
createdAt: '2022-06-04T03:18:09.335Z',
|
|
83
82
|
updatedAt: '2022-06-04T18:16:49.056Z',
|
|
84
83
|
rarity: ItemRarities.Legendary,
|
|
85
|
-
quality: ItemQualityLevel.Mythic,
|
|
86
84
|
canBePurchasedOnlyByPremiumPlans: [
|
|
87
85
|
UserAccountTypes.PremiumGold,
|
|
88
86
|
UserAccountTypes.PremiumUltimate,
|
|
@@ -169,7 +167,6 @@ export const items: IItem[] = [
|
|
|
169
167
|
createdAt: '2022-06-04T03:18:09.335Z',
|
|
170
168
|
updatedAt: '2022-06-04T18:16:49.056Z',
|
|
171
169
|
rarity: ItemRarities.Epic,
|
|
172
|
-
quality: ItemQualityLevel.Normal,
|
|
173
170
|
attachedGems: [
|
|
174
171
|
{
|
|
175
172
|
gemEntityEffectsAdd: ['poison'],
|
|
@@ -224,7 +221,6 @@ export const items: IItem[] = [
|
|
|
224
221
|
createdAt: '2022-06-04T03:18:09.335Z',
|
|
225
222
|
updatedAt: '2022-06-04T18:16:49.056Z',
|
|
226
223
|
rarity: ItemRarities.Uncommon,
|
|
227
|
-
quality: ItemQualityLevel.HighQuality,
|
|
228
224
|
},
|
|
229
225
|
{
|
|
230
226
|
_id: '629acek4j7c8e8002ff60034',
|
|
@@ -259,7 +255,6 @@ export const items: IItem[] = [
|
|
|
259
255
|
createdAt: '2022-06-04T03:18:09.335Z',
|
|
260
256
|
updatedAt: '2022-06-04T18:16:49.056Z',
|
|
261
257
|
rarity: ItemRarities.Rare,
|
|
262
|
-
quality: ItemQualityLevel.Ancient,
|
|
263
258
|
},
|
|
264
259
|
{
|
|
265
260
|
_id: '629acek4j7c8e8002fg60034',
|
|
@@ -294,7 +289,6 @@ export const items: IItem[] = [
|
|
|
294
289
|
createdAt: '2022-06-04T03:18:09.335Z',
|
|
295
290
|
updatedAt: '2022-06-04T18:16:49.056Z',
|
|
296
291
|
rarity: ItemRarities.Common,
|
|
297
|
-
quality: ItemQualityLevel.Mastercrafted,
|
|
298
292
|
},
|
|
299
293
|
{
|
|
300
294
|
_id: '392acek4j7c8e8002ff60403',
|
|
@@ -329,7 +323,6 @@ export const items: IItem[] = [
|
|
|
329
323
|
createdAt: '2022-06-04T03:18:09.335Z',
|
|
330
324
|
updatedAt: '2022-06-04T18:16:49.056Z',
|
|
331
325
|
rarity: ItemRarities.Common,
|
|
332
|
-
quality: ItemQualityLevel.Exceptional,
|
|
333
326
|
},
|
|
334
327
|
createBagItem('392acek4j7c8e8002ff60404', '#FF0000'), // red
|
|
335
328
|
createBagItem('392acek4j7c8e8002ff60405', '#0000FF'), // blue
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { IItem, ItemQualityLevel } from '@rpg-engine/shared';
|
|
2
|
-
|
|
3
|
-
export const qualityColorHex = (item: IItem | null) => {
|
|
4
|
-
switch (item?.quality) {
|
|
5
|
-
case ItemQualityLevel.HighQuality:
|
|
6
|
-
return '#00bfff';
|
|
7
|
-
case ItemQualityLevel.Exceptional:
|
|
8
|
-
return '#ff8c00';
|
|
9
|
-
case ItemQualityLevel.Mastercrafted:
|
|
10
|
-
return '#ff00ff';
|
|
11
|
-
case ItemQualityLevel.Ancient:
|
|
12
|
-
return '#ffd700';
|
|
13
|
-
case ItemQualityLevel.Mythic:
|
|
14
|
-
return '#ff0080';
|
|
15
|
-
default:
|
|
16
|
-
return 'transparent';
|
|
17
|
-
}
|
|
18
|
-
};
|