minecraft-inventory 0.1.23 → 0.1.24
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
|
@@ -96,6 +96,13 @@ export function Slot({
|
|
|
96
96
|
}
|
|
97
97
|
}, [label, item, renderSize])
|
|
98
98
|
|
|
99
|
+
// Clean up long press timer on unmount
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
return () => {
|
|
102
|
+
if (longPressTimerRef.current) clearTimeout(longPressTimerRef.current)
|
|
103
|
+
}
|
|
104
|
+
}, [])
|
|
105
|
+
|
|
99
106
|
const isHovered = hoveredSlot === index
|
|
100
107
|
const isDragTarget = dragSlots.includes(index)
|
|
101
108
|
const dragPreviewEntry = dragPreview.get(index)
|
|
@@ -247,19 +254,63 @@ export function Slot({
|
|
|
247
254
|
|
|
248
255
|
// Mobile touch handlers
|
|
249
256
|
const touchStartRef = useRef<{ x: number; y: number; time: number } | null>(null)
|
|
257
|
+
const longPressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
258
|
+
const longPressFiredRef = useRef(false)
|
|
259
|
+
|
|
260
|
+
const cancelLongPress = useCallback(() => {
|
|
261
|
+
if (longPressTimerRef.current) {
|
|
262
|
+
clearTimeout(longPressTimerRef.current)
|
|
263
|
+
longPressTimerRef.current = null
|
|
264
|
+
}
|
|
265
|
+
}, [])
|
|
250
266
|
|
|
251
267
|
const handleTouchStart = useCallback(
|
|
252
268
|
(e: React.TouchEvent) => {
|
|
253
269
|
if (!isMobile) return
|
|
254
270
|
const touch = e.touches[0]
|
|
255
271
|
touchStartRef.current = { x: touch.clientX, y: touch.clientY, time: Date.now() }
|
|
272
|
+
longPressFiredRef.current = false
|
|
273
|
+
cancelLongPress()
|
|
274
|
+
// Long press: open mobile menu after 400ms if item exists and no held item
|
|
275
|
+
if (item && !heldItem && !disabled) {
|
|
276
|
+
const startX = touch.clientX
|
|
277
|
+
const startY = touch.clientY
|
|
278
|
+
longPressTimerRef.current = setTimeout(() => {
|
|
279
|
+
longPressFiredRef.current = true
|
|
280
|
+
setMobileTouchPos({ x: startX, y: startY })
|
|
281
|
+
setMobileMenuOpen(true)
|
|
282
|
+
}, 400)
|
|
283
|
+
}
|
|
256
284
|
},
|
|
257
|
-
[isMobile],
|
|
285
|
+
[isMobile, item, heldItem, disabled, cancelLongPress],
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
const handleTouchMove = useCallback(
|
|
289
|
+
(e: React.TouchEvent) => {
|
|
290
|
+
if (!longPressTimerRef.current) return
|
|
291
|
+
const touch = e.touches[0]
|
|
292
|
+
const start = touchStartRef.current
|
|
293
|
+
if (!start) return
|
|
294
|
+
if (Math.abs(touch.clientX - start.x) > 10 || Math.abs(touch.clientY - start.y) > 10) {
|
|
295
|
+
cancelLongPress()
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
[cancelLongPress],
|
|
258
299
|
)
|
|
259
300
|
|
|
260
301
|
const handleTouchEnd = useCallback(
|
|
261
302
|
(e: React.TouchEvent) => {
|
|
303
|
+
cancelLongPress()
|
|
262
304
|
if (!isMobile || disabled) return
|
|
305
|
+
// If long press opened the menu, don't process the tap
|
|
306
|
+
if (longPressFiredRef.current) {
|
|
307
|
+
longPressFiredRef.current = false
|
|
308
|
+
e.stopPropagation()
|
|
309
|
+
e.preventDefault()
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
// If mobile menu is open, let menu buttons handle their own events
|
|
313
|
+
if (mobileMenuOpen) return
|
|
263
314
|
const start = touchStartRef.current
|
|
264
315
|
if (!start) return
|
|
265
316
|
touchStartRef.current = null
|
|
@@ -297,7 +348,7 @@ export function Slot({
|
|
|
297
348
|
setFocusedSlot(null)
|
|
298
349
|
}
|
|
299
350
|
},
|
|
300
|
-
[isMobile, disabled, heldItem, sendAction, index, pKeyActive, setPKeyActive, focusedSlot, setFocusedSlot, onClickOverride],
|
|
351
|
+
[isMobile, disabled, heldItem, sendAction, index, pKeyActive, setPKeyActive, focusedSlot, setFocusedSlot, onClickOverride, cancelLongPress, mobileMenuOpen],
|
|
301
352
|
)
|
|
302
353
|
|
|
303
354
|
const handleMobilePickAll = useCallback(() => {
|
|
@@ -319,8 +370,16 @@ export function Slot({
|
|
|
319
370
|
if (isNaN(amount) || amount <= 0) return
|
|
320
371
|
setMobileMenuOpen(false)
|
|
321
372
|
setShowTooltip(false)
|
|
322
|
-
|
|
323
|
-
|
|
373
|
+
const take = Math.min(amount, item.count)
|
|
374
|
+
if (take >= item.count) {
|
|
375
|
+
// Take all: just left-click
|
|
376
|
+
sendAction({ type: 'click', slotIndex: index, button: 'left', mode: 'normal' })
|
|
377
|
+
} else {
|
|
378
|
+
// Pick up all, then put back (count - take) items one-by-one via right-click
|
|
379
|
+
sendAction({ type: 'click', slotIndex: index, button: 'left', mode: 'normal' })
|
|
380
|
+
for (let i = 0; i < item.count - take; i++) {
|
|
381
|
+
sendAction({ type: 'click', slotIndex: index, button: 'right', mode: 'normal' })
|
|
382
|
+
}
|
|
324
383
|
}
|
|
325
384
|
}, [item, sendAction, index])
|
|
326
385
|
|
|
@@ -368,6 +427,7 @@ export function Slot({
|
|
|
368
427
|
onDoubleClick={handleDoubleClick}
|
|
369
428
|
onContextMenu={handleContextMenu}
|
|
370
429
|
onTouchStart={handleTouchStart}
|
|
430
|
+
onTouchMove={handleTouchMove}
|
|
371
431
|
onTouchEnd={handleTouchEnd}
|
|
372
432
|
aria-label={
|
|
373
433
|
label ??
|
|
@@ -462,7 +522,12 @@ export function Slot({
|
|
|
462
522
|
|
|
463
523
|
{isMobile && mobileMenuOpen && item && (
|
|
464
524
|
<>
|
|
465
|
-
<div
|
|
525
|
+
<div
|
|
526
|
+
className={styles.mobileOverlay}
|
|
527
|
+
onClick={closeMobileMenu}
|
|
528
|
+
onTouchStart={(e) => e.stopPropagation()}
|
|
529
|
+
onTouchEnd={(e) => { e.stopPropagation(); e.preventDefault(); closeMobileMenu() }}
|
|
530
|
+
/>
|
|
466
531
|
<MobileSlotMenu
|
|
467
532
|
item={item}
|
|
468
533
|
x={mobileTouchPos.x}
|
|
@@ -509,10 +574,18 @@ function MobileSlotMenu({ item, x, y, onPickAll, onPickHalf, onPickCustom, onDro
|
|
|
509
574
|
setPos({ left, top })
|
|
510
575
|
}, [x, y])
|
|
511
576
|
|
|
577
|
+
// Wrapper to handle both touch and click, preventing event bubbling to the slot
|
|
578
|
+
const touchBtn = (handler: () => void) => ({
|
|
579
|
+
onTouchEnd: (e: React.TouchEvent) => { e.stopPropagation(); e.preventDefault(); handler() },
|
|
580
|
+
onClick: (e: React.MouseEvent) => { e.stopPropagation(); handler() },
|
|
581
|
+
})
|
|
582
|
+
|
|
512
583
|
return (
|
|
513
584
|
<div
|
|
514
585
|
ref={menuRef}
|
|
515
586
|
className={styles.mobileMenu}
|
|
587
|
+
onTouchStart={(e) => e.stopPropagation()}
|
|
588
|
+
onTouchEnd={(e) => e.stopPropagation()}
|
|
516
589
|
style={{
|
|
517
590
|
position: 'fixed',
|
|
518
591
|
left: pos.left,
|
|
@@ -527,11 +600,11 @@ function MobileSlotMenu({ item, x, y, onPickAll, onPickHalf, onPickCustom, onDro
|
|
|
527
600
|
<div className={styles.mobileMenuTitle}>
|
|
528
601
|
{item.displayName ?? item.name ?? `Item #${item.type}`} ×{item.count}
|
|
529
602
|
</div>
|
|
530
|
-
<button className={styles.mobileBtn}
|
|
531
|
-
<button className={styles.mobileBtn}
|
|
532
|
-
<button className={styles.mobileBtn}
|
|
533
|
-
<button className={[styles.mobileBtn, styles.mobileBtnDanger].join(' ')}
|
|
534
|
-
<button className={styles.mobileBtn}
|
|
603
|
+
<button className={styles.mobileBtn} {...touchBtn(onPickAll)}>Take All ({item.count})</button>
|
|
604
|
+
<button className={styles.mobileBtn} {...touchBtn(onPickHalf)}>Take Half ({Math.ceil(item.count / 2)})</button>
|
|
605
|
+
<button className={styles.mobileBtn} {...touchBtn(onPickCustom)}>Take Amount…</button>
|
|
606
|
+
<button className={[styles.mobileBtn, styles.mobileBtnDanger].join(' ')} {...touchBtn(onDrop)}>Drop</button>
|
|
607
|
+
<button className={styles.mobileBtn} {...touchBtn(onClose)}>Cancel</button>
|
|
535
608
|
</div>
|
|
536
609
|
)
|
|
537
610
|
}
|