@rpg-engine/long-bow 0.7.89 → 0.7.91
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/CraftBook/CraftBook.d.ts +1 -1
- package/dist/components/CraftBook/utils/calculateMaxCraftable.d.ts +2 -0
- package/dist/long-bow.cjs.development.js +50 -6
- 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 +50 -6
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CraftBook/CraftBook.tsx +55 -4
- package/src/components/CraftBook/utils/calculateMaxCraftable.ts +30 -0
- package/src/stories/Features/craftbook/CraftBook.stories.tsx +6 -5
package/package.json
CHANGED
|
@@ -13,13 +13,14 @@ import { DraggableContainer } from '../DraggableContainer';
|
|
|
13
13
|
import { InputRadio } from '../InputRadio';
|
|
14
14
|
import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
|
|
15
15
|
import { CraftingRecipe } from './CraftingRecipe';
|
|
16
|
+
import { calculateMaxCraftable } from './utils/calculateMaxCraftable';
|
|
16
17
|
|
|
17
18
|
export interface IItemCraftSelectorProps {
|
|
18
19
|
atlasJSON: any;
|
|
19
20
|
atlasIMG: any;
|
|
20
21
|
onClose: () => void;
|
|
21
22
|
onSelect: (value: string) => void;
|
|
22
|
-
onCraftItem: (value: string | undefined) => void;
|
|
23
|
+
onCraftItem: (value: string | undefined, maxCraftable: number) => void;
|
|
23
24
|
craftablesItems: ICraftableItem[];
|
|
24
25
|
equipmentSet?: IEquipmentSet | null;
|
|
25
26
|
inventory?: IItemContainer | null;
|
|
@@ -66,7 +67,7 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
|
|
|
66
67
|
savedSelectedType ?? Object.keys(ItemSubType)[0]
|
|
67
68
|
);
|
|
68
69
|
const [size, setSize] = useState<{ width: string; height: string }>();
|
|
69
|
-
|
|
70
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
70
71
|
const [isCraftingDisabled, setIsCraftingDisabled] = useState(false);
|
|
71
72
|
|
|
72
73
|
useEffect(() => {
|
|
@@ -149,6 +150,15 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
|
|
|
149
150
|
));
|
|
150
151
|
};
|
|
151
152
|
|
|
153
|
+
const filteredCraftableItems = craftablesItems?.filter(item => {
|
|
154
|
+
const matchesSearch = item.name
|
|
155
|
+
.toLowerCase()
|
|
156
|
+
.includes(searchTerm.toLowerCase());
|
|
157
|
+
const matchesCategory =
|
|
158
|
+
selectedType === 'Suggested' || item.type === selectedType;
|
|
159
|
+
return matchesSearch && matchesCategory;
|
|
160
|
+
});
|
|
161
|
+
|
|
152
162
|
if (!size) return null;
|
|
153
163
|
|
|
154
164
|
return (
|
|
@@ -168,6 +178,15 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
|
|
|
168
178
|
<div style={{ width: '100%' }}>
|
|
169
179
|
<Title>Craftbook</Title>
|
|
170
180
|
<Subtitle>Select an item to craft</Subtitle>
|
|
181
|
+
<SearchContainer>
|
|
182
|
+
<input
|
|
183
|
+
type="text"
|
|
184
|
+
className="rpgui-input"
|
|
185
|
+
placeholder="Search items..."
|
|
186
|
+
value={searchTerm}
|
|
187
|
+
onChange={e => setSearchTerm(e.target.value)}
|
|
188
|
+
/>
|
|
189
|
+
</SearchContainer>
|
|
171
190
|
<hr className="golden" />
|
|
172
191
|
</div>
|
|
173
192
|
|
|
@@ -177,7 +196,7 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
|
|
|
177
196
|
</ItemTypes>
|
|
178
197
|
|
|
179
198
|
<RadioInputScroller className="inputRadioCraftBook">
|
|
180
|
-
{
|
|
199
|
+
{filteredCraftableItems?.map(item => (
|
|
181
200
|
<CraftingRecipe
|
|
182
201
|
key={item.key}
|
|
183
202
|
atlasIMG={atlasIMG}
|
|
@@ -203,7 +222,15 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
|
|
|
203
222
|
onPointerDown={() => {
|
|
204
223
|
if (!craftItemKey || isCraftingDisabled) return;
|
|
205
224
|
|
|
206
|
-
|
|
225
|
+
const selectedItem = craftablesItems.find(
|
|
226
|
+
item => item.key === craftItemKey
|
|
227
|
+
);
|
|
228
|
+
const maxCraftable = calculateMaxCraftable(
|
|
229
|
+
selectedItem,
|
|
230
|
+
inventory
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
onCraftItem(craftItemKey, maxCraftable);
|
|
207
234
|
setIsCraftingDisabled(true);
|
|
208
235
|
|
|
209
236
|
setTimeout(() => {
|
|
@@ -295,3 +322,27 @@ const ItemTypes = styled.div`
|
|
|
295
322
|
width: 100%;
|
|
296
323
|
}
|
|
297
324
|
`;
|
|
325
|
+
|
|
326
|
+
const SearchContainer = styled.div`
|
|
327
|
+
margin: 8px 0;
|
|
328
|
+
padding: 0 16px;
|
|
329
|
+
|
|
330
|
+
input {
|
|
331
|
+
width: 100%;
|
|
332
|
+
font-size: 0.8rem;
|
|
333
|
+
padding: 8px 12px;
|
|
334
|
+
background-color: rgba(0, 0, 0, 0.3);
|
|
335
|
+
border: none;
|
|
336
|
+
color: white;
|
|
337
|
+
border-radius: 4px;
|
|
338
|
+
|
|
339
|
+
&::placeholder {
|
|
340
|
+
color: rgba(255, 255, 255, 0.5);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
&:focus {
|
|
344
|
+
outline: none;
|
|
345
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ICraftableItem, IItem, IItemContainer } from '@rpg-engine/shared';
|
|
2
|
+
|
|
3
|
+
export function calculateMaxCraftable(
|
|
4
|
+
recipe: ICraftableItem | undefined,
|
|
5
|
+
inventory: IItemContainer | null | undefined
|
|
6
|
+
): number {
|
|
7
|
+
if (!inventory || !recipe?.ingredients) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return recipe.ingredients.reduce((maxPossible, ingredient) => {
|
|
12
|
+
const inventoryItem = Object.values(inventory.slots).find(
|
|
13
|
+
(item): item is IItem => item?.key === ingredient.key
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (!inventoryItem) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const possibleWithThisIngredient = Math.floor(
|
|
21
|
+
inventoryItem.stackQty ?? 0 / ingredient.qty
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (maxPossible === -1) {
|
|
25
|
+
return possibleWithThisIngredient;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Math.min(maxPossible, possibleWithThisIngredient);
|
|
29
|
+
}, -1);
|
|
30
|
+
}
|
|
@@ -22,7 +22,12 @@ const Template: Story = args => (
|
|
|
22
22
|
onSelect={onSelect}
|
|
23
23
|
onClose={() => console.log('closing Equipment Set Container')}
|
|
24
24
|
craftablesItems={craftableItems}
|
|
25
|
-
onCraftItem={
|
|
25
|
+
onCraftItem={(value, maxCraftable) => {
|
|
26
|
+
console.log('Crafting:', {
|
|
27
|
+
itemKey: value,
|
|
28
|
+
maxCraftable: maxCraftable,
|
|
29
|
+
});
|
|
30
|
+
}}
|
|
26
31
|
equipmentSet={equipmentSetMock}
|
|
27
32
|
{...args}
|
|
28
33
|
/>
|
|
@@ -31,10 +36,6 @@ const Template: Story = args => (
|
|
|
31
36
|
|
|
32
37
|
export const Default = Template.bind({});
|
|
33
38
|
|
|
34
|
-
const onCraftItem = (value: string | undefined) => {
|
|
35
|
-
console.log('Craft', value);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
39
|
const onSelect = (value: string) => {
|
|
39
40
|
console.log('Story package', value);
|
|
40
41
|
};
|