minecraft-inventory 0.1.17 → 0.1.19
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/package.json +1 -1
- package/src/components/CursorItem/CursorItem.tsx +30 -3
- package/src/components/InventoryOverlay/InventoryOverlay.tsx +1 -1
- package/src/components/InventoryWindow/HotbarExtras.tsx +15 -8
- package/src/components/InventoryWindow/InventoryWindow.tsx +1 -0
- package/src/components/Slot/Slot.tsx +28 -17
- package/src/context/InventoryContext.tsx +20 -6
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from 'react'
|
|
1
|
+
import React, { useEffect, useMemo, useRef } from 'react'
|
|
2
2
|
import { useInventoryContext } from '../../context/InventoryContext'
|
|
3
3
|
import { useScale } from '../../context/ScaleContext'
|
|
4
4
|
import { ItemCanvas } from '../ItemCanvas'
|
|
@@ -11,13 +11,21 @@ import { globalMouse } from '../../utils/globalMouse'
|
|
|
11
11
|
* Only re-renders when heldItem or contentSize changes (for ItemCanvas updates).
|
|
12
12
|
*/
|
|
13
13
|
export function CursorItem() {
|
|
14
|
-
const { heldItem } = useInventoryContext()
|
|
14
|
+
const { heldItem, isDragging, dragSlots, dragRemainder } = useInventoryContext()
|
|
15
15
|
const { contentSize } = useScale()
|
|
16
16
|
const isMobile = useMobile()
|
|
17
17
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
18
18
|
const rafRef = useRef(0)
|
|
19
19
|
const halfSizeRef = useRef(Math.round(contentSize / 2))
|
|
20
20
|
|
|
21
|
+
// Determine display item: during spread, show remainder count
|
|
22
|
+
const displayItem = useMemo(() => {
|
|
23
|
+
if (isDragging && dragSlots.length > 1 && heldItem && dragRemainder !== null) {
|
|
24
|
+
return { ...heldItem, count: dragRemainder }
|
|
25
|
+
}
|
|
26
|
+
return heldItem
|
|
27
|
+
}, [isDragging, dragSlots.length, heldItem, dragRemainder])
|
|
28
|
+
|
|
21
29
|
// Update halfSize when contentSize changes
|
|
22
30
|
useEffect(() => {
|
|
23
31
|
halfSizeRef.current = Math.round(contentSize / 2)
|
|
@@ -76,10 +84,29 @@ export function CursorItem() {
|
|
|
76
84
|
}}
|
|
77
85
|
>
|
|
78
86
|
<ItemCanvas
|
|
79
|
-
item={
|
|
87
|
+
item={displayItem!}
|
|
80
88
|
size={contentSize}
|
|
81
89
|
style={{ position: 'absolute', top: 0, left: 0 }}
|
|
82
90
|
/>
|
|
91
|
+
{isDragging && dragSlots.length > 1 && dragRemainder === 0 && (
|
|
92
|
+
<span
|
|
93
|
+
className="mc-inv-cursor-zero"
|
|
94
|
+
style={{
|
|
95
|
+
position: 'absolute',
|
|
96
|
+
bottom: 0,
|
|
97
|
+
right: 0,
|
|
98
|
+
fontSize: Math.max(7, Math.round(contentSize * 0.5)),
|
|
99
|
+
fontFamily: "'Minecraftia', 'Minecraft', monospace",
|
|
100
|
+
color: '#ffff00',
|
|
101
|
+
textShadow: '1px 1px 0 #3f3f00',
|
|
102
|
+
lineHeight: 1,
|
|
103
|
+
pointerEvents: 'none',
|
|
104
|
+
zIndex: 1,
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
0
|
|
108
|
+
</span>
|
|
109
|
+
)}
|
|
83
110
|
</div>
|
|
84
111
|
)
|
|
85
112
|
}
|
|
@@ -113,9 +113,10 @@ export function HotbarExtras({ showOffhand, container, offhandItem }: HotbarExtr
|
|
|
113
113
|
style={{
|
|
114
114
|
position: 'absolute',
|
|
115
115
|
left: -(24 + GAP) * scale,
|
|
116
|
-
top:
|
|
116
|
+
top: -1 * scale,
|
|
117
117
|
width: 24 * scale,
|
|
118
|
-
height:
|
|
118
|
+
height: 24 * scale,
|
|
119
|
+
pointerEvents: 'auto',
|
|
119
120
|
}}
|
|
120
121
|
>
|
|
121
122
|
<WidgetSprite
|
|
@@ -123,12 +124,12 @@ export function HotbarExtras({ showOffhand, container, offhandItem }: HotbarExtr
|
|
|
123
124
|
srcX={24}
|
|
124
125
|
srcY={22}
|
|
125
126
|
srcW={24}
|
|
126
|
-
srcH={
|
|
127
|
+
srcH={24}
|
|
127
128
|
scale={scale}
|
|
128
129
|
style={{ left: 0, top: 0 }}
|
|
129
130
|
/>
|
|
130
131
|
{/* Item at slot 45 (offhand) */}
|
|
131
|
-
<div style={{ position: 'absolute', top:
|
|
132
|
+
<div style={{ position: 'absolute', top: 4 * scale, left: 3 * scale }}>
|
|
132
133
|
<Slot index={45} item={offhandItem} size={16 * scale} noBackground />
|
|
133
134
|
</div>
|
|
134
135
|
</div>
|
|
@@ -142,19 +143,25 @@ export function HotbarExtras({ showOffhand, container, offhandItem }: HotbarExtr
|
|
|
142
143
|
style={{
|
|
143
144
|
position: 'absolute',
|
|
144
145
|
left: (182 + GAP) * scale,
|
|
145
|
-
top:
|
|
146
|
+
top: -1 * scale,
|
|
146
147
|
width: 24 * scale,
|
|
147
|
-
height:
|
|
148
|
+
height: 24 * scale,
|
|
148
149
|
cursor: 'pointer',
|
|
150
|
+
pointerEvents: 'auto',
|
|
149
151
|
}}
|
|
150
152
|
onClick={() => sendAction({ type: 'open-inventory' })}
|
|
153
|
+
onTouchEnd={(e) => {
|
|
154
|
+
e.stopPropagation()
|
|
155
|
+
e.preventDefault()
|
|
156
|
+
sendAction({ type: 'open-inventory' })
|
|
157
|
+
}}
|
|
151
158
|
>
|
|
152
159
|
<WidgetSprite
|
|
153
160
|
url={hotbarUrl}
|
|
154
161
|
srcX={24}
|
|
155
162
|
srcY={22}
|
|
156
163
|
srcW={24}
|
|
157
|
-
srcH={
|
|
164
|
+
srcH={24}
|
|
158
165
|
scale={scale}
|
|
159
166
|
style={{ left: 0, top: 0 }}
|
|
160
167
|
/>
|
|
@@ -165,7 +172,7 @@ export function HotbarExtras({ showOffhand, container, offhandItem }: HotbarExtr
|
|
|
165
172
|
style={{
|
|
166
173
|
position: 'absolute',
|
|
167
174
|
left: (6 + i * 4) * scale,
|
|
168
|
-
top:
|
|
175
|
+
top: 11 * scale,
|
|
169
176
|
width: 2 * scale,
|
|
170
177
|
height: 2 * scale,
|
|
171
178
|
background: 'white',
|
|
@@ -94,6 +94,7 @@ export function InventoryWindow({
|
|
|
94
94
|
// Offset by borderPx to land inside the texture's slot cell (skip the 1px border)
|
|
95
95
|
left: slotDef.x * scale + borderPx,
|
|
96
96
|
top: slotDef.y * scale + borderPx,
|
|
97
|
+
...(isHotbar ? { pointerEvents: 'none' as const } : {}),
|
|
97
98
|
}}
|
|
98
99
|
>
|
|
99
100
|
<Slot
|
|
@@ -374,6 +374,7 @@ export function Slot({
|
|
|
374
374
|
<ItemCanvas
|
|
375
375
|
item={item}
|
|
376
376
|
size={renderSize}
|
|
377
|
+
noCount={!!dragPreviewEntry}
|
|
377
378
|
style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}
|
|
378
379
|
/>
|
|
379
380
|
)}
|
|
@@ -420,23 +421,33 @@ export function Slot({
|
|
|
420
421
|
)}
|
|
421
422
|
|
|
422
423
|
{dragPreviewEntry && (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
424
|
+
<>
|
|
425
|
+
{!item && heldItem && (
|
|
426
|
+
<ItemCanvas
|
|
427
|
+
item={heldItem}
|
|
428
|
+
size={renderSize}
|
|
429
|
+
noCount
|
|
430
|
+
style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}
|
|
431
|
+
/>
|
|
432
|
+
)}
|
|
433
|
+
<div
|
|
434
|
+
className="mc-inv-drag-preview-count"
|
|
435
|
+
style={{
|
|
436
|
+
position: 'absolute',
|
|
437
|
+
right: 1,
|
|
438
|
+
bottom: 1,
|
|
439
|
+
fontSize: Math.round(renderSize * 0.45),
|
|
440
|
+
fontFamily: "'Minecraftia', 'Minecraft', monospace",
|
|
441
|
+
color: '#ffff00',
|
|
442
|
+
textShadow: '1px 1px 0 #3f3f00',
|
|
443
|
+
lineHeight: 1,
|
|
444
|
+
pointerEvents: 'none',
|
|
445
|
+
zIndex: 3,
|
|
446
|
+
}}
|
|
447
|
+
>
|
|
448
|
+
{dragPreviewEntry.count}
|
|
449
|
+
</div>
|
|
450
|
+
</>
|
|
440
451
|
)}
|
|
441
452
|
|
|
442
453
|
{item && showTooltip && !mobileMenuOpen && !heldItem && (
|
|
@@ -19,6 +19,8 @@ export interface InventoryContextValue {
|
|
|
19
19
|
dragButton: 'left' | 'right' | null
|
|
20
20
|
/** Client-side preview of item counts for each slot in the current drag */
|
|
21
21
|
dragPreview: Map<number, DragPreviewEntry>
|
|
22
|
+
/** Number of items remaining on the cursor during an active spread drag */
|
|
23
|
+
dragRemainder: number | null
|
|
22
24
|
startDrag: (slotIndex: number, button: 'left' | 'right') => void
|
|
23
25
|
addDragSlot: (slotIndex: number) => void
|
|
24
26
|
endDrag: () => void
|
|
@@ -79,6 +81,7 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
79
81
|
const [dragSlots, setDragSlots] = useState<number[]>([])
|
|
80
82
|
const [dragButton, setDragButton] = useState<'left' | 'right' | null>(null)
|
|
81
83
|
const [dragPreview, setDragPreview] = useState<Map<number, DragPreviewEntry>>(new Map())
|
|
84
|
+
const [dragRemainder, setDragRemainder] = useState<number | null>(null)
|
|
82
85
|
const [activeNumberKey, setActiveNumberKey] = useState<number | null>(null)
|
|
83
86
|
const [grabOffset, setGrabOffset] = useState<{ x: number; y: number }>({ x: 0, y: 0 })
|
|
84
87
|
const [pKeyActive, setPKeyActive] = useState(false)
|
|
@@ -133,11 +136,12 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
133
136
|
[],
|
|
134
137
|
)
|
|
135
138
|
|
|
136
|
-
const computeDragPreview = useCallback((slots: number[], button: 'left' | 'right', held: ItemStack | null, ws: InventoryWindowState | null) => {
|
|
139
|
+
const computeDragPreview = useCallback((slots: number[], button: 'left' | 'right', held: ItemStack | null, ws: InventoryWindowState | null): { preview: Map<number, DragPreviewEntry>; remainder: number } => {
|
|
137
140
|
const preview = new Map<number, DragPreviewEntry>()
|
|
138
|
-
if (!held || slots.length === 0) return preview
|
|
141
|
+
if (!held || slots.length === 0) return { preview, remainder: held?.count ?? 0 }
|
|
139
142
|
|
|
140
143
|
const maxStack = getMaxStackSize(held)
|
|
144
|
+
let totalPlaced = 0
|
|
141
145
|
|
|
142
146
|
if (button === 'left') {
|
|
143
147
|
// Only spread into compatible slots (empty or same item type)
|
|
@@ -145,13 +149,15 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
145
149
|
const existingItem = ws?.slots.find((s) => s.index === idx)?.item
|
|
146
150
|
return !existingItem || isItemEqual(existingItem, held)
|
|
147
151
|
})
|
|
148
|
-
if (compatibleSlots.length === 0) return preview
|
|
152
|
+
if (compatibleSlots.length === 0) return { preview, remainder: held.count }
|
|
149
153
|
const perSlot = Math.floor(held.count / compatibleSlots.length)
|
|
150
154
|
// Vanilla behavior: if perSlot=0 (more slots than items), nothing is distributed
|
|
151
|
-
if (perSlot === 0) return preview
|
|
155
|
+
if (perSlot === 0) return { preview, remainder: held.count }
|
|
152
156
|
for (const idx of compatibleSlots) {
|
|
153
157
|
const existingCount = ws?.slots.find((s) => s.index === idx)?.item?.count ?? 0
|
|
154
158
|
const total = Math.min(existingCount + perSlot, maxStack)
|
|
159
|
+
const added = total - existingCount
|
|
160
|
+
totalPlaced += added
|
|
155
161
|
preview.set(idx, { count: total })
|
|
156
162
|
}
|
|
157
163
|
} else {
|
|
@@ -161,10 +167,12 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
161
167
|
if (existingItem && !isItemEqual(existingItem, held)) continue
|
|
162
168
|
const existingCount = existingItem ? existingItem.count : 0
|
|
163
169
|
const total = Math.min(existingCount + 1, maxStack)
|
|
170
|
+
const added = total - existingCount
|
|
171
|
+
totalPlaced += added
|
|
164
172
|
preview.set(idx, { count: total })
|
|
165
173
|
}
|
|
166
174
|
}
|
|
167
|
-
return preview
|
|
175
|
+
return { preview, remainder: held.count - totalPlaced }
|
|
168
176
|
}, [])
|
|
169
177
|
|
|
170
178
|
const startDrag = useCallback((slotIndex: number, button: 'left' | 'right') => {
|
|
@@ -174,13 +182,16 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
174
182
|
setDragButton(button)
|
|
175
183
|
setDragSlots([slotIndex])
|
|
176
184
|
setDragPreview(new Map())
|
|
185
|
+
setDragRemainder(null)
|
|
177
186
|
}, [noDragSpread])
|
|
178
187
|
|
|
179
188
|
const addDragSlot = useCallback((slotIndex: number) => {
|
|
180
189
|
setDragSlots((prev) => {
|
|
181
190
|
if (prev.includes(slotIndex)) return prev
|
|
182
191
|
const next = [...prev, slotIndex]
|
|
183
|
-
|
|
192
|
+
const result = computeDragPreview(next, dragButton!, heldItem, windowState)
|
|
193
|
+
setDragPreview(result.preview)
|
|
194
|
+
setDragRemainder(result.remainder)
|
|
184
195
|
return next
|
|
185
196
|
})
|
|
186
197
|
}, [computeDragPreview, dragButton, heldItem, windowState])
|
|
@@ -255,6 +266,7 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
255
266
|
setIsDragging(false)
|
|
256
267
|
setDragButton(null)
|
|
257
268
|
setDragPreview(new Map())
|
|
269
|
+
setDragRemainder(null)
|
|
258
270
|
}, [])
|
|
259
271
|
|
|
260
272
|
const cancelDrag = useCallback(() => {
|
|
@@ -262,6 +274,7 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
262
274
|
setDragSlots([])
|
|
263
275
|
setDragButton(null)
|
|
264
276
|
setDragPreview(new Map())
|
|
277
|
+
setDragRemainder(null)
|
|
265
278
|
}, [])
|
|
266
279
|
|
|
267
280
|
const getSlot = useCallback(
|
|
@@ -295,6 +308,7 @@ export function InventoryProvider({ connector, children, noDragSpread = false, n
|
|
|
295
308
|
dragSlots,
|
|
296
309
|
dragButton,
|
|
297
310
|
dragPreview,
|
|
311
|
+
dragRemainder,
|
|
298
312
|
startDrag,
|
|
299
313
|
addDragSlot,
|
|
300
314
|
endDrag,
|