This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
<script setup>
|
||||
const props = defineProps(['content']);
|
||||
|
||||
import { parse } from '~/services/widgets/DiceParser';
|
||||
import { AddSound } from '~/services/Sound';
|
||||
|
||||
const container = ref(null);
|
||||
const resultText = ref("");
|
||||
const steps = ref(null);
|
||||
const stepsHtml = ref("");
|
||||
|
||||
const rollDice = () => {
|
||||
const result = parse(props.content);
|
||||
resultText.value = result.total;
|
||||
stepsHtml.value = result.steps.map(renderStep).join('');
|
||||
};
|
||||
|
||||
const renderStep = (s) => {
|
||||
if (s.type === 'op') {
|
||||
const label = s.op === '*' ? '×' : s.op === '/' ? '÷' : s.op;
|
||||
return `<span class="step-op">${label}</span>`;
|
||||
}
|
||||
|
||||
if (s.type === 'const') {
|
||||
return `<span class="step-const">${s.value}</span>`;
|
||||
}
|
||||
|
||||
if (s.type === 'dice') {
|
||||
const { entry } = s;
|
||||
const { rawRolls, kept, sides, mod, value } = entry;
|
||||
|
||||
const keptCopy = [...kept];
|
||||
const tagged = rawRolls.map(v => {
|
||||
const i = keptCopy.indexOf(v);
|
||||
if (i !== -1) { keptCopy.splice(i, 1); return { v, kept: true }; }
|
||||
return { v, kept: false };
|
||||
});
|
||||
|
||||
let html = '';
|
||||
|
||||
tagged.forEach(({ v, kept }) => {
|
||||
const isMax = v === sides, isMin = v === 1;
|
||||
const cls = !kept ? 'roll-val dropped'
|
||||
: isMax ? 'roll-val max-val'
|
||||
: isMin ? 'roll-val min-val'
|
||||
: 'roll-val kept';
|
||||
html += `<span class="${cls}">${v}</span>`;
|
||||
});
|
||||
|
||||
if (kept.length < rawRolls.length || kept.length > 1) {
|
||||
html += `<span class="step-sum">=${value}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
AddSound(container.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="roll-widget" ref="container">
|
||||
<div class="roll-widget-body">
|
||||
<span class="result-text">{{ resultText || '-' }}</span>
|
||||
<button class="btn-primary roll-btn sound-click" @click="rollDice">
|
||||
<span class="dice-content">
|
||||
<!-- Dice icon (SVG) -->
|
||||
<img class="icon" src="/icons/iconoir/regular/dice-three.svg" draggable="false">
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="roll-widget-results">
|
||||
<span class="formula">{{ "[" + props.content + "]" }}</span>
|
||||
<div class="steps" v-html="stepsHtml"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.steps {
|
||||
margin-left: 8px;
|
||||
height: 22px;
|
||||
> {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.steps :deep(.roll-val){
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 18px;
|
||||
padding: 1px 2px;
|
||||
border-radius: 3px;
|
||||
margin: 2px;
|
||||
font-size: 12px;
|
||||
border: 1px solid;
|
||||
}
|
||||
.steps :deep(.roll-val.kept){
|
||||
background: rgba(93,184,122,0.12);
|
||||
border-color: #4a8c5c;
|
||||
color: #a8d4b4;
|
||||
}
|
||||
|
||||
.steps :deep(.roll-val.dropped) {
|
||||
background: transparent;
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-text-tertiary);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.steps :deep(.roll-val.max-val) {
|
||||
background: rgba(93,184,122,0.2);
|
||||
border-color: #5db87a;
|
||||
color: #5db87a;
|
||||
}
|
||||
|
||||
.steps :deep(.roll-val.min-val) {
|
||||
background: rgba(201,95,95,0.12);
|
||||
border-color: #c95f5f;
|
||||
color: #c95f5f;
|
||||
}
|
||||
|
||||
.steps :deep(.step-op) { color: var(--color-text-secondary); padding: 0 4px; font-size: 13px; }
|
||||
.steps :deep(.step-const) { font-size: 13px; color: var(--color-text-primary); padding: 0 2px; }
|
||||
.steps :deep(.step-dice-label) { font-size: 11px; color: var(--color-text-tertiary); margin-right: 2px; }
|
||||
.steps :deep(.step-sum) { font-size: 11px; color: var(--color-text-tertiary); margin-left: 2px; }
|
||||
.steps :deep(.dice-mod) { font-style: normal; margin-left: 2px; }
|
||||
|
||||
.result-text {
|
||||
font-size: 24px;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
.roll-widget-body {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.formula {
|
||||
margin-left: 12px;
|
||||
font-family: ui-monospace, monospace;
|
||||
font-size: 12px;
|
||||
color: var(--color-gray);
|
||||
}
|
||||
|
||||
.roll-widget-results {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.roll-widget {
|
||||
width: 100%;
|
||||
background-color: var(--color-background-light);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.roll-btn {
|
||||
padding: 8px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user