Added world creation dialog
This commit is contained in:
@@ -40,7 +40,7 @@ const isCreatingCalendar = ref<boolean>(false)
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
isCreatingCalendar.value = true
|
||||
await $fetch("/api/calendars/create", { method: "POST", body: {...calendarSkeleton.value, worldId: 1 } })
|
||||
await $fetch("/api/calendars/create", { method: "POST", body: { ...calendarSkeleton.value, worldId: 1 } })
|
||||
|
||||
emit("on-close")
|
||||
} catch (err) {
|
||||
@@ -104,7 +104,7 @@ function handleFormCancel() {
|
||||
type="text"
|
||||
name="new-calendar-name"
|
||||
required
|
||||
placeholder="Titre"
|
||||
:placeholder="$t('common.title')"
|
||||
class="w-full -my-1 py-2 -mx-1 px-1 text-xl border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||
@input="handleNameChange"
|
||||
>
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
type HeadingLevel = "h1" | "h2" | "h3"
|
||||
|
||||
interface HeadingProps {
|
||||
level?: HeadingLevel
|
||||
}
|
||||
|
||||
withDefaults(defineProps<HeadingProps>(), {
|
||||
level: "h2"
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-bold flex">
|
||||
<h1 v-if="level === 'h1'" class="text-4xl font-bold flex">
|
||||
<slot />
|
||||
</h1>
|
||||
<h2 v-else-if="level === 'h2'" class="text-2xl font-bold flex">
|
||||
<slot />
|
||||
</h2>
|
||||
<h3 v-if="level === 'h3'" class="text-xl font-bold flex">
|
||||
<slot />
|
||||
</h3>
|
||||
</template>
|
||||
|
||||
20
components/global/input/Color.vue
Normal file
20
components/global/input/Color.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<script lang="ts" setup>
|
||||
import { type RPGColor, rpgColors } from "~/models/Color";
|
||||
|
||||
const model = defineModel<RPGColor>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiSelect v-model="model">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="$t('ui.colors.selectOne')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectGroup>
|
||||
<UiSelectItem v-for="color in rpgColors" :key="color" :value="color">
|
||||
{{ $t(`ui.colors.${color}`) }}
|
||||
</UiSelectItem>
|
||||
</UiSelectGroup>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
</template>
|
||||
48
components/world/dialog/Create.vue
Normal file
48
components/world/dialog/Create.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhX } from "@phosphor-icons/vue";
|
||||
|
||||
defineProps<{
|
||||
modalState?: boolean
|
||||
}>()
|
||||
|
||||
const worldSkeletonName = ref<string>("")
|
||||
|
||||
function onChangedName(newName: string) {
|
||||
worldSkeletonName.value = newName
|
||||
}
|
||||
|
||||
const emit = defineEmits(["on-close"])
|
||||
|
||||
function handleClose() {
|
||||
emit("on-close")
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiAlertDialog :open="modalState">
|
||||
<UiAlertDialogContent class="gap-4">
|
||||
<header>
|
||||
<UiAlertDialogTitle>
|
||||
<span class="text-2xl">
|
||||
<span v-if="worldSkeletonName">
|
||||
{{ $t('entity.world.createDialog.title') }} — {{ worldSkeletonName }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t('entity.world.createDialog.title') }}
|
||||
</span>
|
||||
</span>
|
||||
</UiAlertDialogTitle>
|
||||
|
||||
<UiAlertDialogDescription>
|
||||
{{ $t('entity.world.createDialog.subtitle') }}
|
||||
</UiAlertDialogDescription>
|
||||
</header>
|
||||
|
||||
<UiButton size="icon" variant="ghost" class="absolute top-4 right-4" title="Fermer la fenêtre" @click="handleClose">
|
||||
<PhX size="20" />
|
||||
</UiButton>
|
||||
|
||||
<WorldFormCreate @on-changed-name="onChangedName" @on-close="handleClose" />
|
||||
</UiAlertDialogContent>
|
||||
</UiAlertDialog>
|
||||
</template>
|
||||
100
components/world/form/Create.vue
Normal file
100
components/world/form/Create.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<script lang="ts" setup>
|
||||
import { PhCircleNotch } from "@phosphor-icons/vue";
|
||||
import type { World } from "~/models/World";
|
||||
|
||||
const user = useSupabaseUser()
|
||||
|
||||
const defaultWorld: World = { name: "", description: "", gmId: user.value?.id }
|
||||
const worldSkeleton = ref<World>({ ...defaultWorld })
|
||||
|
||||
onMounted(() => {
|
||||
worldSkeleton.value = { ...defaultWorld }
|
||||
})
|
||||
|
||||
const isLoading = ref<boolean>(false)
|
||||
|
||||
/**
|
||||
* === Form Validation ===
|
||||
*/
|
||||
/** Whether the skeleton has a valid name */
|
||||
const validSkeleton = computed(() => worldSkeleton.value.name)
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!user.value) return
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
await $fetch("/api/worlds/create", { method: "POST", body: { ...worldSkeleton.value } })
|
||||
|
||||
emit("on-close")
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* === Watch for name changes to display above ===
|
||||
*/
|
||||
const emit = defineEmits<{
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
(e: "on-changed-name", calendarName: string): void
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
(e: "on-close"): void
|
||||
}>()
|
||||
|
||||
/** Hook to emit a debounced event for the changed skeleton name */
|
||||
const handleNameChange = useDebounceFn(() => {
|
||||
emit("on-changed-name", worldSkeleton.value.name)
|
||||
}, 400)
|
||||
|
||||
function handleFormCancel() {
|
||||
emit("on-close")
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="worldSkeleton">
|
||||
<form class="h-full grid grid-rows-[1fr_auto]" @submit.prevent="handleSubmit">
|
||||
<div>
|
||||
<input
|
||||
id="new-world-name"
|
||||
v-model="worldSkeleton.name"
|
||||
type="text"
|
||||
name="new-world-name"
|
||||
required
|
||||
:placeholder="$t('common.title')"
|
||||
class="w-full -my-1 mb-4 py-2 -mx-1 px-1 text-xl border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||
@input="handleNameChange"
|
||||
>
|
||||
|
||||
<div class="-mx-1 mb-4">
|
||||
<InputColor v-model="worldSkeleton.color" />
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
id="new-world-description"
|
||||
v-model="worldSkeleton.description"
|
||||
name="new-world-description"
|
||||
:placeholder="$t('entity.addDescription')"
|
||||
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<footer class="flex justify-end gap-2 mt-6">
|
||||
<UiButton type="button" variant="destructive" @click="handleFormCancel">
|
||||
{{ $t('ui.action.cancel') }}
|
||||
</UiButton>
|
||||
|
||||
<UiButton type="submit" :disabled="!validSkeleton || isLoading">
|
||||
<Transition name="fade">
|
||||
<PhCircleNotch v-if="isLoading" size="20" class="opacity-50 animate-spin"/>
|
||||
</Transition>
|
||||
|
||||
{{ $t('ui.action.save') }}
|
||||
</UiButton>
|
||||
</footer>
|
||||
</form>
|
||||
</template>
|
||||
</template>
|
||||
Reference in New Issue
Block a user