102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { customElement, noShadowDOM } from "solid-element";
|
||
import { createSignal, onMount } from "solid-js";
|
||
import {rollFormula} from "./md-commander/hooks";
|
||
|
||
export interface DiceProps {
|
||
key?: string;
|
||
}
|
||
|
||
// 从 URL 参数获取骰子结果
|
||
function getDiceResultFromUrl(key: string): number | null {
|
||
if (typeof window === "undefined") return null;
|
||
const params = new URLSearchParams(window.location.search);
|
||
const value = params.get(`dice-${key}`);
|
||
return value ? parseInt(value) : null;
|
||
}
|
||
|
||
function setDiceResultToUrl(key: string, value: number | null) {
|
||
if (typeof window === "undefined") return;
|
||
const params = new URLSearchParams(window.location.search);
|
||
if (value === null) {
|
||
params.delete(`dice-${key}`);
|
||
} else {
|
||
params.set(`dice-${key}`, value.toString());
|
||
}
|
||
const newUrl = `${window.location.pathname}${params.toString() ? `?${params.toString()}` : ""}${window.location.hash}`;
|
||
window.history.pushState({}, "", newUrl);
|
||
}
|
||
|
||
customElement("md-dice", { key: "" }, (props, { element }) => {
|
||
noShadowDOM();
|
||
const [result, setResult] = createSignal<number | null>(null);
|
||
const [isRolled, setIsRolled] = createSignal(false);
|
||
const [rollDetail, setRollDetail] = createSignal<string>("");
|
||
|
||
// 从 element 的 textContent 获取骰子公式
|
||
const formula = element?.textContent?.trim() || "";
|
||
|
||
// 隐藏原始文本内容
|
||
if (element) {
|
||
element.textContent = "";
|
||
}
|
||
|
||
// 使用的 key(如果没有提供则使用生成的 ID)
|
||
const effectiveKey = () => props.key;
|
||
|
||
onMount(() => {
|
||
// 初始化时检查 URL 参数
|
||
if (effectiveKey()) {
|
||
const urlResult = getDiceResultFromUrl(effectiveKey());
|
||
if (urlResult !== null) {
|
||
setResult(urlResult);
|
||
setIsRolled(true);
|
||
}
|
||
}
|
||
});
|
||
|
||
const handleRoll = () => {
|
||
// 点击骰子图标:总是重 roll
|
||
const rollResult = rollFormula(formula).result;
|
||
setResult(rollResult.total);
|
||
setRollDetail(rollResult.detail);
|
||
setIsRolled(true);
|
||
if (effectiveKey()) {
|
||
setDiceResultToUrl(effectiveKey(), rollResult.total);
|
||
}
|
||
};
|
||
|
||
const handleTextClick = () => {
|
||
// 点击文本:总是重置为公式
|
||
setResult(null);
|
||
setIsRolled(false);
|
||
setRollDetail("");
|
||
if (effectiveKey()) {
|
||
setDiceResultToUrl(effectiveKey(), null);
|
||
}
|
||
};
|
||
|
||
const displayText = () => (isRolled() ? `${result()}` : formula);
|
||
|
||
return (
|
||
<span class="inline-flex items-center px-1">
|
||
<span
|
||
onClick={(e) => {
|
||
e.preventDefault();
|
||
handleRoll();
|
||
}}
|
||
class="text-blue-600 hover:text-blue-800 cursor-pointer"
|
||
title={rollDetail() || "掷骰子"}
|
||
>
|
||
🎲
|
||
</span>
|
||
<a
|
||
onClick={handleTextClick}
|
||
class="cursor-pointer text-gray-700 hover:text-gray-900"
|
||
title={isRolled() ? "点击重置为公式" : formula}
|
||
>
|
||
{displayText()}
|
||
</a>
|
||
</span>
|
||
);
|
||
});
|