feat: chat and dialogue++
This commit is contained in:
parent
dd456743f8
commit
5a4666ae49
|
@ -1,8 +1,11 @@
|
||||||
|
import {createElement} from 'react';
|
||||||
import mdx from 'remark-mdx';
|
import mdx from 'remark-mdx';
|
||||||
import parse from 'remark-parse';
|
import parse from 'remark-parse';
|
||||||
import {unified} from 'unified';
|
import {unified} from 'unified';
|
||||||
import {visitParents as visit} from 'unist-util-visit-parents';
|
import {visitParents as visit} from 'unist-util-visit-parents';
|
||||||
|
|
||||||
|
import {TAU} from '@/util/math.js';
|
||||||
|
|
||||||
const parser = unified().use(parse).use(mdx);
|
const parser = unified().use(parse).use(mdx);
|
||||||
|
|
||||||
function computeParams(ancestors) {
|
function computeParams(ancestors) {
|
||||||
|
@ -54,3 +57,65 @@ export function parseLetters(source) {
|
||||||
});
|
});
|
||||||
return letters;
|
return letters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function render(letters, className) {
|
||||||
|
return (caret, radians) => (
|
||||||
|
letters
|
||||||
|
.map(({character, indices, params}, i) => {
|
||||||
|
let color = 'inherit';
|
||||||
|
let fade = 0;
|
||||||
|
let fontStyle = 'normal';
|
||||||
|
let fontWeight = 'normal';
|
||||||
|
let left = 0;
|
||||||
|
let opacity = 1;
|
||||||
|
let top = 0;
|
||||||
|
if (params.blink) {
|
||||||
|
const {frequency = 1} = params.blink;
|
||||||
|
opacity = (radians * (2 / frequency) % TAU) > (TAU / 2) ? opacity : 0;
|
||||||
|
}
|
||||||
|
if (params.fade) {
|
||||||
|
const {frequency = 1} = params.fade;
|
||||||
|
fade = frequency;
|
||||||
|
}
|
||||||
|
if (params.wave) {
|
||||||
|
const {frequency = 1, magnitude = 3} = params.wave;
|
||||||
|
top += magnitude * Math.cos((radians * (1 / frequency)) + TAU * indices.wave / params.wave.length);
|
||||||
|
}
|
||||||
|
if (params.rainbow) {
|
||||||
|
const {frequency = 1} = params.rainbow;
|
||||||
|
color = `hsl(${(radians * (1 / frequency)) + TAU * indices.rainbow / params.rainbow.length}rad 100 50)`;
|
||||||
|
}
|
||||||
|
if (params.shake) {
|
||||||
|
const {magnitude = 1} = params.shake;
|
||||||
|
left += (Math.random() * magnitude * 2) - magnitude;
|
||||||
|
top += (Math.random() * magnitude * 2) - magnitude;
|
||||||
|
}
|
||||||
|
if (params.em) {
|
||||||
|
fontStyle = 'italic';
|
||||||
|
}
|
||||||
|
if (params.strong) {
|
||||||
|
fontWeight = 'bold';
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
createElement(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
className,
|
||||||
|
key: i,
|
||||||
|
style: {
|
||||||
|
color,
|
||||||
|
fontStyle,
|
||||||
|
fontWeight,
|
||||||
|
left: `${left}px`,
|
||||||
|
opacity: i <= caret ? opacity : 0,
|
||||||
|
position: 'relative',
|
||||||
|
top: `${top}px`,
|
||||||
|
transition: `opacity ${fade}s`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
character,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
import {useEffect, useState} from 'react';
|
|
||||||
|
|
||||||
import {useClient} from '@/context/client.js';
|
|
||||||
|
|
||||||
import styles from './chat.module.css';
|
|
||||||
|
|
||||||
export default function Chat({
|
|
||||||
chatHistory,
|
|
||||||
onClose,
|
|
||||||
message,
|
|
||||||
setChatHistory,
|
|
||||||
setMessage,
|
|
||||||
}) {
|
|
||||||
const client = useClient();
|
|
||||||
const [disabled, setDisabled] = useState(true);
|
|
||||||
const [historyCaret, setHistoryCaret] = useState(0);
|
|
||||||
useEffect(() => {
|
|
||||||
setDisabled(false);
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<div className={styles.chat}>
|
|
||||||
<form
|
|
||||||
onSubmit={(event) => {
|
|
||||||
if (message) {
|
|
||||||
client.send({
|
|
||||||
type: 'Action',
|
|
||||||
payload: {type: 'chat', value: message},
|
|
||||||
});
|
|
||||||
setChatHistory([message, ...chatHistory]);
|
|
||||||
setMessage('');
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
disabled={disabled}
|
|
||||||
onChange={(event) => {
|
|
||||||
setMessage(event.target.value);
|
|
||||||
}}
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
switch (event.key) {
|
|
||||||
case 'ArrowDown': {
|
|
||||||
if (0 === historyCaret) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let localHistoryCaret = historyCaret - 1;
|
|
||||||
setMessage(chatHistory[localHistoryCaret])
|
|
||||||
setHistoryCaret(localHistoryCaret);
|
|
||||||
if (0 === localHistoryCaret) {
|
|
||||||
setChatHistory(chatHistory.slice(1));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ArrowUp': {
|
|
||||||
if (historyCaret === chatHistory.length - 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let localHistoryCaret = historyCaret;
|
|
||||||
let localChatHistory = chatHistory;
|
|
||||||
if (0 === historyCaret) {
|
|
||||||
localChatHistory = [message, ...localChatHistory];
|
|
||||||
setChatHistory(localChatHistory);
|
|
||||||
}
|
|
||||||
localHistoryCaret += 1;
|
|
||||||
setMessage(localChatHistory[localHistoryCaret])
|
|
||||||
setHistoryCaret(localHistoryCaret);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'Escape': {
|
|
||||||
onClose();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseDown={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
}}
|
|
||||||
maxLength="255"
|
|
||||||
ref={(element) => {
|
|
||||||
if (element) {
|
|
||||||
element.focus();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
type="text"
|
|
||||||
value={message}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
28
app/react-components/dom/chat/chat.jsx
Normal file
28
app/react-components/dom/chat/chat.jsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import styles from './chat.module.css';
|
||||||
|
|
||||||
|
import Input from './input.jsx';
|
||||||
|
import Messages from './messages.jsx';
|
||||||
|
|
||||||
|
export default function Chat({
|
||||||
|
chatHistory,
|
||||||
|
chatMessages,
|
||||||
|
onClose,
|
||||||
|
message,
|
||||||
|
setChatHistory,
|
||||||
|
setMessage,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={styles.chat}>
|
||||||
|
<Messages
|
||||||
|
chatMessages={chatMessages}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
chatHistory={chatHistory}
|
||||||
|
onClose={onClose}
|
||||||
|
message={message}
|
||||||
|
setChatHistory={setChatHistory}
|
||||||
|
setMessage={setMessage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
15
app/react-components/dom/chat/chat.module.css
Normal file
15
app/react-components/dom/chat/chat.module.css
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.chat {
|
||||||
|
background-color: #00000044;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
max-height: 25%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Cookbook";
|
||||||
|
src: url("/assets/fonts/Cookbook.woff") format("woff");
|
||||||
|
}
|
84
app/react-components/dom/chat/input.jsx
Normal file
84
app/react-components/dom/chat/input.jsx
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
import {useClient} from '@/context/client.js';
|
||||||
|
|
||||||
|
import styles from './input.module.css';
|
||||||
|
|
||||||
|
export default function ChatInput({
|
||||||
|
chatHistory,
|
||||||
|
message,
|
||||||
|
setChatHistory,
|
||||||
|
setMessage,
|
||||||
|
}) {
|
||||||
|
const client = useClient();
|
||||||
|
const [disabled, setDisabled] = useState(true);
|
||||||
|
const [historyCaret, setHistoryCaret] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
setDisabled(false);
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className={styles.input}
|
||||||
|
onSubmit={(event) => {
|
||||||
|
if (message) {
|
||||||
|
client.send({
|
||||||
|
type: 'Action',
|
||||||
|
payload: {type: 'chat', value: message},
|
||||||
|
});
|
||||||
|
setChatHistory([message, ...chatHistory]);
|
||||||
|
setMessage('');
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(event) => {
|
||||||
|
setMessage(event.target.value);
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
switch (event.key) {
|
||||||
|
case 'ArrowDown': {
|
||||||
|
if (0 === historyCaret) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let localHistoryCaret = historyCaret - 1;
|
||||||
|
setMessage(chatHistory[localHistoryCaret])
|
||||||
|
setHistoryCaret(localHistoryCaret);
|
||||||
|
if (0 === localHistoryCaret) {
|
||||||
|
setChatHistory(chatHistory.slice(1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ArrowUp': {
|
||||||
|
if (historyCaret === chatHistory.length - 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let localHistoryCaret = historyCaret;
|
||||||
|
let localChatHistory = chatHistory;
|
||||||
|
if (0 === historyCaret) {
|
||||||
|
localChatHistory = [message, ...localChatHistory];
|
||||||
|
setChatHistory(localChatHistory);
|
||||||
|
}
|
||||||
|
localHistoryCaret += 1;
|
||||||
|
setMessage(localChatHistory[localHistoryCaret])
|
||||||
|
setHistoryCaret(localHistoryCaret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseDown={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
maxLength="255"
|
||||||
|
ref={(element) => {
|
||||||
|
if (element) {
|
||||||
|
element.focus();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="text"
|
||||||
|
value={message}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,17 +1,15 @@
|
||||||
.chat {
|
|
||||||
background-color: #00000044;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat form {
|
.input {
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
background-color: #00000044;
|
background-color: #00000044;
|
||||||
border: 1px solid #333333;
|
border: 1px solid #333333;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
font-family: "Cookbook";
|
||||||
|
font-size: 16px;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
|
padding: 0;
|
||||||
|
padding-left: 4px;
|
||||||
width: calc(100% - 8px);
|
width: calc(100% - 8px);
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
border: 1px solid #999999;
|
border: 1px solid #999999;
|
33
app/react-components/dom/chat/message.jsx
Normal file
33
app/react-components/dom/chat/message.jsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import {useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
import {render} from '@/dialogue.js';
|
||||||
|
import {TAU} from '@/util/math.js';
|
||||||
|
|
||||||
|
import styles from './message.module.css';
|
||||||
|
|
||||||
|
export default function Message({letters}) {
|
||||||
|
const [radians, setRadians] = useState(0);
|
||||||
|
useEffect(() => {
|
||||||
|
setRadians(0);
|
||||||
|
let handle;
|
||||||
|
let last;
|
||||||
|
const spin = (ts) => {
|
||||||
|
if ('undefined' === typeof last) {
|
||||||
|
last = ts;
|
||||||
|
}
|
||||||
|
const elapsed = (ts - last) / 1000;
|
||||||
|
last = ts;
|
||||||
|
setRadians((radians) => radians + (elapsed * TAU));
|
||||||
|
handle = requestAnimationFrame(spin);
|
||||||
|
};
|
||||||
|
handle = requestAnimationFrame(spin);
|
||||||
|
return () => {
|
||||||
|
cancelAnimationFrame(handle);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<div className={styles.message}>
|
||||||
|
{render(letters, '')(letters.length - 1, radians)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
4
app/react-components/dom/chat/message.module.css
Normal file
4
app/react-components/dom/chat/message.module.css
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.message {
|
||||||
|
color: #ffffff;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
16
app/react-components/dom/chat/messages.jsx
Normal file
16
app/react-components/dom/chat/messages.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import Message from './message.jsx';
|
||||||
|
|
||||||
|
import styles from './messages.module.css';
|
||||||
|
|
||||||
|
export default function Messages({chatMessages}) {
|
||||||
|
return (
|
||||||
|
<div className={styles.messages}>
|
||||||
|
{chatMessages.map((letters, i) => (
|
||||||
|
<Message
|
||||||
|
key={i}
|
||||||
|
letters={letters}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
5
app/react-components/dom/chat/messages.module.css
Normal file
5
app/react-components/dom/chat/messages.module.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.messages {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
import {useEffect, useRef, useState} from 'react';
|
import {useEffect, useMemo, useRef, useState} from 'react';
|
||||||
|
|
||||||
import {RESOLUTION} from '@/constants.js';
|
import {RESOLUTION} from '@/constants.js';
|
||||||
import {useDomScale} from '@/context/dom-scale.js';
|
import {useDomScale} from '@/context/dom-scale.js';
|
||||||
|
import {render} from '@/dialogue.js';
|
||||||
import {TAU} from '@/util/math.js';
|
import {TAU} from '@/util/math.js';
|
||||||
|
|
||||||
import styles from './dialogue.module.css';
|
import styles from './dialogue.module.css';
|
||||||
|
@ -88,6 +89,10 @@ export default function Dialogue({
|
||||||
cancelAnimationFrame(handle);
|
cancelAnimationFrame(handle);
|
||||||
};
|
};
|
||||||
}, [dialogue, domScale, ref]);
|
}, [dialogue, domScale, ref]);
|
||||||
|
const localRender = useMemo(
|
||||||
|
() => render(dialogue.letters, styles.letter),
|
||||||
|
[dialogue.letters],
|
||||||
|
);
|
||||||
const origin = 'function' === typeof dialogue.origin
|
const origin = 'function' === typeof dialogue.origin
|
||||||
? dialogue.origin()
|
? dialogue.origin()
|
||||||
: dialogue.origin || {x: 0, y: 0};
|
: dialogue.origin || {x: 0, y: 0};
|
||||||
|
@ -175,62 +180,7 @@ export default function Dialogue({
|
||||||
<polygon points="0 0, 24 0, 12 24" />
|
<polygon points="0 0, 24 0, 12 24" />
|
||||||
</svg>
|
</svg>
|
||||||
<p className={styles.letters}>
|
<p className={styles.letters}>
|
||||||
{
|
{localRender(caret, radians)}
|
||||||
dialogue.letters
|
|
||||||
.map(({character, indices, params}, i) => {
|
|
||||||
let color = 'inherit';
|
|
||||||
let fade = 0;
|
|
||||||
let fontStyle = 'normal';
|
|
||||||
let fontWeight = 'normal';
|
|
||||||
let left = 0;
|
|
||||||
let opacity = 1;
|
|
||||||
let top = 0;
|
|
||||||
if (params.blink) {
|
|
||||||
const {frequency = 1} = params.blink;
|
|
||||||
opacity = (radians * (2 / frequency) % TAU) > (TAU / 2) ? opacity : 0;
|
|
||||||
}
|
|
||||||
if (params.fade) {
|
|
||||||
const {frequency = 1} = params.fade;
|
|
||||||
fade = frequency;
|
|
||||||
}
|
|
||||||
if (params.wave) {
|
|
||||||
const {frequency = 1, magnitude = 3} = params.wave;
|
|
||||||
top += magnitude * Math.cos((radians * (1 / frequency)) + TAU * indices.wave / params.wave.length);
|
|
||||||
}
|
|
||||||
if (params.rainbow) {
|
|
||||||
const {frequency = 1} = params.rainbow;
|
|
||||||
color = `hsl(${(radians * (1 / frequency)) + TAU * indices.rainbow / params.rainbow.length}rad 100 50)`;
|
|
||||||
}
|
|
||||||
if (params.shake) {
|
|
||||||
const {magnitude = 1} = params.shake;
|
|
||||||
left += (Math.random() * magnitude * 2) - magnitude;
|
|
||||||
top += (Math.random() * magnitude * 2) - magnitude;
|
|
||||||
}
|
|
||||||
if (params.em) {
|
|
||||||
fontStyle = 'italic';
|
|
||||||
}
|
|
||||||
if (params.strong) {
|
|
||||||
fontWeight = 'bold';
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={styles.letter}
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
color,
|
|
||||||
fontStyle,
|
|
||||||
fontWeight,
|
|
||||||
left: `${left}px`,
|
|
||||||
opacity: i <= caret ? opacity : 0,
|
|
||||||
top: `${top}px`,
|
|
||||||
transition: `opacity ${fade}s`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{character}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
border: solid 1px white;
|
border: solid 1px white;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: white;
|
color: white;
|
||||||
|
overflow-wrap: break-word;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -26,5 +27,4 @@
|
||||||
|
|
||||||
.letter {
|
.letter {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,12 @@ import {parseLetters} from '@/dialogue.js';
|
||||||
|
|
||||||
import Entity from './entity.jsx';
|
import Entity from './entity.jsx';
|
||||||
|
|
||||||
export default function Entities({camera, scale, setMonopolizers}) {
|
export default function Entities({
|
||||||
|
camera,
|
||||||
|
scale,
|
||||||
|
setChatMessages,
|
||||||
|
setMonopolizers,
|
||||||
|
}) {
|
||||||
const [ecs] = useEcs();
|
const [ecs] = useEcs();
|
||||||
const [entities, setEntities] = useState({});
|
const [entities, setEntities] = useState({});
|
||||||
usePacket('EcsChange', async () => {
|
usePacket('EcsChange', async () => {
|
||||||
|
@ -34,6 +39,7 @@ export default function Entities({camera, scale, setMonopolizers}) {
|
||||||
for (const key in dialogue) {
|
for (const key in dialogue) {
|
||||||
dialogues[key] = dialogue[key];
|
dialogues[key] = dialogue[key];
|
||||||
dialogues[key].letters = parseLetters(dialogues[key].body);
|
dialogues[key].letters = parseLetters(dialogues[key].body);
|
||||||
|
setChatMessages((chatMessages) => [dialogues[key].letters, ...chatMessages]);
|
||||||
const skipListeners = new Set();
|
const skipListeners = new Set();
|
||||||
dialogues[key].addSkipListener = (listener) => {
|
dialogues[key].addSkipListener = (listener) => {
|
||||||
skipListeners.add(listener);
|
skipListeners.add(listener);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {useEcs, useEcsTick} from '@/context/ecs.js';
|
||||||
import {useMainEntity} from '@/context/main-entity.js';
|
import {useMainEntity} from '@/context/main-entity.js';
|
||||||
|
|
||||||
import Disconnected from './dom/disconnected.jsx';
|
import Disconnected from './dom/disconnected.jsx';
|
||||||
import Chat from './dom/chat.jsx';
|
import Chat from './dom/chat/chat.jsx';
|
||||||
import Dom from './dom/dom.jsx';
|
import Dom from './dom/dom.jsx';
|
||||||
import Entities from './dom/entities.jsx';
|
import Entities from './dom/entities.jsx';
|
||||||
import HotBar from './dom/hotbar.jsx';
|
import HotBar from './dom/hotbar.jsx';
|
||||||
|
@ -69,6 +69,7 @@ export default function Ui({disconnected}) {
|
||||||
const [message, setMessage] = useState('');
|
const [message, setMessage] = useState('');
|
||||||
const [chatIsOpen, setChatIsOpen] = useState(false);
|
const [chatIsOpen, setChatIsOpen] = useState(false);
|
||||||
const [chatHistory, setChatHistory] = useState([]);
|
const [chatHistory, setChatHistory] = useState([]);
|
||||||
|
const [chatMessages, setChatMessages] = useState([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function setEcsStuff() {
|
async function setEcsStuff() {
|
||||||
const {default: Components} = await import('@/ecs-components/index.js');
|
const {default: Components} = await import('@/ecs-components/index.js');
|
||||||
|
@ -97,6 +98,10 @@ export default function Ui({disconnected}) {
|
||||||
}, [disconnected]);
|
}, [disconnected]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return addKeyListener(document.body, ({event, type, payload}) => {
|
return addKeyListener(document.body, ({event, type, payload}) => {
|
||||||
|
if ('Escape' === payload && 'keyDown' === type && chatIsOpen) {
|
||||||
|
setChatIsOpen(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (chatIsOpen) {
|
if (chatIsOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -323,6 +328,10 @@ export default function Ui({disconnected}) {
|
||||||
<div
|
<div
|
||||||
className={[styles.game, devtoolsIsOpen && styles.devtoolsIsOpen].filter(Boolean).join(' ')}
|
className={[styles.game, devtoolsIsOpen && styles.devtoolsIsOpen].filter(Boolean).join(' ')}
|
||||||
onMouseDown={(event) => {
|
onMouseDown={(event) => {
|
||||||
|
if (chatIsOpen) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (event.button) {
|
switch (event.button) {
|
||||||
case 0:
|
case 0:
|
||||||
if (devtoolsIsOpen) {
|
if (devtoolsIsOpen) {
|
||||||
|
@ -372,6 +381,10 @@ export default function Ui({disconnected}) {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onMouseUp={(event) => {
|
onMouseUp={(event) => {
|
||||||
|
if (chatIsOpen) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (event.button) {
|
switch (event.button) {
|
||||||
case 0:
|
case 0:
|
||||||
client.send({
|
client.send({
|
||||||
|
@ -389,6 +402,10 @@ export default function Ui({disconnected}) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}}
|
}}
|
||||||
onWheel={(event) => {
|
onWheel={(event) => {
|
||||||
|
if (chatIsOpen) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (event.deltaY > 0) {
|
if (event.deltaY > 0) {
|
||||||
client.send({
|
client.send({
|
||||||
type: 'Action',
|
type: 'Action',
|
||||||
|
@ -424,11 +441,13 @@ export default function Ui({disconnected}) {
|
||||||
<Entities
|
<Entities
|
||||||
camera={camera}
|
camera={camera}
|
||||||
scale={scale}
|
scale={scale}
|
||||||
|
setChatMessages={setChatMessages}
|
||||||
setMonopolizers={setMonopolizers}
|
setMonopolizers={setMonopolizers}
|
||||||
/>
|
/>
|
||||||
{chatIsOpen && (
|
{chatIsOpen && (
|
||||||
<Chat
|
<Chat
|
||||||
chatHistory={chatHistory}
|
chatHistory={chatHistory}
|
||||||
|
chatMessages={chatMessages}
|
||||||
message={message}
|
message={message}
|
||||||
setChatHistory={setChatHistory}
|
setChatHistory={setChatHistory}
|
||||||
setMessage={setMessage}
|
setMessage={setMessage}
|
||||||
|
|
16
app/root.css
16
app/root.css
|
@ -10,3 +10,19 @@ html, body {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #777 #333;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #777;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 3px solid #333;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user