This commit is contained in:
Aotumuri
2025-04-02 21:15:04 +09:00
parent 06b9a30111
commit a121f47b2d
2 changed files with 192 additions and 97 deletions
+135 -63
View File
@@ -5,11 +5,25 @@ const quickChatPhrases: Record<
string, string,
Array<{ text: string; requiresPlayer: boolean }> Array<{ text: string; requiresPlayer: boolean }>
> = { > = {
help: [{ text: "Please give me troops!", requiresPlayer: false }], help: [
attack: [{ text: "Attack [P1]!", requiresPlayer: true }], { text: "Please give me troops!", requiresPlayer: false },
{ text: "Please give me golds!", requiresPlayer: false },
{ text: "Please don't attack me!", requiresPlayer: false },
],
attack: [
{ text: "Attack [P1]!", requiresPlayer: true },
{ text: "Launch a MIRV at [P1]!", requiresPlayer: true },
{ text: "Focus fire on [P1]!", requiresPlayer: true },
{ text: "Let's finish off [P1]!", requiresPlayer: true },
],
defend: [{ text: "Defend [P1]!", requiresPlayer: true }], defend: [{ text: "Defend [P1]!", requiresPlayer: true }],
greet: [{ text: "Hello!", requiresPlayer: false }], greet: [{ text: "Hello!", requiresPlayer: false }],
misc: [{ text: "Lets go!", requiresPlayer: false }], misc: [
{ text: "Lets go!", requiresPlayer: false },
{ text: "Nice strategy!", requiresPlayer: false },
{ text: "This game is fun!", requiresPlayer: false },
{ text: "When will my PR finally get merged...?", requiresPlayer: false },
],
}; };
@customElement("chat-modal") @customElement("chat-modal")
@@ -23,10 +37,30 @@ export class ChatModal extends LitElement {
return this; return this;
} }
private players: string[] = [
"Slovakia",
"Germany",
"Japan",
"Anon",
"Anon1",
"Anon2",
"Anon3",
"Anon4",
"Anon5",
"Anon6",
"Anon7",
"Anon8",
"Anon9",
"Anon10",
];
private playerSearchQuery: string = "";
private previewText: string | null = null; private previewText: string | null = null;
private requiresPlayerSelection: boolean = false; private requiresPlayerSelection: boolean = false;
private players: string[] = ["Slovakia", "Germany", "Japan"];
private selectedCategory: string | null = null; private selectedCategory: string | null = null;
private selectedPhraseText: string | null = null;
private selectedPlayer: string | null = null;
private selectedPhraseTemplate: string | null = null;
quickChatPhrases: Record< quickChatPhrases: Record<
string, string,
@@ -52,65 +86,78 @@ export class ChatModal extends LitElement {
} }
render() { render() {
const sortedPlayers = [...this.players].sort((a, b) => a.localeCompare(b));
const filteredPlayers = sortedPlayers.filter((player) =>
player.toLowerCase().includes(this.playerSearchQuery),
);
const otherPlayers = sortedPlayers.filter(
(player) => !player.toLowerCase().includes(this.playerSearchQuery),
);
const displayPlayers = [...filteredPlayers, ...otherPlayers];
return html` return html`
<o-modal title="Quick Chat"> <o-modal title="Quick Chat">
<div class="chat-modal-content"> <div class="chat-columns">
<!-- 入力中のチャットプレビュー --> <div class="chat-column">
<div class="chat-preview"> <div class="column-title">Category</div>
<span>${this.previewText || "Select a phrase"}</span> ${this.categories.map(
(category) => html`
<button
class="chat-option-button ${this.selectedCategory ===
category.id
? "selected"
: ""}"
@click=${() => this.selectCategory(category.id)}
>
${category.name}
</button>
`,
)}
</div> </div>
<!-- カテゴリ選択 -->
<div class="chat-section">
<div class="chat-section-title">Categories</div>
<div class="chat-options">
${this.categories.map(
(category) => html`
<button
class="chat-option-button ${this.selectedCategory ===
category.id
? "selected"
: ""}"
@click=${() => this.selectCategory(category.id)}
>
${category.name}
</button>
`,
)}
</div>
</div>
<!-- 定型文選択 -->
${this.selectedCategory ${this.selectedCategory
? html` ? html`
<div class="chat-section"> <div class="chat-column">
<div class="chat-section-title">Phrases</div> <div class="column-title">Phrase</div>
<div class="chat-options"> ${this.getPhrasesForCategory(this.selectedCategory).map(
${this.getPhrasesForCategory(this.selectedCategory).map( (phrase) => html`
(phrase) => html` <button
<button class="chat-option-button ${this.selectedPhraseText ===
class="chat-option-button" phrase.text
@click=${() => this.selectPhrase(phrase)} ? "selected"
> : ""}"
${this.renderPhrasePreview(phrase)} @click=${() => this.selectPhrase(phrase)}
</button> >
`, ${this.renderPhrasePreview(phrase)}
)} </button>
</div> `,
)}
</div> </div>
` `
: null} : null}
${this.requiresPlayerSelection || this.selectedPlayer
<!-- プレイヤー選択(変数が必要な場合のみ表示) -->
${this.requiresPlayerSelection
? html` ? html`
<div class="chat-section"> <div class="chat-column">
<div class="chat-section-title">Select Player</div> <div class="column-title">Player</div>
<div class="chat-options">
${this.players.map( <input
class="player-search-input"
type="text"
placeholder="Search player..."
.value=${this.playerSearchQuery}
@input=${this.onPlayerSearchInput}
/>
<div class="player-scroll-area">
${this.getSortedFilteredPlayers().map(
(player) => html` (player) => html`
<button <button
class="chat-option-button" class="chat-option-button ${this.selectedPlayer ===
player
? "selected"
: ""}"
@click=${() => this.selectPlayer(player)} @click=${() => this.selectPlayer(player)}
> >
${player} ${player}
@@ -121,17 +168,19 @@ export class ChatModal extends LitElement {
</div> </div>
` `
: null} : null}
</div>
<!-- 送信ボタン --> <div class="chat-preview">
<div class="chat-send"> ${this.previewText || "Build your message..."}
<button </div>
class="chat-send-button" <div class="chat-send">
@click=${this.sendChatMessage} <button
?disabled=${!this.previewText} class="chat-send-button"
> @click=${this.sendChatMessage}
Send ?disabled=${!this.previewText}
</button> >
</div> Send
</button>
</div> </div>
</o-modal> </o-modal>
`; `;
@@ -139,14 +188,19 @@ export class ChatModal extends LitElement {
private selectCategory(categoryId: string) { private selectCategory(categoryId: string) {
this.selectedCategory = categoryId; this.selectedCategory = categoryId;
this.selectedPhraseText = null;
this.previewText = null; this.previewText = null;
this.requiresPlayerSelection = false; this.requiresPlayerSelection = false;
this.selectedPlayer = null;
this.requestUpdate(); this.requestUpdate();
} }
private selectPhrase(phrase: { text: string; requiresPlayer: boolean }) { private selectPhrase(phrase: { text: string; requiresPlayer: boolean }) {
this.selectedPhraseTemplate = phrase.text;
this.selectedPhraseText = phrase.text;
this.previewText = phrase.text; this.previewText = phrase.text;
this.requiresPlayerSelection = phrase.requiresPlayer; this.requiresPlayerSelection = phrase.requiresPlayer;
this.selectedPlayer = null;
this.requestUpdate(); this.requestUpdate();
} }
@@ -156,7 +210,8 @@ export class ChatModal extends LitElement {
private selectPlayer(player: string) { private selectPlayer(player: string) {
if (this.previewText) { if (this.previewText) {
this.previewText = this.previewText.replace("[P1]", player); this.previewText = this.selectedPhraseTemplate.replace("[P1]", player);
this.selectedPlayer = player;
this.requiresPlayerSelection = false; this.requiresPlayerSelection = false;
this.requestUpdate(); this.requestUpdate();
} }
@@ -167,7 +222,24 @@ export class ChatModal extends LitElement {
this.previewText = null; this.previewText = null;
this.selectedCategory = null; this.selectedCategory = null;
this.requiresPlayerSelection = false; this.requiresPlayerSelection = false;
this.close(); // モーダルを閉じる this.close();
}
private onPlayerSearchInput(e: Event) {
const target = e.target as HTMLInputElement;
this.playerSearchQuery = target.value.toLowerCase();
this.requestUpdate();
}
private getSortedFilteredPlayers(): string[] {
const sorted = [...this.players].sort((a, b) => a.localeCompare(b));
const filtered = sorted.filter((p) =>
p.toLowerCase().includes(this.playerSearchQuery),
);
const others = sorted.filter(
(p) => !p.toLowerCase().includes(this.playerSearchQuery),
);
return [...filtered, ...others];
} }
public open() { public open() {
+57 -34
View File
@@ -492,65 +492,88 @@ label.option-card:hover {
border: 1px solid rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.2);
} }
.chat-modal-content { /* .w. */
.chat-columns {
display: flex; display: flex;
flex-direction: column; gap: 16px;
gap: 12px; padding: 12px;
overflow-x: auto;
} }
.chat-preview { .chat-column {
background: #222; display: flex;
padding: 10px; flex-direction: column;
border-radius: 6px; gap: 8px;
min-width: 120px;
}
.column-title {
font-weight: bold; font-weight: bold;
text-align: center; margin-bottom: 4px;
}
.chat-section {
display: flex;
flex-direction: column;
}
.chat-section-title {
font-size: 14px;
margin-bottom: 6px;
}
.chat-options {
display: flex;
flex-wrap: wrap;
gap: 6px;
} }
.chat-option-button { .chat-option-button {
background: #444; background: #333;
color: white;
border: none; border: none;
padding: 8px 12px;
border-radius: 4px; border-radius: 4px;
padding: 6px 10px; text-align: left;
cursor: pointer; cursor: pointer;
} }
.chat-option-button.selected { .chat-option-button.selected {
background: #66c; background-color: #66c;
}
.chat-preview {
margin: 10px 12px;
padding: 10px;
background: #222;
color: white; color: white;
border-radius: 6px;
text-align: center;
} }
.chat-send { .chat-send {
display: flex; display: flex;
justify-content: space-between; justify-content: flex-end;
margin-top: 10px; padding: 0 12px 12px;
} }
.chat-send-button { .chat-send-button {
background: #4caf50; background: #4caf50;
color: white; color: white;
padding: 8px 14px; padding: 8px 16px;
border: none;
border-radius: 4px; border-radius: 4px;
cursor: pointer;
} }
.chat-cancel-button { .chat-column {
background: #aaa; display: flex;
color: black; flex-direction: column;
padding: 8px 14px; gap: 8px;
min-width: 140px;
}
.player-search-input {
padding: 6px 8px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #666;
font-size: 14px;
outline: none;
background-color: #fff;
color: #000;
}
.player-scroll-area {
max-height: 200px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 6px;
padding-right: 4px;
} }