minecraft-inventory 0.1.17 → 0.1.18

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecraft-inventory",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "release": {
@@ -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={heldItem}
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
  }
@@ -304,7 +304,7 @@ export function InventoryOverlay({
304
304
  lineHeight: 1,
305
305
  }}
306
306
  >
307
- INV 0.1.17
307
+ INV 0.1.18
308
308
  </a>
309
309
  )}
310
310
 
@@ -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
- <div
424
- className="mc-inv-drag-preview-count"
425
- style={{
426
- position: 'absolute',
427
- right: 1,
428
- bottom: 1,
429
- fontSize: Math.round(renderSize * 0.45),
430
- fontFamily: "'Minecraftia', 'Minecraft', monospace",
431
- color: '#ffff00',
432
- textShadow: '1px 1px 0 #3f3f00',
433
- lineHeight: 1,
434
- pointerEvents: 'none',
435
- zIndex: 3,
436
- }}
437
- >
438
- {dragPreviewEntry.count}
439
- </div>
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
- setDragPreview(computeDragPreview(next, dragButton!, heldItem, windowState))
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,