@rpg-engine/long-bow 0.1.69 → 0.1.72

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 (41) hide show
  1. package/dist/components/Abstractions/SlotsContainer.d.ts +11 -0
  2. package/dist/components/DraggableContainer.d.ts +2 -1
  3. package/dist/components/Equipment/EquipmentSet.d.ts +13 -0
  4. package/dist/components/Item/Inventory/ItemSlot.d.ts +10 -2
  5. package/dist/components/Item/Inventory/itemContainerHelper.d.ts +6 -2
  6. package/dist/components/Multitab/Tab.d.ts +9 -0
  7. package/dist/components/Multitab/TabBody.d.ts +7 -0
  8. package/dist/components/Multitab/TabsContainer.d.ts +17 -0
  9. package/dist/components/{Item → shared}/SpriteFromAtlas.d.ts +4 -1
  10. package/dist/components/shared/SpriteIcon.d.ts +9 -0
  11. package/dist/components/store/UI.store.d.ts +34 -0
  12. package/dist/index.d.ts +3 -1
  13. package/dist/long-bow.cjs.development.js +2137 -449
  14. package/dist/long-bow.cjs.development.js.map +1 -1
  15. package/dist/long-bow.cjs.production.min.js +1 -1
  16. package/dist/long-bow.cjs.production.min.js.map +1 -1
  17. package/dist/long-bow.esm.js +2139 -451
  18. package/dist/long-bow.esm.js.map +1 -1
  19. package/dist/mocks/equipmentSet.mocks.d.ts +3 -0
  20. package/package.json +4 -2
  21. package/src/components/Abstractions/SlotsContainer.tsx +42 -0
  22. package/src/components/DraggableContainer.tsx +63 -36
  23. package/src/components/Equipment/EquipmentSet.tsx +179 -0
  24. package/src/components/Item/Inventory/ItemContainer.tsx +70 -178
  25. package/src/components/Item/Inventory/ItemSlot.tsx +93 -25
  26. package/src/components/Item/Inventory/itemContainerHelper.ts +48 -11
  27. package/src/components/ListMenu.tsx +4 -4
  28. package/src/components/Multitab/Tab.tsx +57 -0
  29. package/src/components/Multitab/TabBody.tsx +13 -0
  30. package/src/components/Multitab/TabsContainer.tsx +99 -0
  31. package/src/components/SkillProgressBar.tsx +2 -2
  32. package/src/components/{Item → shared}/SpriteFromAtlas.tsx +24 -4
  33. package/src/components/shared/SpriteIcon.tsx +67 -0
  34. package/src/components/store/UI.store.ts +192 -0
  35. package/src/index.tsx +3 -1
  36. package/src/mocks/atlas/icons/icons.json +303 -0
  37. package/src/mocks/atlas/icons/icons.png +0 -0
  38. package/src/mocks/atlas/items/items.json +1209 -181
  39. package/src/mocks/atlas/items/items.png +0 -0
  40. package/src/mocks/equipmentSet.mocks.ts +347 -0
  41. package/src/mocks/itemContainer.mocks.ts +33 -33
@@ -1,12 +1,34 @@
1
- import { IItem } from '@rpg-engine/shared';
1
+ import { IItem, IItemContainer, ItemSlotType } from '@rpg-engine/shared';
2
2
  import React from 'react';
3
3
  import styled from 'styled-components';
4
4
  import atlasJSON from '../../../mocks/atlas/items/items.json';
5
5
  import atlasIMG from '../../../mocks/atlas/items/items.png';
