@rpg-engine/long-bow 0.1.63 → 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.
Files changed (33) hide show
  1. package/dist/components/DraggableContainer.d.ts +3 -0
  2. package/dist/components/Item/Cards/ItemCard.d.ts +9 -0
  3. package/dist/components/Item/Inventory/ItemContainer.d.ts +13 -0
  4. package/dist/components/Item/Inventory/ItemSlot.d.ts +11 -0
  5. package/dist/components/Item/Inventory/itemContainerHelper.d.ts +7 -0
  6. package/dist/components/Item/SpriteFromAtlas.d.ts +11 -0
  7. package/dist/hooks/useOutsideAlerter.d.ts +1 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/long-bow.cjs.development.js +2092 -28
  10. package/dist/long-bow.cjs.development.js.map +1 -1
  11. package/dist/long-bow.cjs.production.min.js +1 -1
  12. package/dist/long-bow.cjs.production.min.js.map +1 -1
  13. package/dist/long-bow.esm.js +2091 -30
  14. package/dist/long-bow.esm.js.map +1 -1
  15. package/dist/mocks/itemContainer.mocks.d.ts +3 -0
  16. package/dist/types/eventTypes.d.ts +4 -0
  17. package/package.json +8 -7
  18. package/src/components/Chat/Chat.tsx +2 -2
  19. package/src/components/DraggableContainer.tsx +31 -8
  20. package/src/components/Item/Cards/ItemCard.tsx +36 -0
  21. package/src/components/Item/Inventory/ItemContainer.tsx +213 -0
  22. package/src/components/Item/Inventory/ItemSlot.tsx +90 -0
  23. package/src/components/Item/Inventory/itemContainerHelper.ts +44 -0
  24. package/src/components/Item/SpriteFromAtlas.tsx +69 -0
  25. package/src/hooks/useOutsideAlerter.ts +25 -0
  26. package/src/index.tsx +3 -0
  27. package/src/mocks/atlas/items/items.json +1695 -0
  28. package/src/mocks/atlas/items/items.png +0 -0
  29. package/src/mocks/itemContainer.mocks.ts +249 -0
  30. package/src/types/eventTypes.ts +4 -0
  31. package/dist/alice.png +0 -0
  32. package/dist/imgExp.png +0 -0
  33. package/dist/space.gif +0 -0
