Initial Commit

This commit is contained in:
2025-07-08 05:08:10 +01:00
commit 7e445551af
28 changed files with 1872 additions and 0 deletions
+337
View File
@@ -0,0 +1,337 @@
<template>
<div class="name-generator">
<h2>Name Generator</h2>
<div class="controls">
<div class="name-type-switch">
<span class="switch-label">Name Type:</span>
<div class="switch-container">
<button
type="button"
class="switch-button"
:class="{ active: !modern }"
@click="modern = false"
>
Fantasy
</button>
<button
type="button"
class="switch-button button-switch"
:class="{ active: modern }"
@click="modern = true"
>
Modern
</button>
</div>
</div>
<label v-if="!modern">
Race:
<select v-model="race">
<option v-for="r in races" :key="r.value" :value="r.value">
{{ r.label }}
</option>
</select>
</label>
<label>
Gender:
<select v-model="gender">
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</label>
<label>
Count:
<select v-model="count">
<option :value="1">1 name</option>
<option :value="3">3 names</option>
<option :value="5">5 names</option>
<option :value="8">8 names</option>
<option :value="10">10 names</option>
<option :value="15">15 names</option>
<option :value="20">20 names</option>
</select>
</label>
</div>
<button class="button-main" @click="generate">Generate</button>
<ul class="results">
<li
v-for="name in fullNames"
:key="name"
@click="copyToClipboard(name)"
class="name-item"
:title="'Click to copy: ' + name"
>
{{ name }}
<svg
class="copy-icon"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="9"
y="9"
width="13"
height="13"
rx="2"
ry="2"
stroke="currentColor"
stroke-width="2"
fill="none"
/>
<path
d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
stroke="currentColor"
stroke-width="2"
fill="none"
/>
</svg>
</li>
</ul>
<!-- Toast notification -->
<div v-if="showToast" class="toast">
{{ toastMessage }}
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const modern = ref(false);
const races = [
{ label: "Dragonborn", value: "dragonborn" },
{ label: "Dwarf", value: "dwarf" },
{ label: "Elf", value: "elf" },
{ label: "Gnome", value: "gnome" },
{ label: "Goblin", value: "goblin" },
{ label: "Half-Elf", value: "half-elf" },
{ label: "Half-Orc", value: "half-orc" },
{ label: "Halfling", value: "halfling" },
{ label: "Human", value: "human" },
{ label: "Orc", value: "orc" },
{ label: "Tiefling", value: "tiefling" },
{ label: "Troll", value: "troll" },
];
// Modern name arrays
const modernMaleNames = [
"James",
"John",
"Robert",
"Michael",
"William",
"David",
"Richard",
"Charles",
"Joseph",
"Thomas",
"Christopher",
"Daniel",
"Paul",
"Mark",
"Donald",
"Steven",
"Andrew",
"Kenneth",
"Joshua",
"Kevin",
"Brian",
"George",
"Timothy",
"Ronald",
"Jason",
"Edward",
"Jeffrey",
"Ryan",
"Jacob",
"Gary",
"Nicholas",
"Eric",
"Jonathan",
"Stephen",
"Larry",
"Justin",
"Scott",
"Brandon",
"Benjamin",
"Samuel",
];
const modernFemaleNames = [
"Mary",
"Patricia",
"Jennifer",
"Linda",
"Elizabeth",
"Barbara",
"Susan",
"Jessica",
"Sarah",
"Karen",
"Nancy",
"Lisa",
"Betty",
"Helen",
"Sandra",
"Donna",
"Carol",
"Ruth",
"Sharon",
"Michelle",
"Laura",
"Sarah",
"Kimberly",
"Deborah",
"Dorothy",
"Lisa",
"Nancy",
"Karen",
"Betty",
"Helen",
"Sandra",
"Donna",
"Carol",
"Ruth",
"Sharon",
"Michelle",
"Laura",
"Amy",
"Angela",
"Brenda",
];
const modernLastNames = [
"Smith",
"Johnson",
"Williams",
"Brown",
"Jones",
"Garcia",
"Miller",
"Davis",
"Rodriguez",
"Martinez",
"Hernandez",
"Lopez",
"Gonzalez",
"Wilson",
"Anderson",
"Thomas",
"Taylor",
"Moore",
"Jackson",
"Martin",
"Lee",
"Perez",
"Thompson",
"White",
"Harris",
"Sanchez",
"Clark",
"Ramirez",
"Lewis",
"Robinson",
"Walker",
"Young",
"Allen",
"King",
"Wright",
"Scott",
"Torres",
"Nguyen",
"Hill",
"Flores",
"Green",
"Adams",
"Nelson",
"Baker",
"Hall",
"Rivera",
"Campbell",
"Mitchell",
"Carter",
"Roberts",
];
const race = ref(races[0].value);
const gender = ref("male");
const count = ref(5);
const fullNames = ref([]);
const showToast = ref(false);
const toastMessage = ref("");
function getRandomName(array) {
return array[Math.floor(Math.random() * array.length)];
}
function generateModernNames() {
const names = [];
const firstNames =
gender.value === "male" ? modernMaleNames : modernFemaleNames;
for (let i = 0; i < count.value; i++) {
const firstName = getRandomName(firstNames);
const lastName = getRandomName(modernLastNames);
names.push(`${firstName} ${lastName}`);
}
return names;
}
async function generate() {
fullNames.value = [];
if (modern.value) {
// Generate modern names locally
fullNames.value = generateModernNames();
} else {
// Use API for fantasy names
const selectedRace = race.value;
const firstUrl = `https://names.ironarachne.com/race/${selectedRace}/${gender.value}/${count.value}`;
const familyUrl = `https://names.ironarachne.com/race/${selectedRace}/family/${count.value}`;
try {
const [firstRes, familyRes] = await Promise.all([
fetch(firstUrl),
fetch(familyUrl),
]);
const firstData = await firstRes.json();
const familyData = await familyRes.json();
fullNames.value = firstData.names.map(
(n, i) => `${n} ${familyData.names[i]}`
);
} catch (e) {
console.error("Error fetching names", e);
}
}
}
async function copyToClipboard(name) {
try {
await navigator.clipboard.writeText(name);
showToastMessage(`"${name}" copied to clipboard!`);
} catch (err) {
console.error("Failed to copy to clipboard:", err);
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = name;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
showToastMessage(`"${name}" copied to clipboard!`);
}
}
function showToastMessage(message) {
toastMessage.value = message;
showToast.value = true;
setTimeout(() => {
showToast.value = false;
}, 2000);
}
</script>
<style scoped src="./NameGenerator.css"></style>