Files
Ivan Fontosh f270812219 feat(tokens): app-local non-player tokens with session moves and UI polish
Add token library/placements, keep play-time moves for the session, lock presentation interactions, and fix export/import modal layout plus freeform trap label.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 11:04:12 +08:00

70 lines
1.8 KiB
TypeScript

import React from 'react';
import type { SceneToken } from '../../shared/types';
import { useTokenImageUrl } from '../shared/tokens/useTokenImageUrl';
import styles from './SceneEditorApp.module.css';
type Props = {
token: SceneToken;
left: number;
top: number;
sizePx: number;
selected: boolean;
onSelect: () => void;
onContextMenu?: (e: React.MouseEvent) => void;
onMovePointerDown: (e: React.PointerEvent) => void;
onResizePointerDown: (e: React.PointerEvent) => void;
onRotatePointerDown: (e: React.PointerEvent) => void;
};
export function SceneTokenMarker({
token,
left,
top,
sizePx,
selected,
onSelect,
onContextMenu,
onMovePointerDown,
onResizePointerDown,
onRotatePointerDown,
}: Props) {
const url = useTokenImageUrl(token.tokenId);
return (
<div
className={[styles.sceneToken, selected ? styles.sceneTokenSelected : ''].filter(Boolean).join(' ')}
style={{
left,
top,
width: sizePx,
height: sizePx,
transform: `translate(-50%, -50%) rotate(${String(token.rotationDeg)}deg)`,
}}
title="Перетащите · ПКМ — удалить"
onContextMenu={onContextMenu}
onPointerDown={(e) => {
if (e.button !== 0) return;
onSelect();
onMovePointerDown(e);
}}
>
{url ? <img className={styles.sceneTokenImg} src={url} alt="" draggable={false} /> : null}
{selected ? (
<>
<div className={styles.handle} onPointerDown={onResizePointerDown} />
<button
type="button"
className={styles.rotateHandle}
aria-label="Повернуть"
title="Повернуть"
onPointerDown={onRotatePointerDown}
>
</button>
</>
) : null}
</div>
);
}