6
- import { SpriteFromAtlas } from '../SpriteFromAtlas';
6
+ import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
7
+
8
+ export enum SlotContainerType {
9
+ INVENTORY = 'Inventory',
10
+ EQUIPMENT_SET = 'EquipmentSet',
11
+ }
12
+
13
+ const EquipmentSlotSpriteByType: any = {
14
+ Neck: 'accessories/corruption-necklace.png',
15
+ LeftHand: 'swords/broad-sword.png',
16
+ Ring: 'rings/iron-ring.png',
17
+ Head: 'helmets/viking-helmet.png',
18
+ Torso: 'armors/iron-armor.png',
19
+ Legs: 'crafting-resources/medicinal-leaf.png',
20
+ Feet: 'boots/iron-boots.png',
21
+ Inventory: 'containers/bag.png',
22
+ RightHand: 'shields/plate-shield.png',
23
+ Accessory: 'gloves/plate-gloves.png',
24
+ };
25
+
7
26
  interface IProps {
8
27
  slotIndex: number;
9
28
  item: IItem | null;
29
+ itemContainer?: IItemContainer | null;
30
+ slotContainerType: SlotContainerType | null;
31
+ slotSpriteMask?: ItemSlotType | null;
10
32
  onMouseOver: (
11
33
  event: any,
12
34
  slotIndex: number,
@@ -14,14 +36,23 @@ interface IProps {
14
36
  x: number,
15
37
  y: number
16
38
  ) => void;
17
- onClick: (item: IItem, posX: number, posY: number) => void;
39
+ onMouseOut: () => void;
40
+ onClick: (
41
+ item: IItem,
42
+ posX: number,
43
+ posY: number,
44
+ slotContainerType: SlotContainerType | null
45
+ ) => void;
18
46
  onCancelContextMenu: () => void;
19
47
  }
20
48
 
21
49
  export const ItemSlot: React.FC<IProps> = ({
22
50
  slotIndex,
23
51
  item,
52
+ slotContainerType,
53
+ slotSpriteMask,
24
54
  onMouseOver,
55
+ onMouseOut,
25
56
  onClick,
26
57
  onCancelContextMenu,
27
58
  }) => {
@@ -32,51 +63,88 @@ export const ItemSlot: React.FC<IProps> = ({
32
63
  return '2.5rem';
33
64
  };
34
65
 
66
+ const renderItem = (itemToRender: IItem | null) => {
67
+ const element = [];
68
+ if (itemToRender?.texturePath) {
69
+ element.push(
70
+ <SpriteFromAtlas
71
+ atlasIMG={atlasIMG}
72
+ atlasJSON={atlasJSON}
73
+ spriteKey={itemToRender.texturePath}
74
+ imgScale={3}
75
+ />
76
+ );
77
+ }
78
+ if (itemToRender?.isStackable && itemToRender?.stackQty) {
79
+ element.push(
80
+ <ItemQty left={getLeftPositionValue(itemToRender.stackQty)}>
81
+ {' '}
82
+ {itemToRender.stackQty}{' '}
83
+ </ItemQty>
84
+ );
85
+ }
86
+ return element;
87
+ };
88
+
89
+ const renderEquipment = (itemToRender: IItem | null) => {
90
+ if (
91
+ itemToRender?.texturePath &&
92
+ itemToRender.allowedEquipSlotType?.includes(slotSpriteMask!)
93
+ ) {
94
+ return (
95
+ <SpriteFromAtlas
96
+ atlasIMG={atlasIMG}
97
+ atlasJSON={atlasJSON}
98
+ spriteKey={itemToRender.texturePath}
99
+ imgScale={3}
100
+ />
101
+ );
102
+ } else {
103
+ return (
104
+ <SpriteFromAtlas
105
+ atlasIMG={atlasIMG}
106
+ atlasJSON={atlasJSON}
107
+ spriteKey={EquipmentSlotSpriteByType[slotSpriteMask!]}
108
+ imgScale={3}
109
+ grayScale={true}
110
+ opacity={0.4}
111
+ />
112
+ );
113
+ }
114
+ };
115
+
116
+ const onRenderSlot = (itemToRender: IItem | null) => {
117
+ if (slotContainerType === SlotContainerType.EQUIPMENT_SET)
118
+ return renderEquipment(itemToRender);
119
+ return renderItem(itemToRender);
120
+ };
121
+
35
122
  return (
36
123
  <Container
37
124
  className="rpgui-icon empty-slot"
38
125
  onMouseOver={event =>
39
126
  onMouseOver(event, slotIndex, item, event.clientX, event.clientY)
40
127
  }
128
+ onMouseOut={() => onMouseOut()}
41
129
  onClick={e => {
42
130
  if (item) {
43
- console.log(e);
44
- onClick(item, e.clientX, e.clientY);
131
+ onClick(item, e.clientX, e.clientY, slotContainerType);
45
132
  } else {
46
133
  onCancelContextMenu();
47
134
  }
48
135
  }}
49
136
  >
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}
137
+ {onRenderSlot(item)}
64
138
  </Container>
65
139
  );
66
140
  };
67
141
 
68
142
  const Container = styled.div`
69
143
  margin: 0.1rem;
70
- // border: 1px red solid;
71
-
72
144
  .sprite-from-atlas-img {
73
145
  position: relative;
74
146
  top: 1.5rem;
75
147
  left: 1.5rem;
76
-
77
- &:hover {
78
- filter: sepia(100%) saturate(300%) brightness(70%) hue-rotate(180deg);
79
- }
80
148
  }
81
149
  `;
82
150
 
@@ -1,21 +1,32 @@
1
- import { ActionsByItemType, ItemType } from '@rpg-engine/shared';
1
+ import {
2
+ ActionsByItemType,
3
+ ItemSocketEventsDisplayLabels,
4
+ ItemType,
5
+ } from '@rpg-engine/shared';
2
6
 
3
- interface IContextMenuItem {
7
+ export interface IContextMenuItem {
4
8
  id: string;
5
9
  text: string;
6
10
  }
7
11
 
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
- };
12
+ export enum ContainerType {
13
+ INVENTORY = 'Inventory',
14
+ EQUIPMENT_SET = 'EquipmentSet',
15
+ }
16
+
17
+ // TODO: Refactor this file
18
+ const generateContextList = (actionsByTypeList: any) => {
19
+ const contextMenu: IContextMenuItem[] = actionsByTypeList.map(
20
+ (action: string) => {
21
+ return { id: action, text: ItemSocketEventsDisplayLabels[action] };
22
+ }
23
+ );
24
+ return contextMenu;
25
+ };
17
26
 
27
+ export const handleContextMenuList = (itemType: ItemType) => {
18
28
  let contextActionMenu: IContextMenuItem[] = [];
29
+
19
30
  switch (itemType) {
20
31
  case ItemType.Weapon:
21
32
  case ItemType.Armor:
@@ -42,3 +53,29 @@ export const handleContextMenuList = (itemType: ItemType) => {
42
53
  }
43
54
  return contextActionMenu;
44
55
  };
56
+
57
+ export const handleEquipmentContextMenuList = (itemType: ItemType) => {
58
+ let contextActionMenu: IContextMenuItem[] = [];
59
+ switch (itemType) {
60
+ case ItemType.Weapon:
61
+ case ItemType.Armor:
62
+ case ItemType.Accessory:
63
+ case ItemType.Jewelry:
64
+ case ItemType.Tool:
65
+ contextActionMenu = generateContextList(
66
+ ActionsByItemType.EquipmenSetItems
67
+ );
68
+ break;
69
+ case ItemType.Container:
70
+ contextActionMenu = generateContextList(
71
+ ActionsByItemType.EquipmenSetContainer
72
+ );
73
+ break;
74
+ default:
75
+ contextActionMenu = generateContextList(
76
+ ActionsByItemType.EquipmenSetItems
77
+ );
78
+ break;
79
+ }
80
+ return contextActionMenu;
81
+ };
@@ -24,14 +24,14 @@ export const ListMenu: React.FC<IListMenuProps> = ({
24
24
  return (
25
25
  <Container x={x} y={y} fontSize={fontSize}>
26
26
  <ul className="rpgui-list-imp" style={{ overflow: 'hidden' }}>
27
- {options.map(params => (
27
+ {options.map((params, index) => (
28
28
  <ListElement
29
- key={params.text}
29
+ key={params?.id || index}
30
30
  onClick={() => {
31
- onSelected(params.id);
31
+ onSelected(params?.id);
32
32
  }}
33
33
  >
34
- {params.text}
34
+ {params?.text || 'No text'}
35
35
  </ListElement>
36
36
  ))}
37
37
  </ul>
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { MultitabType } from './TabsContainer';
4
+
5
+ export interface ISingleTab {
6
+ active: boolean;
7
+ label: string;
8
+ onClick: () => void;
9
+ type: MultitabType;
10
+ }
11
+
12
+ export const Tab: React.FC<ISingleTab> = ({ active, label, onClick, type }) => {
13
+ return (
14
+ <CustomTab activeTab={active} onClick={onClick} className={type}>
15
+ <p>{label}</p>
16
+ </CustomTab>
17
+ );
18
+ };
19
+
20
+ interface ICustomTab {
21
+ activeTab: boolean;
22
+ }
23
+
24
+ const CustomTab = styled.div<ICustomTab>`
25
+ width: 120px;
26
+ color: white;
27
+ font-size: 0.8rem;
28
+
29
+ &.gray {
30
+ border-left: 0.25rem solid rgba(0, 0, 0, 0.25);
31
+ border-right: 0.25rem solid rgba(0, 0, 0, 0.25);
32
+ border-top: 0.25rem solid rgba(0, 0, 0, 0.25);
33
+ background: ${props => (props.activeTab ? '#4E4A4E' : '#2b292b')};
34
+ }
35
+
36
+ &.brown {
37
+ border-left: 0.25rem solid #996d55;
38
+ border-right: 0.25rem solid #996d55;
39
+ border-top: 0.25rem solid #996d55;
40
+ background: ${props => (props.activeTab ? '#BF886A' : '#B67051')};
41
+ }
42
+
43
+ margin-right: 10px;
44
+ p {
45
+ text-align: center;
46
+ font-size: 0.6rem;
47
+
48
+ opacity: ${props => (props.activeTab ? '1' : '0.5')};
49
+ }
50
+
51
+ border-radius: 5px 5px 0 0;
52
+
53
+ z-index: ${props => (props.activeTab ? 1 : -1)};
54
+
55
+ position: relative;
56
+ top: 0.3rem;
57
+ `;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ interface IProps {
5
+ id: string;
6
+ children: React.ReactNode;
7
+ }
8
+
9
+ export const TabBody: React.FC<IProps> = ({ id, children }) => {
10
+ return <Container data-tab-id={id}>{children}</Container>;
11
+ };
12
+
13
+ const Container = styled.div``;
@@ -0,0 +1,99 @@
1
+ import React, { useState } from 'react';
2
+ import styled from 'styled-components';
3
+ import { DraggableContainer } from '../DraggableContainer';
4
+ import { RPGUIContainerTypes } from '../RPGUIContainer';
5
+ import { Tab } from './Tab';
6
+
7
+ interface ITab {
8
+ label: string;
9
+ id: string;
10
+ }
11
+
12
+ export enum MultitabType {
13
+ Brown = 'brown',
14
+ Gray = 'gray',
15
+ }
16
+
17
+ export interface ITabsContainer {
18
+ children: React.ReactNode;
19
+ onCloseButton?: () => void;
20
+ tabs: ITab[];
21
+ type: MultitabType;
22
+ }
23
+
24
+ export const TabsContainer: React.FC<ITabsContainer> = ({
25
+ children,
26
+ onCloseButton,
27
+ tabs,
28
+ type = MultitabType.Brown,
29
+ }) => {
30
+ const [selectedTab, setSelectedTab] = useState(tabs[0].id); //by default the first one
31
+
32
+ const onRenderTabs = () =>
33
+ tabs.map((tab, index) => (
34
+ <Tab
35
+ type={type}
36
+ active={selectedTab === tab.id}
37
+ label={tab.label}
38
+ key={`${tab.label}_${index}`}
39
+ onClick={() => setSelectedTab(tab.id)}
40
+ >
41
+ {tab.label}
42
+ </Tab>
43
+ ));
44
+
45
+ const onGetContainerType = () => {
46
+ switch (type) {
47
+ case 'brown':
48
+ return RPGUIContainerTypes.FramedGold;
49
+ case 'gray':
50
+ return RPGUIContainerTypes.Framed;
51
+ default:
52
+ return RPGUIContainerTypes.Framed;
53
+ }
54
+ };
55
+
56
+ return (
57
+ <DraggableContainer
58
+ onCloseButton={onCloseButton}
59
+ type={onGetContainerType()}
60
+ >
61
+ {onRenderTabs()}
62
+ <BodyContainer selectedTab={selectedTab} className={type}>
63
+ {children}
64
+ </BodyContainer>
65
+ </DraggableContainer>
66
+ );
67
+ };
68
+
69
+ interface IBodyContainer {
70
+ selectedTab: string;
71
+ }
72
+
73
+ const BodyContainer = styled.div<IBodyContainer>`
74
+ display: flex;
75
+ width: 100%;
76
+ height: 100%;
77
+ justify-content: space-between;
78
+
79
+ /* hide everything that is not data-tab-id selectedTab */
80
+ & > *:not([data-tab-id=${props => props.selectedTab}]) {
81
+ display: none;
82
+ }
83
+
84
+ &.brown {
85
+ border: 0.25rem solid #996D55;
86
+ background-color: #BF886A;
87
+ }
88
+
89
+
90
+ &.gray {
91
+ border: 0.25rem solid rgba(0, 0, 0, 0.25);
92
+ background-color: #4E4A4E;
93
+ }
94
+
95
+
96
+ border-radius: 5px;
97
+ padding: 0.5rem;
98
+
99
+ `;
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import styled from 'styled-components';
4
4
  import atlasJSON from '../mocks/atlas/items/items.json';
5
5
  import atlasIMG from '../mocks/atlas/items/items.png';
6
- import { SpriteFromAtlas } from './Item/SpriteFromAtlas';
6
+ import { SpriteFromAtlas } from './shared/SpriteFromAtlas';
7
7
  import { SimpleProgressBar } from './SimpleProgressBar';
8
8
 
9
9
  export interface ISkillProgressBarProps {
@@ -42,7 +42,7 @@ export const SkillProgressBar: React.FC<ISkillProgressBarProps> = ({
42
42
  atlasIMG={atlasIMG}
43
43
  atlasJSON={atlasJSON}
44
44
  spriteKey={texturePath}
45
- scale={1}
45
+ imgScale={1}
46
46
  grayScale
47
47
  opacity={0.5}
48
48
  />
@@ -8,9 +8,12 @@ interface IProps {
8
8
  spriteKey: string;
9
9
  width?: number;
10
10
  height?: number;
11
- scale?: number;
12
11
  grayScale?: boolean;
13
12
  opacity?: number;
13
+ onClick?: () => void;
14
+ containerStyle?: any;
15
+ imgStyle?: any;
16
+ imgScale?: number;
14
17
  }
15
18
 
16
19
  export const SpriteFromAtlas: React.FC<IProps> = ({
@@ -19,7 +22,10 @@ export const SpriteFromAtlas: React.FC<IProps> = ({
19
22
  spriteKey,
20
23
  width = GRID_WIDTH,
21
24
  height = GRID_HEIGHT,
22
- scale = 2,
25
+ imgScale = 2,
26
+ imgStyle,
27
+ onClick,
28
+ containerStyle,
23
29
  grayScale = false,
24
30
  opacity = 1,
25
31
  }) => {
@@ -29,14 +35,21 @@ export const SpriteFromAtlas: React.FC<IProps> = ({
29
35
  const spriteData = atlasJSON.frames[spriteKey];
30
36
 
31
37
  return (
32
- <Container width={width} height={height}>
38
+ <Container
39
+ width={width}
40
+ height={height}
41
+ hasHover={grayScale}
42
+ onClick={onClick}
43
+ style={containerStyle}
44
+ >
33
45
  <ImgSprite
34
46
  className="sprite-from-atlas-img"
35
47
  atlasIMG={atlasIMG}
36
48
  frame={spriteData.frame}
37
- scale={scale}
49
+ scale={imgScale}
38
50
  grayScale={grayScale}
39
51
  opacity={opacity}
52
+ style={imgStyle}
40
53
  />
41
54
  </Container>
42
55
  );
@@ -58,11 +71,18 @@ interface IImgSpriteProps {
58
71
  interface IContainerProps {
59
72
  width: number;
60
73
  height: number;
74
+ hasHover: boolean;
61
75
  }
62
76
 
63
77
  const Container = styled.div`
64
78
  width: ${(props: IContainerProps) => props.width}px;
65
79
  height: ${(props: IContainerProps) => props.height}px;
80
+ ${(props: IContainerProps) =>
81
+ !props.hasHover
82
+ ? `&:hover {
83
+ filter: sepia(100%) saturate(300%) brightness(70%) hue-rotate(180deg);
84
+ }`
85
+ : ``}
66
86
  `;
67
87
 
68
88
  const ImgSprite = styled.div<IImgSpriteProps>`
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import iconsAtlasJSON from '../../mocks/atlas/icons/icons.json';
4
+ import iconsAtlasIMG from '../../mocks/atlas/icons/icons.png';
5
+ import { SpriteFromAtlas } from './SpriteFromAtlas';
6
+
7
+ interface IProps {
8
+ onClick?: () => void;
9
+ spriteKey: string;
10
+ baseSpriteKey?: string;
11
+ imgStyle?: any;
12
+ }
13
+
14
+ export const SpriteIcon: React.FC<IProps> = ({
15
+ onClick,
16
+ spriteKey,
17
+ baseSpriteKey,
18
+ imgStyle,
19
+ }) => {
20
+ return (
21
+ <Container>
22
+ {baseSpriteKey && (
23
+ <Slot>
24
+ <SpriteFromAtlas
25
+ onClick={onClick}
26
+ atlasJSON={iconsAtlasJSON}
27
+ atlasIMG={iconsAtlasIMG}
28
+ spriteKey={baseSpriteKey}
29
+ width={32}
30
+ height={32}
31
+ containerStyle={{
32
+ zIndex: -1,
33
+ }}
34
+ />
35
+ </Slot>
36
+ )}
37
+ <Slot>
38
+ <SpriteFromAtlas
39
+ onClick={onClick}
40
+ atlasJSON={iconsAtlasJSON}
41
+ atlasIMG={iconsAtlasIMG}
42
+ spriteKey={spriteKey}
43
+ width={28}
44
+ height={28}
45
+ containerStyle={{
46
+ zIndex: 0,
47
+ }}
48
+ imgScale={baseSpriteKey ? 1.5 : 2}
49
+ imgStyle={imgStyle}
50
+ />
51
+ </Slot>
52
+ </Container>
53
+ );
54
+ };
55
+
56
+ const Container = styled.div`
57
+ position: relative;
58
+ width: 32px;
59
+ height: 32px;
60
+ display: flex;
61
+ justify-content: center;
62
+ align-items: center;
63
+ `;
64
+
65
+ const Slot = styled.div`
66
+ position: absolute;
67
+ `;