refactor: inventory UI
This commit is contained in:
parent
b37d3513f6
commit
ce9a1aeba7
|
@ -1,54 +1,28 @@
|
|||
import styles from './bag.module.css';
|
||||
import Slot from './slot.jsx';
|
||||
|
||||
import Grid from './grid.jsx';
|
||||
|
||||
/**
|
||||
* Inventory bag. 10-40 slots of inventory.
|
||||
*/
|
||||
export default function Bag({
|
||||
isInventoryOpen,
|
||||
onActivate,
|
||||
slots,
|
||||
}) {
|
||||
const Slots = slots.map((slot, i) => (
|
||||
<div
|
||||
className={
|
||||
[styles.slotWrapper]
|
||||
.filter(Boolean).join(' ')
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
<Slot
|
||||
icon={slot?.icon}
|
||||
// onMouseDown={(event) => {
|
||||
// onActivate(i)
|
||||
// event.stopPropagation();
|
||||
// }}
|
||||
// onMouseUp={(event) => {
|
||||
// event.stopPropagation();
|
||||
// }}
|
||||
// onDragOver={(event) => {
|
||||
// event.preventDefault();
|
||||
// }}
|
||||
// onDragStart={(event) => {
|
||||
// if (!slot) {
|
||||
// event.preventDefault();
|
||||
// }
|
||||
// event.dataTransfer.setData('silphius/item', i);
|
||||
// onActivate(i);
|
||||
// }}
|
||||
// onDrop={(event) => {
|
||||
// event.preventDefault();
|
||||
// onActivate(i);
|
||||
// }}
|
||||
qty={slot?.qty}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
return (
|
||||
<div
|
||||
className={styles.bag}
|
||||
style={isInventoryOpen ? {transition: 'none'} : {left: '-440px'}}
|
||||
style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, left: '-440px'}}
|
||||
>
|
||||
{Slots}
|
||||
|
||||
<Grid
|
||||
color="rgba(02, 02, 28, 0.6)"
|
||||
columns={10}
|
||||
label="Bag"
|
||||
onActivate={onActivate}
|
||||
slots={slots}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,31 +1,10 @@
|
|||
.bag {
|
||||
align-self: left;
|
||||
--border: calc(var(--unit) * 3px);
|
||||
background-color: rgba(02, 02, 28, 0.6);
|
||||
border: var(--border) solid #444444;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
left: calc(var(--unit) * 20px);
|
||||
left: 20px;
|
||||
line-height: 0;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: calc(var(--unit) * 90px);
|
||||
transition: left 150ms;
|
||||
max-width: 430.5px;
|
||||
}
|
||||
|
||||
.slotWrapper {
|
||||
border: var(--border) solid #999999;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
&:not(:nth-child(10n)) {
|
||||
border-right: none;
|
||||
}
|
||||
&:not(:nth-last-of-type(-n+10)) {
|
||||
border-bottom: none;
|
||||
}
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
top: 74px;
|
||||
transition: left 150ms, opacity 200ms;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ export default function Dom({children}) {
|
|||
<style>{`
|
||||
.${styles.dom}{
|
||||
--scale: ${scale};
|
||||
--unit: calc(${RESOLUTION.x} / 1000);
|
||||
}
|
||||
`}</style>
|
||||
)}
|
||||
|
|
69
app/react/components/dom/grid.jsx
Normal file
69
app/react/components/dom/grid.jsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import styles from './grid.module.css';
|
||||
import Slot from './slot.jsx';
|
||||
|
||||
/**
|
||||
* Inventory grid.
|
||||
*/
|
||||
export default function Grid({
|
||||
active = -1,
|
||||
color,
|
||||
columns,
|
||||
label,
|
||||
onActivate,
|
||||
slots,
|
||||
}) {
|
||||
const Slots = slots.map((slot, i) => (
|
||||
<div
|
||||
className={
|
||||
[styles.slot, active === i && styles.active]
|
||||
.filter(Boolean).join(' ')
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
<Slot
|
||||
icon={slot?.icon}
|
||||
onMouseDown={(event) => {
|
||||
onActivate(i)
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onMouseUp={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onDragOver={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
onDragStart={(event) => {
|
||||
if (!slot) {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.dataTransfer.setData('silphius/item', i);
|
||||
onActivate(i);
|
||||
}}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault();
|
||||
onActivate(i);
|
||||
}}
|
||||
qty={slot?.qty}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
return (
|
||||
<div className={styles.gridWrapper}>
|
||||
<p className={styles.label}>{label}</p>
|
||||
<div
|
||||
className={styles.grid}
|
||||
style={{
|
||||
'--color': color,
|
||||
'--columns': columns,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.innerGrid}
|
||||
>
|
||||
{Slots}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
45
app/react/components/dom/grid.module.css
Normal file
45
app/react/components/dom/grid.module.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
.grid {
|
||||
--border: 2.5px;
|
||||
border: var(--border) solid #444444;
|
||||
line-height: 0;
|
||||
opacity: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.innerGrid {
|
||||
background-color: var(--color);
|
||||
border-bottom: var(--border) solid #999999;
|
||||
border-right: var(--border) solid #999999;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--columns), 1fr);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||
margin: 0;
|
||||
text-shadow:
|
||||
0px -1px 0px black,
|
||||
1px 0px 0px black,
|
||||
0px 1px 0px black,
|
||||
-1px 0px 0px black
|
||||
;
|
||||
}
|
||||
|
||||
.slot {
|
||||
border-left: var(--border) solid #999999;
|
||||
border-top: var(--border) solid #999999;
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
&.active {
|
||||
border: var(--border) solid yellow;
|
||||
margin-bottom: calc(-1 * var(--border));
|
||||
margin-right: calc(-1 * var(--border));
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import styles from './hotbar.module.css';
|
||||
import Slot from './slot.jsx';
|
||||
import gridStyles from './grid.module.css';
|
||||
|
||||
import Grid from './grid.jsx';
|
||||
|
||||
/**
|
||||
* The hotbar. 10 slots of inventory with an active selection.
|
||||
|
@ -10,48 +12,24 @@ export default function Hotbar({
|
|||
onActivate,
|
||||
slots,
|
||||
}) {
|
||||
const Slots = slots.map((slot, i) => (
|
||||
<div
|
||||
className={
|
||||
[styles.slotWrapper, active === i && styles.active]
|
||||
.filter(Boolean).join(' ')
|
||||
}
|
||||
key={i}
|
||||
>
|
||||
<Slot
|
||||
icon={slot?.icon}
|
||||
onMouseDown={(event) => {
|
||||
onActivate(i)
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onMouseUp={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
onDragOver={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
onDragStart={(event) => {
|
||||
if (!slot) {
|
||||
event.preventDefault();
|
||||
}
|
||||
event.dataTransfer.setData('silphius/item', i);
|
||||
onActivate(i);
|
||||
}}
|
||||
onDrop={(event) => {
|
||||
event.preventDefault();
|
||||
onActivate(i);
|
||||
}}
|
||||
qty={slot?.qty}
|
||||
/>
|
||||
</div>
|
||||
));
|
||||
return (
|
||||
<div
|
||||
className={styles.hotbar}
|
||||
style={hotbarIsHidden ? {top: '-50px'} : {transition: 'none'}}
|
||||
style={hotbarIsHidden ? {opacity: 0, top: '-50px'} : {transition: 'opacity 50ms'}}
|
||||
>
|
||||
<p className={styles.label}>{slots[active] && slots[active].label}</p>
|
||||
{Slots}
|
||||
<style>{`
|
||||
.${styles.hotbar} .${gridStyles.label} {
|
||||
text-align: center;
|
||||
}
|
||||
`}</style>
|
||||
<Grid
|
||||
active={active}
|
||||
color="rgba(02, 02, 57, 0.6)"
|
||||
columns={10}
|
||||
label={slots[active] && slots[active].label}
|
||||
onActivate={onActivate}
|
||||
slots={slots}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
.hotbar {
|
||||
align-self: left;
|
||||
--border: calc(var(--unit) * 3px);
|
||||
background-color: rgba(02, 02, 57, 0.6);
|
||||
border: var(--border) solid #444444;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
left: calc(var(--unit) * 20px);
|
||||
left: 20px;
|
||||
line-height: 0;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: calc(var(--unit) * 20px);
|
||||
transition: top 150ms;
|
||||
top: 4px;
|
||||
transition: top 150ms, opacity 200ms;
|
||||
}
|
||||
|
||||
.label {
|
||||
|
@ -28,27 +25,3 @@
|
|||
top: -17.5px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.slotWrapper {
|
||||
border: var(--border) solid #999999;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
line-height: 0;
|
||||
padding: 0;
|
||||
|
||||
&.active + .slotWrapper {
|
||||
border-left: none;
|
||||
}
|
||||
&:not(:last-child) {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-right: var(--border) solid #999999;
|
||||
border-color: yellow;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.slot {
|
||||
--size: calc(var(--unit) * 50px);
|
||||
--space: calc(var(--unit) * 10px);
|
||||
--size: 40px;
|
||||
--space: 7px;
|
||||
background-color: transparent;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {memo, useCallback, useEffect, useRef, useState} from 'react';
|
||||
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||
|
||||
import {useClient, usePacket} from '@/react/context/client.js';
|
||||
import {useDebug} from '@/react/context/debug.js';
|
||||
|
@ -14,6 +14,7 @@ import Chat from './dom/chat/chat.jsx';
|
|||
import Bag from './dom/bag.jsx';
|
||||
import Dom from './dom/dom.jsx';
|
||||
import Entities from './dom/entities.jsx';
|
||||
import Grid from './dom/grid.jsx';
|
||||
import HotBar from './dom/hotbar.jsx';
|
||||
import Pixi from './pixi/pixi.jsx';
|
||||
import Devtools from './devtools.jsx';
|
||||
|
@ -25,7 +26,7 @@ const KEY_MAP = {
|
|||
};
|
||||
|
||||
function emptySlots() {
|
||||
return Array(10).fill(undefined);
|
||||
return Array(40).fill(undefined);
|
||||
}
|
||||
|
||||
const devEventsChannel = new EventEmitter();
|
||||
|
@ -43,7 +44,7 @@ function Ui({disconnected}) {
|
|||
const [devtoolsIsOpen, setDevtoolsIsOpen] = useState(false);
|
||||
const ratio = (RESOLUTION.x * (devtoolsIsOpen ? 2 : 1)) / RESOLUTION.y;
|
||||
const [camera, setCamera] = useState({x: 0, y: 0});
|
||||
const [hotbarSlots, setHotbarSlots] = useState(emptySlots());
|
||||
const [inventorySlots, setInventorySlots] = useState(emptySlots());
|
||||
const [activeSlot, setActiveSlot] = useState(0);
|
||||
const [scale, setScale] = useState(2);
|
||||
const [Components, setComponents] = useState();
|
||||
|
@ -123,6 +124,17 @@ function Ui({disconnected}) {
|
|||
chatIsOpen,
|
||||
client,
|
||||
]);
|
||||
const keepHotbarOpen = useCallback(() => {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
}, [hotbarHideHandle, isInventoryOpen]);
|
||||
useEffect(() => {
|
||||
return addKeyListener(document.body, ({event, type, payload}) => {
|
||||
if ('Escape' === payload && 'keyDown' === type && chatIsOpen) {
|
||||
|
@ -166,7 +178,10 @@ function Ui({disconnected}) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'Tab': {
|
||||
case '`': {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if ('keyDown' === type) {
|
||||
if (isInventoryOpen) {
|
||||
setHotbarIsHidden(true);
|
||||
|
@ -199,150 +214,70 @@ function Ui({disconnected}) {
|
|||
}
|
||||
case '1': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 1};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '2': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 2};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '3': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 3};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '4': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 4};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '5': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 5};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '6': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 6};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '7': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 7};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '8': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 8};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '9': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 9};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '0': {
|
||||
if ('keyDown' === type) {
|
||||
if (!isInventoryOpen) {
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
setHotbarHideHandle(setTimeout(() => {
|
||||
setHotbarIsHidden(true);
|
||||
}, 4000));
|
||||
}
|
||||
keepHotbarOpen();
|
||||
actionPayload = {type: 'changeSlot', value: 10};
|
||||
}
|
||||
break;
|
||||
|
@ -362,6 +297,7 @@ function Ui({disconnected}) {
|
|||
devtoolsIsOpen,
|
||||
hotbarHideHandle,
|
||||
isInventoryOpen,
|
||||
keepHotbarOpen,
|
||||
monopolizers,
|
||||
setDebug,
|
||||
setScale,
|
||||
|
@ -400,11 +336,11 @@ function Ui({disconnected}) {
|
|||
if (localMainEntity === id) {
|
||||
if (update.Inventory) {
|
||||
setBufferSlot(entity.Inventory.item(0));
|
||||
const newHotbarSlots = emptySlots();
|
||||
for (let i = 1; i < 11; ++i) {
|
||||
newHotbarSlots[i - 1] = entity.Inventory.item(i);
|
||||
const newInventorySlots = emptySlots();
|
||||
for (let i = 1; i < 41; ++i) {
|
||||
newInventorySlots[i - 1] = entity.Inventory.item(i);
|
||||
}
|
||||
setHotbarSlots(newHotbarSlots);
|
||||
setInventorySlots(newInventorySlots);
|
||||
}
|
||||
if (update.Wielder && 'activeSlot' in update.Wielder) {
|
||||
setActiveSlot(update.Wielder.activeSlot);
|
||||
|
@ -568,16 +504,24 @@ function Ui({disconnected}) {
|
|||
active={activeSlot}
|
||||
hotbarIsHidden={hotbarIsHidden}
|
||||
onActivate={(i) => {
|
||||
keepHotbarOpen();
|
||||
client.send({
|
||||
type: 'Action',
|
||||
payload: {type: 'swapSlots', value: [0, i + 1]},
|
||||
});
|
||||
}}
|
||||
slots={hotbarSlots}
|
||||
slots={inventorySlots.slice(0, 10)}
|
||||
/>
|
||||
<Bag
|
||||
isInventoryOpen={isInventoryOpen}
|
||||
slots={Array(30).fill(undefined)}
|
||||
onActivate={(i) => {
|
||||
keepHotbarOpen();
|
||||
client.send({
|
||||
type: 'Action',
|
||||
payload: {type: 'swapSlots', value: [0, i + 11]},
|
||||
});
|
||||
}}
|
||||
slots={inventorySlots.slice(10)}
|
||||
/>
|
||||
<Entities
|
||||
camera={camera}
|
||||
|
@ -618,4 +562,4 @@ function Ui({disconnected}) {
|
|||
);
|
||||
}
|
||||
|
||||
export default memo(Ui);
|
||||
export default Ui;
|
||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.4 KiB |
Loading…
Reference in New Issue
Block a user