feat: handle non-letter characters in actor names with separator rows
Display spaces, hyphens and other non-letter characters as static cells instead of input fields, and add separator rows in the grid for non-alphabetic characters in the main actor's name. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ export default function ActorPopover({ actorName }) {
|
|||||||
open: isOpen,
|
open: isOpen,
|
||||||
onOpenChange: setIsOpen,
|
onOpenChange: setIsOpen,
|
||||||
middleware: [offset(8), flip(), shift()],
|
middleware: [offset(8), flip(), shift()],
|
||||||
placement: 'top',
|
placement: 'left',
|
||||||
});
|
});
|
||||||
|
|
||||||
const click = useClick(context);
|
const click = useClick(context);
|
||||||
|
|||||||
@@ -5,7 +5,25 @@ export default function GameGrid({ grid, width, middle }) {
|
|||||||
return (
|
return (
|
||||||
<table id="actors">
|
<table id="actors">
|
||||||
<tbody>
|
<tbody>
|
||||||
{grid.map((row, rowIndex) => (
|
{grid.map((row, rowIndex) => {
|
||||||
|
if (row.separator !== undefined) {
|
||||||
|
return (
|
||||||
|
<tr key={rowIndex} className="separator-row">
|
||||||
|
<td />
|
||||||
|
{Array.from({ length: middle }, (_, i) => (
|
||||||
|
<td key={i} />
|
||||||
|
))}
|
||||||
|
<td className="letter-static separator-char">
|
||||||
|
{row.separator === ' ' ? '' : row.separator}
|
||||||
|
</td>
|
||||||
|
{Array.from({ length: width - middle }, (_, i) => (
|
||||||
|
<td key={middle + 1 + i} />
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<GameRow
|
<GameRow
|
||||||
key={rowIndex}
|
key={rowIndex}
|
||||||
actorName={row.actorName}
|
actorName={row.actorName}
|
||||||
@@ -13,7 +31,8 @@ export default function GameGrid({ grid, width, middle }) {
|
|||||||
colStart={middle - row.pos}
|
colStart={middle - row.pos}
|
||||||
totalWidth={width}
|
totalWidth={width}
|
||||||
/>
|
/>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,19 +1,31 @@
|
|||||||
import React, { useRef, useCallback } from 'react';
|
import React, { useRef, useCallback, useMemo } from 'react';
|
||||||
import LetterInput from './LetterInput';
|
import LetterInput from './LetterInput';
|
||||||
import ActorPopover from './ActorPopover';
|
import ActorPopover from './ActorPopover';
|
||||||
|
|
||||||
|
function isLetter(ch) {
|
||||||
|
return /[a-zA-Z]/.test(ch);
|
||||||
|
}
|
||||||
|
|
||||||
export default function GameRow({ actorName, pos, colStart, totalWidth }) {
|
export default function GameRow({ actorName, pos, colStart, totalWidth }) {
|
||||||
const inputRefs = useRef([]);
|
const inputRefs = useRef([]);
|
||||||
|
const letters = actorName.split('');
|
||||||
|
|
||||||
|
const letterIndices = useMemo(
|
||||||
|
() => letters.reduce((acc, ch, i) => { if (isLetter(ch)) acc.push(i); return acc; }, []),
|
||||||
|
[actorName]
|
||||||
|
);
|
||||||
|
|
||||||
const setInputRef = useCallback((index) => (el) => {
|
const setInputRef = useCallback((index) => (el) => {
|
||||||
inputRefs.current[index] = el;
|
inputRefs.current[index] = el;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const focusInput = useCallback((index) => {
|
const focusNextInput = useCallback((charIndex, direction) => {
|
||||||
inputRefs.current[index]?.focus();
|
const currentPos = letterIndices.indexOf(charIndex);
|
||||||
}, []);
|
const nextPos = currentPos + direction;
|
||||||
|
if (nextPos >= 0 && nextPos < letterIndices.length) {
|
||||||
const letters = actorName.split('');
|
inputRefs.current[letterIndices[nextPos]]?.focus();
|
||||||
|
}
|
||||||
|
}, [letterIndices]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
@@ -28,13 +40,23 @@ export default function GameRow({ actorName, pos, colStart, totalWidth }) {
|
|||||||
return <td key={colIndex} />;
|
return <td key={colIndex} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ch = letters[charIndex];
|
||||||
|
|
||||||
|
if (!isLetter(ch)) {
|
||||||
|
return (
|
||||||
|
<td key={colIndex} className="letter-static">
|
||||||
|
{ch}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LetterInput
|
<LetterInput
|
||||||
key={colIndex}
|
key={colIndex}
|
||||||
highlighted={charIndex === pos}
|
highlighted={charIndex === pos}
|
||||||
inputRef={setInputRef(charIndex)}
|
inputRef={setInputRef(charIndex)}
|
||||||
onNext={() => focusInput(charIndex + 1)}
|
onNext={() => focusNextInput(charIndex, 1)}
|
||||||
onPrev={() => focusInput(charIndex - 1)}
|
onPrev={() => focusNextInput(charIndex, -1)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -75,6 +75,27 @@ body {
|
|||||||
color: var(--orange);
|
color: var(--orange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.letter-static {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator-row td {
|
||||||
|
height: 12px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator-char {
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Popover ── */
|
/* ── Popover ── */
|
||||||
|
|
||||||
.popover-trigger {
|
.popover-trigger {
|
||||||
|
|||||||
@@ -71,7 +71,25 @@ class GameGridGenerator
|
|||||||
$rightSize = 0;
|
$rightSize = 0;
|
||||||
$grid = [];
|
$grid = [];
|
||||||
|
|
||||||
foreach ($game->getRows() as $row) {
|
$mainActorChars = str_split($game->getMainActor()->getName());
|
||||||
|
$rows = $game->getRows()->toArray();
|
||||||
|
$rowIndex = 0;
|
||||||
|
|
||||||
|
foreach ($mainActorChars as $char) {
|
||||||
|
if (!preg_match('/[a-zA-Z]/', $char)) {
|
||||||
|
$grid[] = [
|
||||||
|
'separator' => $char,
|
||||||
|
];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = $rows[$rowIndex] ?? null;
|
||||||
|
++$rowIndex;
|
||||||
|
|
||||||
|
if ($row === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$actor = $row->getActor();
|
$actor = $row->getActor();
|
||||||
$pos = $row->getPosition();
|
$pos = $row->getPosition();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user