feat(control): трекер инициативы в отдельном окне

Очередь NPC/игроков с DnD, курсором на сцене, состояниями и запасом; сброс при смене сцены.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-09-04 10:09:51 +08:00
parent db1168a46c
commit 86d6d157f0
28 changed files with 1783 additions and 4 deletions
@@ -0,0 +1,43 @@
import React from 'react';
import type { InitiativeConditionId } from '../../../shared/types/initiative';
import { useEditorI18n } from '../../editor/i18n/EditorI18nContext';
import styles from './InitiativeConditionChips.module.css';
export function InitiativeConditionChips({
conditions,
removable = false,
onRemove,
compact = false,
}: {
conditions: readonly InitiativeConditionId[];
removable?: boolean;
onRemove?: (id: InitiativeConditionId) => void;
compact?: boolean;
}) {
const { t } = useEditorI18n();
if (conditions.length === 0) return null;
return (
<div className={[styles.row, compact ? styles.compact : ''].filter(Boolean).join(' ')}>
{conditions.map((id) => (
<span key={id} className={styles.chip} data-testid={`initiative-condition-${id}`}>
<span className={styles.chipLabel}>{t(`initiative.condition.${id}`)}</span>
{removable ? (
<button
type="button"
className={styles.chipRemove}
aria-label={t('initiative.removeCondition')}
onClick={(e) => {
e.stopPropagation();
onRemove?.(id);
}}
>
×
</button>
) : null}
</span>
))}
</div>
);
}