@rpg-engine/long-bow 0.3.80 → 0.3.81
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/Abstractions/ModalPortal.d.ts +6 -0
- package/dist/long-bow.cjs.development.js +64 -67
- 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 +64 -67
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Abstractions/ModalPortal.tsx +22 -0
- package/src/components/Item/Cards/ItemInfo.tsx +1 -1
- package/src/components/Item/Cards/ItemTooltip.tsx +20 -22
- package/src/components/Item/Cards/MobileItemTooltip.tsx +70 -55
- package/src/components/Item/Inventory/ItemContainer.tsx +24 -21
- package/src/components/Item/Inventory/ItemSlot.tsx +3 -1
- package/src/mocks/itemContainer.mocks.ts +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
|
|
5
|
+
const modalRoot = document.getElementById('modal-root')!;
|
|
6
|
+
|
|
7
|
+
interface ModalPortalProps {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ModalPortal: React.FC<ModalPortalProps> = ({ children }) => {
|
|
12
|
+
return ReactDOM.createPortal(
|
|
13
|
+
<Container className="rpgui-content">{children}</Container>,
|
|
14
|
+
modalRoot
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Container = styled.div`
|
|
19
|
+
position: static !important;
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
export default ModalPortal;
|
|
@@ -137,7 +137,7 @@ export const ItemInfo: React.FC<IItemInfoProps> = ({
|
|
|
137
137
|
|
|
138
138
|
{item.maxStackSize && item.maxStackSize !== 1 && (
|
|
139
139
|
<StackInfo>
|
|
140
|
-
x{item.stackQty ?? 1}({item.maxStackSize})
|
|
140
|
+
x{Math.round((item.stackQty ?? 1) * 100) / 100}({item.maxStackSize})
|
|
141
141
|
</StackInfo>
|
|
142
142
|
)}
|
|
143
143
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IEquipmentSet, IItem } from '@rpg-engine/shared';
|
|
2
2
|
import React, { useEffect, useRef } from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
|
+
import ModalPortal from '../../Abstractions/ModalPortal';
|
|
4
5
|
import { ItemInfoDisplay } from './ItemInfoDisplay';
|
|
5
6
|
|
|
6
7
|
export interface IItemTooltipProps {
|
|
@@ -22,7 +23,6 @@ export const ItemTooltip: React.FC<IItemTooltipProps> = ({
|
|
|
22
23
|
|
|
23
24
|
useEffect(() => {
|
|
24
25
|
const { current } = ref;
|
|
25
|
-
let initialOffset: DOMRect;
|
|
26
26
|
|
|
27
27
|
if (current) {
|
|
28
28
|
const handleMouseMove = (event: MouseEvent) => {
|
|
@@ -30,9 +30,6 @@ export const ItemTooltip: React.FC<IItemTooltipProps> = ({
|
|
|
30
30
|
|
|
31
31
|
// Adjust the position of the tooltip based on the mouse position
|
|
32
32
|
const rect = current.getBoundingClientRect();
|
|
33
|
-
if (!initialOffset) {
|
|
34
|
-
initialOffset = rect;
|
|
35
|
-
}
|
|
36
33
|
|
|
37
34
|
const tooltipWidth = rect.width;
|
|
38
35
|
const tooltipHeight = rect.height;
|
|
@@ -40,14 +37,12 @@ export const ItemTooltip: React.FC<IItemTooltipProps> = ({
|
|
|
40
37
|
clientX + tooltipWidth + offset > window.innerWidth;
|
|
41
38
|
const isOffScreenBottom =
|
|
42
39
|
clientY + tooltipHeight + offset > window.innerHeight;
|
|
43
|
-
const x =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
? clientY - tooltipHeight - offset
|
|
50
|
-
: clientY + offset) - initialOffset.y;
|
|
40
|
+
const x = isOffScreenRight
|
|
41
|
+
? clientX - tooltipWidth - offset
|
|
42
|
+
: clientX + offset;
|
|
43
|
+
const y = isOffScreenBottom
|
|
44
|
+
? clientY - tooltipHeight - offset
|
|
45
|
+
: clientY + offset;
|
|
51
46
|
|
|
52
47
|
current.style.transform = `translate(${x}px, ${y}px)`;
|
|
53
48
|
current.style.opacity = '1';
|
|
@@ -64,22 +59,25 @@ export const ItemTooltip: React.FC<IItemTooltipProps> = ({
|
|
|
64
59
|
}, []);
|
|
65
60
|
|
|
66
61
|
return (
|
|
67
|
-
<
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
<ModalPortal>
|
|
63
|
+
<Container ref={ref}>
|
|
64
|
+
<ItemInfoDisplay
|
|
65
|
+
item={item}
|
|
66
|
+
atlasIMG={atlasIMG}
|
|
67
|
+
atlasJSON={atlasJSON}
|
|
68
|
+
equipmentSet={equipmentSet}
|
|
69
|
+
/>
|
|
70
|
+
</Container>
|
|
71
|
+
</ModalPortal>
|
|
75
72
|
);
|
|
76
73
|
};
|
|
77
74
|
|
|
78
75
|
const Container = styled.div`
|
|
79
|
-
position:
|
|
80
|
-
z-index:
|
|
76
|
+
position: absolute;
|
|
77
|
+
z-index: 100;
|
|
81
78
|
pointer-events: none;
|
|
82
79
|
left: 0;
|
|
83
80
|
top: 0;
|
|
84
81
|
opacity: 0;
|
|
82
|
+
transition: opacity 0.08s;
|
|
85
83
|
`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IEquipmentSet, IItem } from '@rpg-engine/shared';
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { useRef } from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
|
+
import ModalPortal from '../../Abstractions/ModalPortal';
|
|
4
5
|
import { ItemInfoDisplay } from './ItemInfoDisplay';
|
|
5
6
|
|
|
6
7
|
interface IListMenuOption {
|
|
@@ -31,72 +32,86 @@ export const MobileItemTooltip: React.FC<MobileItemTooltipProps> = ({
|
|
|
31
32
|
}) => {
|
|
32
33
|
const ref = useRef<HTMLDivElement>(null);
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (current) {
|
|
38
|
-
// Adjust the position to be on whole window
|
|
39
|
-
const rect = current.getBoundingClientRect();
|
|
40
|
-
|
|
41
|
-
const x = (-rect.x * 100) / (scale * 100);
|
|
42
|
-
const y = (-rect.y * 100) / (scale * 100);
|
|
43
|
-
|
|
44
|
-
current.style.transform = `translate(${x}px, ${y}px)`;
|
|
45
|
-
current.style.opacity = '0.92';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return;
|
|
49
|
-
}, []);
|
|
35
|
+
const handleFadeOut = () => {
|
|
36
|
+
ref.current?.classList.add('fadeOut');
|
|
37
|
+
};
|
|
50
38
|
|
|
51
39
|
return (
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
40
|
+
<ModalPortal>
|
|
41
|
+
<Container
|
|
42
|
+
ref={ref}
|
|
43
|
+
onTouchEnd={() => {
|
|
44
|
+
handleFadeOut();
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
closeTooltip();
|
|
47
|
+
}, 100);
|
|
48
|
+
}}
|
|
49
|
+
scale={scale}
|
|
50
|
+
>
|
|
51
|
+
<ItemInfoDisplay
|
|
52
|
+
item={item}
|
|
53
|
+
atlasIMG={atlasIMG}
|
|
54
|
+
atlasJSON={atlasJSON}
|
|
55
|
+
equipmentSet={equipmentSet}
|
|
56
|
+
isMobile
|
|
57
|
+
/>
|
|
58
|
+
<OptionsContainer>
|
|
59
|
+
{options?.map(option => (
|
|
60
|
+
<Option
|
|
61
|
+
key={option.id}
|
|
62
|
+
onTouchEnd={() => {
|
|
63
|
+
handleFadeOut();
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
onSelected?.(option.id);
|
|
66
|
+
closeTooltip();
|
|
67
|
+
}, 100);
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{option.text}
|
|
71
|
+
</Option>
|
|
72
|
+
))}
|
|
73
|
+
</OptionsContainer>
|
|
74
|
+
</Container>
|
|
75
|
+
</ModalPortal>
|
|
84
76
|
);
|
|
85
77
|
};
|
|
86
78
|
|
|
87
79
|
const Container = styled.div<{ scale: number }>`
|
|
88
|
-
position:
|
|
89
|
-
z-index:
|
|
80
|
+
position: absolute;
|
|
81
|
+
z-index: 100;
|
|
90
82
|
left: 0;
|
|
91
83
|
top: 0;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
height: ${({ scale }) => `calc(100vh * 100 / ${scale * 100})`};
|
|
84
|
+
width: 100vw;
|
|
85
|
+
height: 100vh;
|
|
95
86
|
background-color: rgba(0 0 0 / 0.5);
|
|
96
87
|
display: flex;
|
|
97
88
|
justify-content: center;
|
|
98
89
|
align-items: center;
|
|
99
90
|
gap: 0.5rem;
|
|
91
|
+
transition: opacity 0.08s;
|
|
92
|
+
animation: fadeIn 0.1s forwards;
|
|
93
|
+
|
|
94
|
+
@keyframes fadeIn {
|
|
95
|
+
0% {
|
|
96
|
+
opacity: 0;
|
|
97
|
+
}
|
|
98
|
+
100% {
|
|
99
|
+
opacity: 0.92;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@keyframes fadeOut {
|
|
104
|
+
0% {
|
|
105
|
+
opacity: 0.92;
|
|
106
|
+
}
|
|
107
|
+
100% {
|
|
108
|
+
opacity: 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
&.fadeOut {
|
|
113
|
+
animation: fadeOut 0.1s forwards;
|
|
114
|
+
}
|
|
100
115
|
|
|
101
116
|
@media (max-width: 580px) {
|
|
102
117
|
flex-direction: column;
|
|
@@ -122,7 +137,7 @@ const Option = styled.button`
|
|
|
122
137
|
border: none;
|
|
123
138
|
border-radius: 3px;
|
|
124
139
|
width: 8rem;
|
|
125
|
-
transition: background-color 0.
|
|
140
|
+
transition: background-color 0.1s;
|
|
126
141
|
|
|
127
142
|
&:hover {
|
|
128
143
|
background-color: #555;
|
|
@@ -12,6 +12,7 @@ import { SlotsContainer } from '../../Abstractions/SlotsContainer';
|
|
|
12
12
|
import { ItemQuantitySelector } from './ItemQuantitySelector';
|
|
13
13
|
|
|
14
14
|
import { IPosition } from '../../../types/eventTypes';
|
|
15
|
+
import ModalPortal from '../../Abstractions/ModalPortal';
|
|
15
16
|
import { ShortcutsSetter } from '../../Shortcuts/ShortcutsSetter';
|
|
16
17
|
import { ItemSlot } from './ItemSlot';
|
|
17
18
|
|
|
@@ -169,27 +170,29 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
|
|
|
169
170
|
</ItemsContainer>
|
|
170
171
|
</SlotsContainer>
|
|
171
172
|
{quantitySelect.isOpen && (
|
|
172
|
-
<
|
|
173
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
173
|
+
<ModalPortal>
|
|
174
|
+
<QuantitySelectorContainer>
|
|
175
|
+
<ItemQuantitySelector
|
|
176
|
+
quantity={quantitySelect.maxQuantity}
|
|
177
|
+
onConfirm={quantity => {
|
|
178
|
+
quantitySelect.callback(quantity);
|
|
179
|
+
setQuantitySelect({
|
|
180
|
+
isOpen: false,
|
|
181
|
+
maxQuantity: 1,
|
|
182
|
+
callback: () => {},
|
|
183
|
+
});
|
|
184
|
+
}}
|
|
185
|
+
onClose={() => {
|
|
186
|
+
quantitySelect.callback(-1);
|
|
187
|
+
setQuantitySelect({
|
|
188
|
+
isOpen: false,
|
|
189
|
+
maxQuantity: 1,
|
|
190
|
+
callback: () => {},
|
|
191
|
+
});
|
|
192
|
+
}}
|
|
193
|
+
/>
|
|
194
|
+
</QuantitySelectorContainer>
|
|
195
|
+
</ModalPortal>
|
|
193
196
|
)}
|
|
194
197
|
</>
|
|
195
198
|
);
|
|
@@ -146,7 +146,9 @@ export const ItemSlot: React.FC<IProps> = observer(
|
|
|
146
146
|
return (
|
|
147
147
|
<ItemQtyContainer key={`qty-${itemId}`}>
|
|
148
148
|
<Ellipsis maxLines={1} maxWidth="48px">
|
|
149
|
-
<ItemQty className={qtyClassName}>
|
|
149
|
+
<ItemQty className={qtyClassName}>
|
|
150
|
+
{Math.round(stackQty * 100) / 100}{' '}
|
|
151
|
+
</ItemQty>
|
|
150
152
|
</Ellipsis>
|
|
151
153
|
</ItemQtyContainer>
|
|
152
154
|
);
|