mirror of
https://github.com/cfultz/mtgcsv.git
synced 2024-11-25 02:20:03 +01:00
Initial
This commit is contained in:
commit
ba4a7638c0
149
app.js
Normal file
149
app.js
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
let cardList = [];
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const toggle = document.getElementById('dark-mode-toggle');
|
||||||
|
|
||||||
|
toggle.addEventListener('change', () => {
|
||||||
|
if (toggle.checked) {
|
||||||
|
document.documentElement.classList.add('dark-mode');
|
||||||
|
localStorage.setItem('theme', 'dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark-mode');
|
||||||
|
localStorage.setItem('theme', 'light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load the saved theme preference
|
||||||
|
const savedTheme = localStorage.getItem('theme');
|
||||||
|
if (savedTheme === 'dark') {
|
||||||
|
document.documentElement.classList.add('dark-mode');
|
||||||
|
toggle.checked = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchCardSuggestions(query) {
|
||||||
|
const response = await fetch(`https://api.scryfall.com/cards/autocomplete?q=${encodeURIComponent(query)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
return data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('card-name').addEventListener('input', async function() {
|
||||||
|
const query = this.value;
|
||||||
|
const suggestions = await fetchCardSuggestions(query);
|
||||||
|
const suggestionsDiv = document.getElementById('suggestions');
|
||||||
|
suggestionsDiv.innerHTML = '';
|
||||||
|
suggestions.forEach(suggestion => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = suggestion;
|
||||||
|
div.onclick = () => {
|
||||||
|
document.getElementById('card-name').value = suggestion;
|
||||||
|
suggestionsDiv.innerHTML = '';
|
||||||
|
};
|
||||||
|
suggestionsDiv.appendChild(div);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchCardDetails(cardName, setCode) {
|
||||||
|
const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
return {
|
||||||
|
mana_cost: data.mana_cost || 'N/A',
|
||||||
|
power: data.power || 'N/A',
|
||||||
|
toughness: data.toughness || 'N/A',
|
||||||
|
type: data.type_line.split('—')[0].trim(), // Remove anything after em dash
|
||||||
|
rarity: data.rarity || 'N/A'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addCard() {
|
||||||
|
const cardName = document.getElementById('card-name').value;
|
||||||
|
const setCode = document.getElementById('set-code').value;
|
||||||
|
const cardId = document.getElementById('card-id').value;
|
||||||
|
const quantity = document.getElementById('quantity').value;
|
||||||
|
const foil = document.getElementById('foil').value;
|
||||||
|
|
||||||
|
const cardDetails = await fetchCardDetails(cardName, setCode);
|
||||||
|
|
||||||
|
const cardData = {
|
||||||
|
name: cardName,
|
||||||
|
set: setCode,
|
||||||
|
card_id: cardId,
|
||||||
|
quantity: quantity,
|
||||||
|
foil: foil,
|
||||||
|
mana_type: cardDetails.mana_cost,
|
||||||
|
power: cardDetails.power,
|
||||||
|
toughness: cardDetails.toughness,
|
||||||
|
type: cardDetails.type,
|
||||||
|
rarity: cardDetails.rarity
|
||||||
|
};
|
||||||
|
|
||||||
|
cardList.push(cardData);
|
||||||
|
|
||||||
|
// Update the textarea with the new card information
|
||||||
|
let csvContent = generateCSVContent();
|
||||||
|
document.getElementById('csv-output').value = csvContent;
|
||||||
|
|
||||||
|
// Show the card image in the dissolving popup
|
||||||
|
showCardPopup(cardName, setCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateCSVContent() {
|
||||||
|
let csvContent = "Card Name,Set,Card ID,Quantity,Foil,Mana Type,Power,Toughness,Type,Rarity\n";
|
||||||
|
cardList.forEach(card => {
|
||||||
|
const escapedValues = [
|
||||||
|
card.name,
|
||||||
|
card.set,
|
||||||
|
card.card_id,
|
||||||
|
card.quantity,
|
||||||
|
card.foil,
|
||||||
|
card.mana_type,
|
||||||
|
card.power,
|
||||||
|
card.toughness,
|
||||||
|
card.type,
|
||||||
|
card.rarity
|
||||||
|
].map(value => `"${value.replace(/"/g, '""')}"`); // Escape any existing quotes by doubling them
|
||||||
|
csvContent += escapedValues.join(",") + "\n";
|
||||||
|
});
|
||||||
|
return csvContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showCardPopup(cardName, setCode) {
|
||||||
|
const cardPopup = document.getElementById('card-popup');
|
||||||
|
const response = await fetch(`https://api.scryfall.com/cards/named?exact=${encodeURIComponent(cardName)}&set=${encodeURIComponent(setCode)}`);
|
||||||
|
const data = await response.json();
|
||||||
|
const imageUrl = data.image_uris ? data.image_uris.small : null;
|
||||||
|
|
||||||
|
if (imageUrl) {
|
||||||
|
cardPopup.innerHTML = `<img src="${imageUrl}" alt="${cardName}">`;
|
||||||
|
cardPopup.style.display = 'block';
|
||||||
|
cardPopup.style.opacity = 1;
|
||||||
|
|
||||||
|
// Dissolve the popup after a few seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
cardPopup.style.opacity = 0;
|
||||||
|
setTimeout(() => {
|
||||||
|
cardPopup.style.display = 'none';
|
||||||
|
}, 500);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadCSV() {
|
||||||
|
const csvContent = generateCSVContent();
|
||||||
|
|
||||||
|
// Create a blob with the CSV content
|
||||||
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Create a temporary link element
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.setAttribute("href", url);
|
||||||
|
link.setAttribute("download", "mtg_cards.csv");
|
||||||
|
document.body.appendChild(link);
|
||||||
|
|
||||||
|
// Trigger the download
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
// Clean up and remove the link
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
47
index.html
Normal file
47
index.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MTG Card CSV Generator</title>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<link rel="manifest" href="manifest.json">
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Sorts+Mill+Goudy&display=swap" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>MTG Card CSV Generator</h1>
|
||||||
|
<form id="card-form">
|
||||||
|
<div>
|
||||||
|
<label for="card-name">Card Name:</label>
|
||||||
|
<input type="text" id="card-name" autocomplete="off" required>
|
||||||
|
<div id="suggestions"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="set-code">Set Code:</label>
|
||||||
|
<input type="text" id="set-code" required>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="card-id">Card ID:</label>
|
||||||
|
<input type="text" id="card-id" required>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="quantity">Quantity:</label>
|
||||||
|
<input type="number" id="quantity" min="1" value="1" required>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="foil">Is it Foil?</label>
|
||||||
|
<select id="foil">
|
||||||
|
<option value="No">No</option>
|
||||||
|
<option value="Yes">Yes</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" onclick="addCard()">Add Card</button>
|
||||||
|
</form>
|
||||||
|
<button onclick="downloadCSV()">Download CSV</button>
|
||||||
|
<textarea id="csv-output" readonly></textarea>
|
||||||
|
<div id="card-popup" class="card-popup"></div>
|
||||||
|
</div>
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
22
manifest.json
Normal file
22
manifest.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "MTG Card CSV Generator",
|
||||||
|
"short_name": "MTG CSV",
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#cc5200",
|
||||||
|
"theme_color": "#007acc",
|
||||||
|
"description": "A PWA for generating CSV files with Magic: The Gathering card details",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icon.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icon.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
22
service-worker.js
Normal file
22
service-worker.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
self.addEventListener('install', (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open('mtg-csv-generator-v1').then((cache) => {
|
||||||
|
return cache.addAll([
|
||||||
|
'/',
|
||||||
|
'/index.html',
|
||||||
|
'/styles.css',
|
||||||
|
'/app.js',
|
||||||
|
'/manifest.json',
|
||||||
|
'/icon.png'
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (event) => {
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(event.request).then((response) => {
|
||||||
|
return response || fetch(event.request);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
115
styles.css
Normal file
115
styles.css
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
:root {
|
||||||
|
--background-color: #f0f0f0;
|
||||||
|
--text-color: #333;
|
||||||
|
--card-background: #ffffff;
|
||||||
|
--button-background: #007acc;
|
||||||
|
--button-hover: #005fa3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background-color: #1e1e1e;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--card-background: #2e2e2e;
|
||||||
|
--button-background: #1f6fb2;
|
||||||
|
--button-hover: #145b8c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: var(--card-background);
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-family: 'Sorts Mill Goudy', serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select, textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--card-background);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: var(--button-background);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: var(--button-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
#csv-output {
|
||||||
|
margin-top: 20px;
|
||||||
|
height: 200px;
|
||||||
|
background-color: var(--card-background);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-popup {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
max-width: 20%;
|
||||||
|
z-index: 1000;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease-in-out;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-popup img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.card-popup {
|
||||||
|
max-width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.card-popup {
|
||||||
|
max-width: 60%;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user