@@ -0,0 +1,213 @@
1
+ import {
2
+ IItem,
3
+ IItemContainer,
4
+ IPayloadProps,
5
+ ItemSocketEvents,
6
+ } from '@rpg-engine/shared';
7
+ import React, { useEffect, useRef, useState } from 'react';
8
+ import styled from 'styled-components';
9
+ import { useOutsideClick } from '../../../hooks/useOutsideAlerter';
10
+ import { IPosition } from '../../../types/eventTypes';
11
+ import { DraggableContainer } from '../../DraggableContainer';
12
+ import { ListMenu } from '../../ListMenu';
13
+ import { RPGUIContainerTypes } from '../../RPGUIContainer';
14
+ import { ItemCard } from '../Cards/ItemCard';
15
+ import { handleContextMenuList } from './itemContainerHelper';
16
+ import { ItemSlot } from './ItemSlot';
17
+
18
+ export interface IItemContainerProps {
19
+ itemContainer: IItemContainer;
20
+ onClose: () => void;
21
+ onMouseOver: (e: any, slotIndex: number, item: IItem | null) => void;
22
+ onActionSelected: (payload: any) => void;
23
+ initialPosition?: { x: number; y: number };
24
+ }
25
+
26
+ interface IContextItemProps {
27
+ visible: boolean;
28
+ posX: number;
29
+ posY: number;
30
+ slotItem: IItem | null;
31
+ slotIndex?: number | null;
32
+ contextActions: any;
33
+ }
34
+
35
+ interface IHoverDetailProps {
36
+ visible: boolean;
37
+ posX: number;
38
+ posY: number;
39
+ item: IItem | null;
40
+ }
41
+
42
+ export const ItemContainer: React.FC<IItemContainerProps> = ({
43
+ itemContainer,
44
+ onClose,
45
+ onMouseOver,
46
+ onActionSelected,
47
+ initialPosition = { x: 0, y: 0 },
48
+ }) => {
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>({
76
+ visible: false,
77
+ posX: initX,
78
+ posY: initY,
79
+ contextActions: [],
80
+ slotItem: null,
81
+ });
82
+ const [itemHoverDetail, setItemHoverDetail] = useState<IHoverDetailProps>({
83
+ visible: false,
84
+ posX: initX,
85
+ posY: initY,
86
+ item: null,
87
+ });
88
+
89
+ const clearContextMenu = () => {
90
+ setContextData({
91
+ visible: false,
92
+ posX: 0,
93
+ posY: 0,
94
+ contextActions: [],
95
+ slotItem: null,
96
+ });
97
+ };
98
+
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,
113
+ });
114
+ onMouseOver(event, slotIndex, item);
115
+ } else {
116
+ clearItemHoverDetail();
117
+ }
118
+ };
119
+
120
+ const clearItemHoverDetail = () => {
121
+ setItemHoverDetail({
122
+ ...itemHoverDetail,
123
+ visible: false,
124
+ item: null,
125
+ });
126
+ };
127
+
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();
140
+ };
141
+
142
+ const onSelected = (selectedActionId: ItemSocketEvents | string): void => {
143
+ let payloadData: IPayloadProps = {
144
+ actionType: selectedActionId,
145
+ item: contextData.slotItem,
146
+ };
147
+ onActionSelected(payloadData);
148
+ clearContextMenu();
149
+ };
150
+
151
+ const onRenderSlots = () => {
152
+ const slots = [];
153
+
154
+ for (let i = 0; i < itemContainer.slotQty; i++) {
155
+ slots.push(
156
+ <ItemSlot
157
+ key={i}
158
+ slotIndex={i}
159
+ item={itemContainer.slots?.[i] || null}
160
+ onMouseOver={handleOnMouseHover}
161
+ onClick={handleOnItemClick}
162
+ onCancelContextMenu={() => {
163
+ clearContextMenu();
164
+ }}
165
+ />
166
+ );
167
+ }
168
+ return slots;
169
+ };
170
+
171
+ return (
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>
205
+ );
206
+ };
207
+
208
+ const ItemsContainer = styled.div`
209
+ max-width: 280px;
210
+ display: flex;
211
+ justify-content: center;
212
+ flex-wrap: wrap;
213
+ `;
@@ -0,0 +1,90 @@
1
+ import { IItem } from '@rpg-engine/shared';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+ import atlasJSON from '../../../mocks/atlas/items/items.json';
5
+ import atlasIMG from '../../../mocks/atlas/items/items.png';
6
+ import { SpriteFromAtlas } from '../SpriteFromAtlas';
7
+ interface IProps {
8
+ slotIndex: number;
9
+ item: IItem | null;
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;
19
+ }
20
+
21
+ export const ItemSlot: React.FC<IProps> = ({
22
+ slotIndex,
23
+ item,
24
+ onMouseOver,
25
+ onClick,
26
+ onCancelContextMenu,
27
+ }) => {
28
+ const getLeftPositionValue = (quantity: number) => {
29
+ if (quantity > 0 && quantity < 10) return '2.5rem';
30
+ else if (quantity > 9 && quantity < 100) return '2.0rem';
31
+ else if (quantity > 99) return '1rem';
32
+ return '2.5rem';
33
+ };
34
+
35
+ return (
36
+ <Container
37
+ className="rpgui-icon empty-slot"
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
+ }}
49
+ >
50
+ {item && item.texturePath ? (
51
+ <SpriteFromAtlas
52
+ atlasIMG={atlasIMG}
53
+ atlasJSON={atlasJSON}
54
+ spriteKey={item.texturePath}
55
+ scale={3}
56
+ />
57
+ ) : null}
58
+ {item && item.isStackable && item?.stackQty ? (
59
+ <ItemQty left={getLeftPositionValue(item.stackQty)}>
60
+ {' '}
61
+ {item.stackQty}{' '}
62
+ </ItemQty>
63
+ ) : null}
64
+ </Container>
65
+ );
66
+ };
67
+
68
+ const Container = styled.div`
69
+ margin: 0.1rem;
70
+ // border: 1px red solid;
71
+
72
+ .sprite-from-atlas-img {
73
+ position: relative;
74
+ top: 1.5rem;
75
+ left: 1.5rem;
76
+
77
+ &:hover {
78
+ filter: sepia(100%) saturate(300%) brightness(70%) hue-rotate(180deg);
79
+ }
80
+ }
81
+ `;
82
+
83
+ interface IItemQtyProps {
84
+ left: string;
85
+ }
86
+ const ItemQty = styled.span<IItemQtyProps>`
87
+ position: relative;
88
+ top: 1.5rem;
89
+ left: ${props => props.left};
90
+ `;
@@ -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,69 @@
1
+ import { GRID_HEIGHT, GRID_WIDTH } from '@rpg-engine/shared';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+
5
+ interface IProps {
6
+ atlasJSON: any;
7
+ atlasIMG: any;
8
+ spriteKey: string;
9
+ width?: number;
10
+ height?: number;
11
+ scale?: number;
12
+ }
13
+
14
+ export const SpriteFromAtlas: React.FC<IProps> = ({
15
+ atlasJSON,
16
+ atlasIMG,
17
+ spriteKey,
18
+ width = GRID_WIDTH,
19
+ height = GRID_HEIGHT,
20
+ scale = 2,
21
+ }) => {
22
+ //! 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).
23
+ //!Due to React's limitations, we cannot import it from the public folder directly!
24
+
25
+ const spriteData = atlasJSON.frames[spriteKey];
26
+
27
+ return (
28
+ <Container width={width} height={height}>
29
+ <ImgSprite
30
+ className="sprite-from-atlas-img"
31
+ atlasIMG={atlasIMG}
32
+ frame={spriteData.frame}
33
+ scale={scale}
34
+ />
35
+ </Container>
36
+ );
37
+ };
38
+
39
+ interface IImgSpriteProps {
40
+ atlasIMG: any;
41
+ frame: {
42
+ x: number;
43
+ y: number;
44
+ w: number;
45
+ h: number;
46
+ };
47
+ scale: number;
48
+ }
49
+
50
+ interface IContainerProps {
51
+ width: number;
52
+ height: number;
53
+ }
54
+
55
+ const Container = styled.div`
56
+ width: ${(props: IContainerProps) => props.width}px;
57
+ height: ${(props: IContainerProps) => props.height}px;
58
+ `;
59
+
60
+ const ImgSprite = styled.div<IImgSpriteProps>`
61
+ width: ${props => props.frame.w}px;
62
+ height: ${props => props.frame.h}px;
63
+ background-image: url(${props => props.atlasIMG});
64
+ background-position: -${props => props.frame.x}px -${props => props.frame.y}px;
65
+ transform: scale(${props => props.scale});
66
+ position: relative;
67
+ top: 8px;
68
+ left: 8px;
69
+ `;
@@ -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
+ }
package/src/index.tsx CHANGED
@@ -4,6 +4,9 @@ export * from './components/CheckButton';
4
4
  export * from './components/DraggableContainer';
5
5
  export * from './components/Dropdown';
6
6
  export * from './components/Input';
7
+ export * from './components/Item/Inventory/ItemContainer';
8
+ export * from './components/Item/Inventory/ItemSlot';
9
+ export * from './components/Item/SpriteFromAtlas';
7
10
  export * from './components/ListMenu';
8
11
  export * from './components/NPCDialog/NPCDialog';
9
12
  export * from './components/NPCDialog/QuestionDialog/QuestionDialog';