megafonias exteriores
This commit is contained in:
22
index.html
22
index.html
@@ -1,12 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Megafonías TsSAEx</title>
|
||||
<link rel="stylesheet" href="/src/style.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="loading-screen">
|
||||
@@ -28,16 +33,24 @@
|
||||
<div class="vol-header"><md-icon>meeting_room</md-icon><span>Panel Interior</span></div>
|
||||
<md-filled-select id="int-linea" label="Línea"></md-filled-select>
|
||||
<md-filled-select id="int-parada" label="Parada" disabled></md-filled-select>
|
||||
|
||||
<div class="radio-container">
|
||||
<label><md-radio name="tipo" value="actual" checked></md-radio>Parada actual</label>
|
||||
<label><md-radio name="tipo" value="siguiente"></md-radio>Parada siguiente</label>
|
||||
</div>
|
||||
|
||||
<div id="container-regulacion" class="regulacion-box">
|
||||
<md-checkbox id="chk-regulacion"></md-checkbox>
|
||||
<label for="chk-regulacion">Parada de regulación</label>
|
||||
</div>
|
||||
|
||||
<md-filled-button id="btn-int">Anunciar Parada</md-filled-button>
|
||||
</section>
|
||||
|
||||
<section class="m3-card secondary">
|
||||
<div class="vol-header"><md-icon>campaign</md-icon><span>Exterior</span></div>
|
||||
<p style="margin:0; opacity:0.7; font-size:0.9rem;">Configuración de letrero y megafonía externa para paradas.</p>
|
||||
<p style="margin:0; opacity:0.7; font-size:0.9rem;">Configuración de letrero y megafonía externa para paradas.
|
||||
</p>
|
||||
<md-filled-tonal-button id="btn-abrir-exterior">
|
||||
<md-icon slot="icon">open_in_new</md-icon>Configurar Exterior
|
||||
</md-filled-tonal-button>
|
||||
@@ -74,7 +87,8 @@
|
||||
</div>
|
||||
<div id="lista-paradas-editor"></div>
|
||||
<div class="editor-actions">
|
||||
<md-filled-tonal-button id="btn-add-parada-ed"><md-icon slot="icon">add</md-icon>Añadir Parada</md-filled-tonal-button>
|
||||
<md-filled-tonal-button id="btn-add-parada-ed"><md-icon slot="icon">add</md-icon>Añadir
|
||||
Parada</md-filled-tonal-button>
|
||||
<md-filled-button id="btn-generar-json">Exportar JSON</md-filled-button>
|
||||
</div>
|
||||
<pre id="output-json" class="json-preview"></pre>
|
||||
@@ -88,7 +102,8 @@
|
||||
<md-filled-select id="ext-linea" label="Línea"></md-filled-select>
|
||||
<md-filled-select id="ext-destino" label="Destino" disabled></md-filled-select>
|
||||
<md-filled-select id="ext-coche" label="Coche"></md-filled-select>
|
||||
<div class="vol-header" style="margin-top:10px"><md-icon>volume_down</md-icon><span>Volumen Exterior</span></div>
|
||||
<div class="vol-header" style="margin-top:10px"><md-icon>volume_down</md-icon><span>Volumen Exterior</span>
|
||||
</div>
|
||||
<md-slider id="vol-ext" min="0" max="1" step="0.01" value="0.9"></md-slider>
|
||||
</div>
|
||||
</form>
|
||||
@@ -101,4 +116,5 @@
|
||||
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -735,6 +735,7 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
BIN
public/audio/correspondencia_linea.wav
Normal file
BIN
public/audio/correspondencia_linea.wav
Normal file
Binary file not shown.
BIN
public/audio/correspondencia_lineas.wav
Normal file
BIN
public/audio/correspondencia_lineas.wav
Normal file
Binary file not shown.
BIN
public/audio/correspondencia_todas_las_lineas.wav
Normal file
BIN
public/audio/correspondencia_todas_las_lineas.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_315.wav
Normal file
BIN
public/audio/exterior/autobus_315.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_316.wav
Normal file
BIN
public/audio/exterior/autobus_316.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_317.wav
Normal file
BIN
public/audio/exterior/autobus_317.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_318.wav
Normal file
BIN
public/audio/exterior/autobus_318.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_319.wav
Normal file
BIN
public/audio/exterior/autobus_319.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_320.wav
Normal file
BIN
public/audio/exterior/autobus_320.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_321.wav
Normal file
BIN
public/audio/exterior/autobus_321.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_322.wav
Normal file
BIN
public/audio/exterior/autobus_322.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_323.wav
Normal file
BIN
public/audio/exterior/autobus_323.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_324.wav
Normal file
BIN
public/audio/exterior/autobus_324.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_325.wav
Normal file
BIN
public/audio/exterior/autobus_325.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_326.wav
Normal file
BIN
public/audio/exterior/autobus_326.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_327.wav
Normal file
BIN
public/audio/exterior/autobus_327.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_328.wav
Normal file
BIN
public/audio/exterior/autobus_328.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_329.wav
Normal file
BIN
public/audio/exterior/autobus_329.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_330.wav
Normal file
BIN
public/audio/exterior/autobus_330.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_331.wav
Normal file
BIN
public/audio/exterior/autobus_331.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_332.wav
Normal file
BIN
public/audio/exterior/autobus_332.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_333.wav
Normal file
BIN
public/audio/exterior/autobus_333.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_334.wav
Normal file
BIN
public/audio/exterior/autobus_334.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_400.wav
Normal file
BIN
public/audio/exterior/autobus_400.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_401.wav
Normal file
BIN
public/audio/exterior/autobus_401.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_402.wav
Normal file
BIN
public/audio/exterior/autobus_402.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_403.wav
Normal file
BIN
public/audio/exterior/autobus_403.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_404.wav
Normal file
BIN
public/audio/exterior/autobus_404.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_405.wav
Normal file
BIN
public/audio/exterior/autobus_405.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_406.wav
Normal file
BIN
public/audio/exterior/autobus_406.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_407.wav
Normal file
BIN
public/audio/exterior/autobus_407.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_408.wav
Normal file
BIN
public/audio/exterior/autobus_408.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/autobus_409.wav
Normal file
BIN
public/audio/exterior/autobus_409.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/destino.wav
Normal file
BIN
public/audio/exterior/destino.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/destino_hospital_jrj.wav
Normal file
BIN
public/audio/exterior/destino_hospital_jrj.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/destino_zafra.wav
Normal file
BIN
public/audio/exterior/destino_zafra.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/linea_L1.wav
Normal file
BIN
public/audio/exterior/linea_L1.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/linea_L2.wav
Normal file
BIN
public/audio/exterior/linea_L2.wav
Normal file
Binary file not shown.
BIN
public/audio/exterior/zafra.wav
Normal file
BIN
public/audio/exterior/zafra.wav
Normal file
Binary file not shown.
BIN
public/audio/parada_regulacion.wav
Normal file
BIN
public/audio/parada_regulacion.wav
Normal file
Binary file not shown.
@@ -7,6 +7,10 @@
|
||||
"L2": {
|
||||
"nombre": "Línea 2",
|
||||
"destinos": ["Zafra", "Hospital JRJ"]
|
||||
},
|
||||
"L3": {
|
||||
"nombre": "Línea 3",
|
||||
"destinos": ["Zafra", "Orden Baja"]
|
||||
}
|
||||
}
|
||||
}
|
||||
211
src/main.js
211
src/main.js
@@ -1,118 +1,151 @@
|
||||
import '@material/web/all.js';
|
||||
import "@fontsource/roboto";
|
||||
import 'https://cdn.jsdelivr.net/npm/@material/web/all.js/+esm';
|
||||
|
||||
let dbInt = {}, dbExt = {};
|
||||
let audio = null;
|
||||
let dbInt = {};
|
||||
let dbExt = { lineas: {} }; // Base de datos exterior inicializada
|
||||
let audioActual = null;
|
||||
const get = (id) => document.getElementById(id);
|
||||
|
||||
// --- 1. INICIO DEL SISTEMA ---
|
||||
async function init() {
|
||||
try {
|
||||
const [rI, rE] = await Promise.all([
|
||||
fetch('/data/lineas.json'),
|
||||
fetch('/data/exterior.json')
|
||||
const [resInt, resExt] = await Promise.all([
|
||||
fetch('data/lineas.json'),
|
||||
fetch('data/exterior.json')
|
||||
]);
|
||||
dbInt = await rI.json();
|
||||
dbExt = await rE.json();
|
||||
|
||||
// 1. Rellenar Líneas (Interior y Exterior)
|
||||
const lineas = Object.keys(dbInt);
|
||||
const opts = lineas.map(id => `<md-select-option value="${id}"><div slot="headline">${id}</div></md-select-option>`).join('');
|
||||
if (!resInt.ok || !resExt.ok) throw new Error("Error cargando archivos JSON");
|
||||
|
||||
if(get('int-linea')) get('int-linea').innerHTML = opts;
|
||||
if(get('ext-linea')) get('ext-linea').innerHTML = opts;
|
||||
dbInt = await resInt.json();
|
||||
dbExt = await resExt.json();
|
||||
|
||||
// 2. Generar Coches
|
||||
let coches = [];
|
||||
for (let i = 315; i <= 334; i++) coches.push(i);
|
||||
for (let i = 400; i <= 409; i++) coches.push(i);
|
||||
if(get('ext-coche')) {
|
||||
get('ext-coche').innerHTML = coches.map(c => `<md-select-option value="${c}"><div slot="headline">${c}</div></md-select-option>`).join('');
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error cargando TsSAEx:", e);
|
||||
}
|
||||
|
||||
// Quitar Splash
|
||||
setTimeout(() => {
|
||||
const splash = get('loading-screen');
|
||||
if(splash) splash.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
if(splash) splash.style.display = 'none';
|
||||
const app = get('app');
|
||||
if(app) app.style.display = 'flex';
|
||||
}, 500);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// REPRODUCTOR
|
||||
function play(lista, volumen) {
|
||||
if (audio) { audio.pause(); audio.currentTime = 0; }
|
||||
if (!lista || lista.length === 0) return;
|
||||
const file = lista.shift();
|
||||
audio = new Audio(`${file}?t=${Date.now()}`);
|
||||
audio.volume = volumen;
|
||||
audio.play().catch(() => play(lista, volumen));
|
||||
audio.onended = () => play(lista, volumen);
|
||||
}
|
||||
|
||||
// --- EVENTO CLAVE: CAMBIO DE LÍNEA INTERIOR ---
|
||||
get('int-linea')?.addEventListener('change', (e) => {
|
||||
const lineaSeleccionada = e.target.value;
|
||||
const paradas = dbInt[lineaSeleccionada] || [];
|
||||
const selectorParadas = get('int-parada');
|
||||
|
||||
if (selectorParadas) {
|
||||
// Insertar nuevas paradas
|
||||
selectorParadas.innerHTML = paradas.map(p =>
|
||||
`<md-select-option value="${p.id}"><div slot="headline">${p.nombre}</div></md-select-option>`
|
||||
const lineasHTML = Object.keys(dbInt).map(id =>
|
||||
`<md-select-option value="${id}"><div slot="headline">${id}</div></md-select-option>`
|
||||
).join('');
|
||||
|
||||
// IMPORTANTE: Resetear valor y habilitar
|
||||
selectorParadas.value = "";
|
||||
selectorParadas.disabled = false;
|
||||
if (get('int-linea')) get('int-linea').innerHTML = lineasHTML;
|
||||
if (get('ext-linea')) get('ext-linea').innerHTML = lineasHTML;
|
||||
|
||||
// Generar números de coche de Huelva (315-334 y 400-409)
|
||||
const coches = [...Array(20).keys()].map(i => i + 315).concat([...Array(10).keys()].map(i => i + 400));
|
||||
if (get('ext-coche')) {
|
||||
get('ext-coche').innerHTML = coches.map(c =>
|
||||
`<md-select-option value="${c}"><div slot="headline">${c}</div></md-select-option>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error("Error cargando DB:", err);
|
||||
} finally {
|
||||
const splash = get('loading-screen');
|
||||
if (splash) {
|
||||
splash.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
splash.style.display = 'none';
|
||||
get('app').style.display = 'flex';
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. MOTOR DE AUDIO ---
|
||||
function play(cola, volId = 'vol') {
|
||||
if (audioActual) { audioActual.pause(); audioActual.currentTime = 0; }
|
||||
if (!cola || cola.length === 0) return;
|
||||
|
||||
const item = cola.shift();
|
||||
audioActual = new Audio(`${item.file}?cb=${Date.now()}`);
|
||||
|
||||
// Usar el slider correspondiente (interior o exterior)
|
||||
const slider = get(volId);
|
||||
audioActual.volume = slider ? slider.value : 0.8;
|
||||
|
||||
audioActual.play().catch(e => console.warn("Audio no encontrado:", item.file));
|
||||
audioActual.onended = () => setTimeout(() => play(cola, volId), item.gap || 400);
|
||||
}
|
||||
|
||||
// --- 3. EVENTOS PANEL INTERIOR ---
|
||||
get('int-linea')?.addEventListener('change', (e) => {
|
||||
const linea = dbInt[e.target.value];
|
||||
const selParada = get('int-parada');
|
||||
if (linea && selParada) {
|
||||
selParada.innerHTML = linea.paradas.map(p =>
|
||||
`<md-select-option value="${p.id}"><div slot="headline">${p.nombre}</div></md-select-option>`
|
||||
).join('');
|
||||
selParada.disabled = false;
|
||||
selParada.value = "";
|
||||
}
|
||||
get('container-regulacion').style.display = 'none';
|
||||
});
|
||||
|
||||
get('int-parada')?.addEventListener('change', (e) => {
|
||||
const paradasReg = ['zafra', 'orden_baja', 'hospital_jrj'];
|
||||
const esReg = paradasReg.includes(e.target.value);
|
||||
get('container-regulacion').style.display = esReg ? 'flex' : 'none';
|
||||
if (!esReg) get('chk-regulacion').checked = false;
|
||||
});
|
||||
|
||||
// BOTÓN ANUNCIAR
|
||||
get('btn-int')?.addEventListener('click', () => {
|
||||
const lineaId = get('int-linea').value;
|
||||
const paradaId = get('int-parada').value;
|
||||
const tipo = document.querySelector('md-radio[name="tipo"][checked]')?.value || "actual";
|
||||
const lId = get('int-linea').value, pId = get('int-parada').value;
|
||||
if (!lId || !pId) return;
|
||||
|
||||
if(!lineaId || !paradaId) return alert("Selecciona línea y parada");
|
||||
const paradaData = dbInt[lId].paradas.find(p => p.id === pId);
|
||||
const tipo = document.querySelector('md-radio[value="siguiente"]')?.checked ? "siguiente" : "actual";
|
||||
|
||||
const p = dbInt[lineaId]?.find(x => x.id === paradaId);
|
||||
if (!p) return;
|
||||
let cola = [
|
||||
{ file: `audio/parada_${tipo}.wav`, gap: 400 },
|
||||
{ file: `audio/${pId}.wav`, gap: 600 }
|
||||
];
|
||||
|
||||
let cola = [`/audio/parada_${tipo}.wav`, `/audio/${p.id}.wav` ];
|
||||
if (p.enlaces?.length > 0) {
|
||||
cola.push(`/audio/correspondencia.wav`);
|
||||
p.enlaces.includes("todas") ? cola.push(`/audio/todas_las_lineas.wav`) : p.enlaces.forEach(e => cola.push(`/audio/linea_${e}.wav`));
|
||||
if (paradaData.enlaces?.length > 0) {
|
||||
const enlaces = paradaData.enlaces.filter(en => en.trim() !== "");
|
||||
if (enlaces.includes("todas")) {
|
||||
cola.push({ file: `audio/correspondencia_todas_las_lineas.wav`, gap: 400 });
|
||||
} else if (enlaces.length > 0) {
|
||||
cola.push({ file: `audio/${enlaces.length === 1 ? 'correspondencia_linea' : 'correspondencia_lineas'}.wav`, gap: 300 });
|
||||
enlaces.forEach(en => cola.push({ file: `audio/linea_${en}.wav`, gap: 300 }));
|
||||
}
|
||||
}
|
||||
play(cola, get('vol').value);
|
||||
|
||||
if (get('chk-regulacion')?.checked) cola.push({ file: `audio/parada_regulacion.wav`, gap: 400 });
|
||||
play(cola, 'vol');
|
||||
});
|
||||
|
||||
// LOGICA EXTERIOR (POPUP)
|
||||
// --- 4. LÓGICA EXTERIOR (RESTAURADA) ---
|
||||
get('btn-abrir-exterior').onclick = () => get('dialog-exterior').show();
|
||||
|
||||
get('ext-linea')?.addEventListener('change', (e) => {
|
||||
const d = dbExt.lineas[e.target.value]?.destinos || [];
|
||||
get('ext-destino').innerHTML = d.map(x => `<md-select-option value="${x}"><div slot="headline">${x}</div></md-select-option>`).join('');
|
||||
get('ext-destino').disabled = false;
|
||||
const lineaId = e.target.value;
|
||||
const destinos = dbExt.lineas[lineaId]?.destinos || [];
|
||||
const selDest = get('ext-destino');
|
||||
if (selDest) {
|
||||
selDest.innerHTML = destinos.map(d => `<md-select-option value="${d}"><div slot="headline">${d}</div></md-select-option>`).join('');
|
||||
selDest.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
get('btn-reproducir-ext').onclick = () => {
|
||||
const c = get('ext-coche').value, l = get('ext-linea').value, d = get('ext-destino').value;
|
||||
if (!c || !l || !d) return;
|
||||
const destFile = d.toLowerCase().replace(/ /g, "_");
|
||||
play(['/audio/linea.wav', `/audio/${l}.wav`, '/audio/autobus.wav', `/audio/${c}.wav`, '/audio/destino.wav', `/audio/${destFile}.wav`], get('vol-ext').value);
|
||||
};
|
||||
get('btn-reproducir-ext')?.addEventListener('click', () => {
|
||||
const linea = get('ext-linea').value;
|
||||
const destino = get('ext-destino').value;
|
||||
const coche = get('ext-coche').value;
|
||||
|
||||
// BOTONES ESPECIALES
|
||||
get('btn-colision').onclick = () => play(['/audio/colision.wav'], get('vol').value);
|
||||
get('btn-hora').onclick = () => play(['/audio/hora_salida.wav'], get('vol').value);
|
||||
get('btn-saldo').onclick = () => play(['/audio/atencion_saldo.wav'], get('vol').value);
|
||||
get('btn-insuficiente').onclick = () => play(['/audio/saldo_insuficiente.wav'], get('vol').value);
|
||||
if (!linea || !destino || !coche) return;
|
||||
|
||||
const destFile = destino.toLowerCase().replace(/ /g, "_");
|
||||
|
||||
// Cola exterior: Línea X -> Autobús XXX -> Destino YYY
|
||||
const colaExt = [
|
||||
{ file: `audio/exterior/linea_${linea}.wav`, gap: 300 },
|
||||
{ file: `audio/exterior/autobus_${coche}.wav`, gap: 500 },
|
||||
{ file: `audio/exterior/destino_${destFile}.wav`, gap: 300 }
|
||||
];
|
||||
|
||||
play(colaExt, 'vol-ext');
|
||||
});
|
||||
|
||||
// --- 5. ESPECIALES ---
|
||||
get('btn-colision').onclick = () => play([{ file: `audio/colision.wav` }]);
|
||||
get('btn-hora').onclick = () => play([{ file: `audio/hora_salida.wav` }]);
|
||||
get('btn-saldo').onclick = () => play([{ file: `audio/atencion_saldo.wav` }]);
|
||||
get('btn-insuficiente').onclick = () => play([{ file: `audio/saldo_insuficiente.wav` }]);
|
||||
|
||||
init();
|
||||
@@ -110,6 +110,22 @@ md-icon { font-family: 'Material Symbols Outlined' !important; }
|
||||
}
|
||||
.radio-container label { display: flex; align-items: center; gap: 12px; cursor: pointer; }
|
||||
|
||||
/* --- ESTILOS REGULACIÓN --- */
|
||||
#container-regulacion {
|
||||
/* display: none; El JS lo cambia a flex */
|
||||
background: rgba(109, 94, 0, 0.08); /* Uso del color primary con baja opacidad */
|
||||
padding: 12px 16px;
|
||||
border-radius: 20px;
|
||||
border: 1px dashed var(--md-sys-color-primary);
|
||||
animation: slideIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* --- BOTONES Y ESPECIALES --- */
|
||||
.button-grid-specials { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.danger-btn { --md-filled-tonal-button-container-color: var(--md-sys-color-error-container); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user