ttrpg-tools/src/components/dice.tsx

67 lines
1.7 KiB
TypeScript
Raw Normal View History

2026-02-26 00:47:26 +08:00
import { customElement, noShadowDOM } from 'solid-element';
import { createSignal } from 'solid-js';
2026-02-26 00:17:23 +08:00
function rollDice(formula: string): number {
// 解析骰子公式例如2d6+d8
const parts = formula.split('+').map(p => p.trim());
let total = 0;
for (const part of parts) {
const match = part.match(/^(\d+)?d(\d+)$/i);
if (match) {
const count = parseInt(match[1] || '1');
const sides = parseInt(match[2]);
for (let i = 0; i < count; i++) {
total += Math.floor(Math.random() * sides) + 1;
}
} else {
// 处理固定数字
const num = parseInt(part);
if (!isNaN(num)) {
total += num;
}
}
}
return total;
}
2026-02-26 00:51:32 +08:00
customElement('md-dice', { key: '' }, (props, { element }) => {
2026-02-26 00:47:26 +08:00
noShadowDOM();
2026-02-26 00:17:23 +08:00
const [result, setResult] = createSignal<number | null>(null);
const [isRolled, setIsRolled] = createSignal(false);
2026-02-26 00:47:26 +08:00
// 从 element 的 textContent 获取骰子公式
const formula = element?.textContent?.trim() || '';
2026-02-26 00:17:23 +08:00
const handleClick = () => {
if (isRolled()) {
// 重置为公式
setResult(null);
setIsRolled(false);
} else {
// 掷骰子
2026-02-26 00:47:26 +08:00
const rollResult = rollDice(formula);
2026-02-26 00:17:23 +08:00
setResult(rollResult);
setIsRolled(true);
}
};
2026-02-26 00:47:26 +08:00
const displayText = () => (isRolled() ? `${result()}` : formula);
2026-02-26 00:17:23 +08:00
return (
<a
2026-02-26 00:47:26 +08:00
href={props.key && isRolled() ? `?dice-${props.key}=${result()}` : '#'}
2026-02-26 00:17:23 +08:00
onClick={(e) => {
e.preventDefault();
handleClick();
}}
class="inline-flex items-center gap-1 text-blue-600 hover:text-blue-800 cursor-pointer"
>
<span>🎲</span>
<span>{displayText()}</span>
</a>
);
2026-02-26 00:47:26 +08:00
});