@rpg-engine/long-bow 0.1.66 → 0.1.67
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/DraggableContainer.d.ts +3 -0
- package/dist/components/Item/Inventory/ItemContainer.d.ts +4 -0
- package/dist/components/Item/Inventory/ItemSlot.d.ts +3 -2
- package/dist/components/Item/Inventory/itemContainerHelper.d.ts +7 -0
- package/dist/hooks/useOutsideAlerter.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +201 -137
- 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 +203 -139
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/types/eventTypes.d.ts +4 -0
- package/package.json +5 -5
- package/src/components/DraggableContainer.tsx +18 -3
- package/src/components/Item/Cards/ItemCard.tsx +7 -1
- package/src/components/Item/Inventory/ItemContainer.tsx +129 -132
- package/src/components/Item/Inventory/ItemSlot.tsx +24 -9
- package/src/components/Item/Inventory/itemContainerHelper.ts +44 -0
- package/src/hooks/useOutsideAlerter.ts +25 -0
- package/src/types/eventTypes.ts +4 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ActionsByItemType,
|
|
3
2
|
IItem,
|
|
4
3
|
IItemContainer,
|
|
5
4
|
IPayloadProps,
|
|
6
5
|
ItemSocketEvents,
|
|
7
|
-
ItemType,
|
|
8
6
|
} from '@rpg-engine/shared';
|
|
9
|
-
import React, { useEffect, useState } from 'react';
|
|
7
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
10
8
|
import styled from 'styled-components';
|
|
9
|
+
import { useOutsideClick } from '../../../hooks/useOutsideAlerter';
|
|
10
|
+
import { IPosition } from '../../../types/eventTypes';
|
|
11
11
|
import { DraggableContainer } from '../../DraggableContainer';
|
|
12
12
|
import { ListMenu } from '../../ListMenu';
|
|
13
13
|
import { RPGUIContainerTypes } from '../../RPGUIContainer';
|
|
14
14
|
import { ItemCard } from '../Cards/ItemCard';
|
|
15
|
+
import { handleContextMenuList } from './itemContainerHelper';
|
|
15
16
|
import { ItemSlot } from './ItemSlot';
|
|
16
17
|
|
|
17
18
|
export interface IItemContainerProps {
|
|
@@ -19,9 +20,10 @@ export interface IItemContainerProps {
|
|
|
19
20
|
onClose: () => void;
|
|
20
21
|
onMouseOver: (e: any, slotIndex: number, item: IItem | null) => void;
|
|
21
22
|
onActionSelected: (payload: any) => void;
|
|
23
|
+
initialPosition?: { x: number; y: number };
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
interface
|
|
26
|
+
interface IContextItemProps {
|
|
25
27
|
visible: boolean;
|
|
26
28
|
posX: number;
|
|
27
29
|
posY: number;
|
|
@@ -30,127 +32,111 @@ interface contextItemPropx {
|
|
|
30
32
|
contextActions: any;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
interface IHoverDetailProps {
|
|
36
|
+
visible: boolean;
|
|
37
|
+
posX: number;
|
|
38
|
+
posY: number;
|
|
39
|
+
item: IItem | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
34
43
|
itemContainer,
|
|
35
44
|
onClose,
|
|
36
45
|
onMouseOver,
|
|
37
46
|
onActionSelected,
|
|
47
|
+
initialPosition = { x: 0, y: 0 },
|
|
38
48
|
}) => {
|
|
39
|
-
|
|
49
|
+
// we use this draggable position to offset the menu position, after the container is dragged (otherwise, it bugs!)
|
|
50
|
+
const [draggablePosition, setDraggablePosition] = useState<IPosition>(
|
|
51
|
+
initialPosition
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const draggableRef = useRef(null);
|
|
55
|
+
|
|
56
|
+
useOutsideClick(draggableRef, 'item-container');
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
document.addEventListener('clickOutside', event => {
|
|
60
|
+
const e = event as CustomEvent;
|
|
61
|
+
|
|
62
|
+
if (e.detail.id === 'item-container') {
|
|
63
|
+
clearContextMenu();
|
|
64
|
+
clearItemHoverDetail();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return () => {
|
|
69
|
+
document.removeEventListener('clickOutside', _e => {});
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const { x: initX, y: initY } = initialPosition;
|
|
74
|
+
|
|
75
|
+
const [contextData, setContextData] = useState<IContextItemProps>({
|
|
40
76
|
visible: false,
|
|
41
|
-
posX:
|
|
42
|
-
posY:
|
|
77
|
+
posX: initX,
|
|
78
|
+
posY: initY,
|
|
43
79
|
contextActions: [],
|
|
44
80
|
slotItem: null,
|
|
45
81
|
});
|
|
46
|
-
const [itemHoverDetail] = useState({
|
|
82
|
+
const [itemHoverDetail, setItemHoverDetail] = useState<IHoverDetailProps>({
|
|
47
83
|
visible: false,
|
|
48
|
-
posX:
|
|
49
|
-
posY:
|
|
84
|
+
posX: initX,
|
|
85
|
+
posY: initY,
|
|
50
86
|
item: null,
|
|
51
87
|
});
|
|
52
|
-
let selectedSlotContext: number | null = null;
|
|
53
88
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
let contextActionMenu: any = [];
|
|
63
|
-
switch (itemType) {
|
|
64
|
-
case ItemType.Weapon:
|
|
65
|
-
case ItemType.Armor:
|
|
66
|
-
case ItemType.Accessory:
|
|
67
|
-
case ItemType.Jewelry:
|
|
68
|
-
case ItemType.Tool:
|
|
69
|
-
contextActionMenu = generateContextList(ActionsByItemType.Equipment);
|
|
70
|
-
break;
|
|
71
|
-
case ItemType.Consumable:
|
|
72
|
-
contextActionMenu = generateContextList(ActionsByItemType.Consumable);
|
|
73
|
-
break;
|
|
74
|
-
case ItemType.CraftMaterial:
|
|
75
|
-
contextActionMenu = generateContextList(
|
|
76
|
-
ActionsByItemType.CraftMaterial
|
|
77
|
-
);
|
|
78
|
-
break;
|
|
79
|
-
case ItemType.Other:
|
|
80
|
-
case ItemType.Information:
|
|
81
|
-
case ItemType.Quest:
|
|
82
|
-
case ItemType.Container:
|
|
83
|
-
contextActionMenu = generateContextList(ActionsByItemType.Other);
|
|
84
|
-
break;
|
|
85
|
-
default:
|
|
86
|
-
contextActionMenu = generateContextList(ActionsByItemType.Other);
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
return contextActionMenu;
|
|
89
|
+
const clearContextMenu = () => {
|
|
90
|
+
setContextData({
|
|
91
|
+
visible: false,
|
|
92
|
+
posX: 0,
|
|
93
|
+
posY: 0,
|
|
94
|
+
contextActions: [],
|
|
95
|
+
slotItem: null,
|
|
96
|
+
});
|
|
90
97
|
};
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
contextActions: contextActionMenu,
|
|
107
|
-
});
|
|
108
|
-
} else {
|
|
109
|
-
// console.log("Empty Slot")
|
|
110
|
-
setContextData({
|
|
111
|
-
...contextData,
|
|
112
|
-
visible: false,
|
|
113
|
-
posX: 0,
|
|
114
|
-
posY: 0,
|
|
115
|
-
slotIndex: null,
|
|
116
|
-
slotItem: null,
|
|
117
|
-
contextActions: [],
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const offClickHandler = (event: any) => {
|
|
124
|
-
console.log(event);
|
|
125
|
-
setContextData({
|
|
126
|
-
...contextData,
|
|
127
|
-
visible: false,
|
|
128
|
-
posX: 0,
|
|
129
|
-
posY: 0,
|
|
130
|
-
slotIndex: null,
|
|
131
|
-
slotItem: null,
|
|
99
|
+
const handleOnMouseHover = (
|
|
100
|
+
event: any,
|
|
101
|
+
slotIndex: number,
|
|
102
|
+
item: IItem | null,
|
|
103
|
+
x: number,
|
|
104
|
+
y: number
|
|
105
|
+
) => {
|
|
106
|
+
if (item) {
|
|
107
|
+
setItemHoverDetail({
|
|
108
|
+
...itemHoverDetail,
|
|
109
|
+
visible: true,
|
|
110
|
+
item: item,
|
|
111
|
+
posX: x - draggablePosition.x,
|
|
112
|
+
posY: y - draggablePosition.y,
|
|
132
113
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
document.removeEventListener('contextmenu', contextMenuEventHandler);
|
|
139
|
-
document.removeEventListener('click', offClickHandler);
|
|
140
|
-
};
|
|
141
|
-
}, [contextData, selectedSlotContext]);
|
|
114
|
+
onMouseOver(event, slotIndex, item);
|
|
115
|
+
} else {
|
|
116
|
+
clearItemHoverDetail();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
142
119
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
120
|
+
const clearItemHoverDetail = () => {
|
|
121
|
+
setItemHoverDetail({
|
|
122
|
+
...itemHoverDetail,
|
|
123
|
+
visible: false,
|
|
124
|
+
item: null,
|
|
125
|
+
});
|
|
147
126
|
};
|
|
148
127
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
128
|
+
const handleOnItemClick = (item: IItem, posX: number, posY: number) => {
|
|
129
|
+
const contextList = handleContextMenuList(item.type);
|
|
130
|
+
|
|
131
|
+
setContextData({
|
|
132
|
+
...contextData,
|
|
133
|
+
visible: true,
|
|
134
|
+
posX,
|
|
135
|
+
posY,
|
|
136
|
+
slotItem: item,
|
|
137
|
+
contextActions: contextList,
|
|
138
|
+
});
|
|
139
|
+
clearItemHoverDetail();
|
|
154
140
|
};
|
|
155
141
|
|
|
156
142
|
const onSelected = (selectedActionId: ItemSocketEvents | string): void => {
|
|
@@ -158,8 +144,8 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
|
158
144
|
actionType: selectedActionId,
|
|
159
145
|
item: contextData.slotItem,
|
|
160
146
|
};
|
|
161
|
-
// TODO: create a function to validate the selection set the paylod data.
|
|
162
147
|
onActionSelected(payloadData);
|
|
148
|
+
clearContextMenu();
|
|
163
149
|
};
|
|
164
150
|
|
|
165
151
|
const onRenderSlots = () => {
|
|
@@ -172,7 +158,10 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
|
172
158
|
slotIndex={i}
|
|
173
159
|
item={itemContainer.slots?.[i] || null}
|
|
174
160
|
onMouseOver={handleOnMouseHover}
|
|
175
|
-
|
|
161
|
+
onClick={handleOnItemClick}
|
|
162
|
+
onCancelContextMenu={() => {
|
|
163
|
+
clearContextMenu();
|
|
164
|
+
}}
|
|
176
165
|
/>
|
|
177
166
|
);
|
|
178
167
|
}
|
|
@@ -180,31 +169,39 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
|
180
169
|
};
|
|
181
170
|
|
|
182
171
|
return (
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
172
|
+
<div ref={draggableRef}>
|
|
173
|
+
<DraggableContainer
|
|
174
|
+
title={itemContainer.name || 'Container'}
|
|
175
|
+
type={RPGUIContainerTypes.Framed}
|
|
176
|
+
onCloseButton={() => onClose()}
|
|
177
|
+
width="330px"
|
|
178
|
+
cancelDrag=".item-container-body"
|
|
179
|
+
onPositionChange={({ x, y }) => {
|
|
180
|
+
setDraggablePosition({ x, y });
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
<ItemsContainer className="item-container-body">
|
|
184
|
+
{onRenderSlots()}
|
|
185
|
+
</ItemsContainer>
|
|
186
|
+
|
|
187
|
+
{contextData.visible ? (
|
|
188
|
+
<ListMenu
|
|
189
|
+
x={contextData.posX - draggablePosition.x}
|
|
190
|
+
y={contextData.posY - draggablePosition.y}
|
|
191
|
+
options={contextData.contextActions}
|
|
192
|
+
onSelected={onSelected}
|
|
193
|
+
/>
|
|
194
|
+
) : null}
|
|
195
|
+
|
|
196
|
+
{itemHoverDetail.visible ? (
|
|
197
|
+
<ItemCard
|
|
198
|
+
item={itemHoverDetail.item}
|
|
199
|
+
x={itemHoverDetail.posX}
|
|
200
|
+
y={itemHoverDetail.posY}
|
|
201
|
+
/>
|
|
202
|
+
) : null}
|
|
203
|
+
</DraggableContainer>
|
|
204
|
+
</div>
|
|
208
205
|
);
|
|
209
206
|
};
|
|
210
207
|
|
|
@@ -7,17 +7,25 @@ import { SpriteFromAtlas } from '../SpriteFromAtlas';
|
|
|
7
7
|
interface IProps {
|
|
8
8
|
slotIndex: number;
|
|
9
9
|
item: IItem | null;
|
|
10
|
-
onMouseOver: (
|
|
11
|
-
|
|
10
|
+
onMouseOver: (
|
|
11
|
+
event: any,
|
|
12
|
+
slotIndex: number,
|
|
13
|
+
item: IItem | null,
|
|
14
|
+
x: number,
|
|
15
|
+
y: number
|
|
16
|
+
) => void;
|
|
17
|
+
onClick: (item: IItem, posX: number, posY: number) => void;
|
|
18
|
+
onCancelContextMenu: () => void;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
export const ItemSlot: React.FC<IProps> = ({
|
|
15
22
|
slotIndex,
|
|
16
23
|
item,
|
|
17
24
|
onMouseOver,
|
|
18
|
-
|
|
25
|
+
onClick,
|
|
26
|
+
onCancelContextMenu,
|
|
19
27
|
}) => {
|
|
20
|
-
const
|
|
28
|
+
const getLeftPositionValue = (quantity: number) => {
|
|
21
29
|
if (quantity > 0 && quantity < 10) return '2.5rem';
|
|
22
30
|
else if (quantity > 9 && quantity < 100) return '2.0rem';
|
|
23
31
|
else if (quantity > 99) return '1rem';
|
|
@@ -26,11 +34,18 @@ export const ItemSlot: React.FC<IProps> = ({
|
|
|
26
34
|
|
|
27
35
|
return (
|
|
28
36
|
<Container
|
|
29
|
-
onContextMenu={event => {
|
|
30
|
-
onActionContextMenu(event, slotIndex);
|
|
31
|
-
}}
|
|
32
37
|
className="rpgui-icon empty-slot"
|
|
33
|
-
onMouseOver={event =>
|
|
38
|
+
onMouseOver={event =>
|
|
39
|
+
onMouseOver(event, slotIndex, item, event.clientX, event.clientY)
|
|
40
|
+
}
|
|
41
|
+
onClick={e => {
|
|
42
|
+
if (item) {
|
|
43
|
+
console.log(e);
|
|
44
|
+
onClick(item, e.clientX, e.clientY);
|
|
45
|
+
} else {
|
|
46
|
+
onCancelContextMenu();
|
|
47
|
+
}
|
|
48
|
+
}}
|
|
34
49
|
>
|
|
35
50
|
{item && item.texturePath ? (
|
|
36
51
|
<SpriteFromAtlas
|
|
@@ -41,7 +56,7 @@ export const ItemSlot: React.FC<IProps> = ({
|
|
|
41
56
|
/>
|
|
42
57
|
) : null}
|
|
43
58
|
{item && item.isStackable && item?.stackQty ? (
|
|
44
|
-
<ItemQty left={
|
|
59
|
+
<ItemQty left={getLeftPositionValue(item.stackQty)}>
|
|
45
60
|
{' '}
|
|
46
61
|
{item.stackQty}{' '}
|
|
47
62
|
</ItemQty>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ActionsByItemType, ItemType } from '@rpg-engine/shared';
|
|
2
|
+
|
|
3
|
+
interface IContextMenuItem {
|
|
4
|
+
id: string;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const handleContextMenuList = (itemType: ItemType) => {
|
|
9
|
+
const generateContextList = (actionsByTypeList: any) => {
|
|
10
|
+
const contextMenu: IContextMenuItem[] = actionsByTypeList.map(
|
|
11
|
+
(action: string) => {
|
|
12
|
+
return { id: action, text: action };
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
return contextMenu;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let contextActionMenu: IContextMenuItem[] = [];
|
|
19
|
+
switch (itemType) {
|
|
20
|
+
case ItemType.Weapon:
|
|
21
|
+
case ItemType.Armor:
|
|
22
|
+
case ItemType.Accessory:
|
|
23
|
+
case ItemType.Jewelry:
|
|
24
|
+
case ItemType.Tool:
|
|
25
|
+
contextActionMenu = generateContextList(ActionsByItemType.Equipment);
|
|
26
|
+
break;
|
|
27
|
+
case ItemType.Consumable:
|
|
28
|
+
contextActionMenu = generateContextList(ActionsByItemType.Consumable);
|
|
29
|
+
break;
|
|
30
|
+
case ItemType.CraftMaterial:
|
|
31
|
+
contextActionMenu = generateContextList(ActionsByItemType.CraftMaterial);
|
|
32
|
+
break;
|
|
33
|
+
case ItemType.Other:
|
|
34
|
+
case ItemType.Information:
|
|
35
|
+
case ItemType.Quest:
|
|
36
|
+
case ItemType.Container:
|
|
37
|
+
contextActionMenu = generateContextList(ActionsByItemType.Other);
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
contextActionMenu = generateContextList(ActionsByItemType.Other);
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
return contextActionMenu;
|
|
44
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useOutsideClick(ref: any, id: string) {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
/**
|
|
6
|
+
* Alert if clicked on outside of element
|
|
7
|
+
*/
|
|
8
|
+
function handleClickOutside(event: any) {
|
|
9
|
+
if (ref.current && !ref.current.contains(event.target)) {
|
|
10
|
+
const event = new CustomEvent('clickOutside', {
|
|
11
|
+
detail: {
|
|
12
|
+
id,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
document.dispatchEvent(event);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Bind the event listener
|
|
19
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
20
|
+
return () => {
|
|
21
|
+
// Unbind the event listener on clean up
|
|
22
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
23
|
+
};
|
|
24
|
+
}, [ref]);
|
|
25
|
+
}
|