aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/Editor/EditorPane.vue216
-rw-r--r--components/base/Button.vue (renamed from components/Control/Button.vue)0
-rw-r--r--components/base/Checkbox.vue (renamed from components/Control/Checkbox.vue)0
-rw-r--r--components/base/ItemStackForm.vue (renamed from components/Control/ItemStackForm.vue)0
-rw-r--r--components/base/ItemStackModal.vue (renamed from components/Control/ItemStackModal.vue)61
-rw-r--r--components/base/ItemStackPicker.vue (renamed from components/Control/ItemStackPicker.vue)0
-rw-r--r--components/base/Modal.vue (renamed from components/Control/Modal.vue)0
-rw-r--r--components/base/TrueFalseSwitch.vue (renamed from components/Control/TrueFalseSwitch.vue)0
-rw-r--r--components/editor/OptionsPanel.vue (renamed from components/Editor/EditorOptionsPanel.vue)0
-rw-r--r--components/editor/Sidebar.vue (renamed from components/Editor/EditorSidebar.vue)6
-rw-r--r--components/editor/SidebarCategory.vue (renamed from components/Editor/EditorSidebarCategory.vue)1
-rw-r--r--components/editor/SidebarQuest.vue (renamed from components/Editor/EditorSidebarQuest.vue)0
-rw-r--r--components/editor/category/ChildrenOptionsPanel.vue (renamed from components/Editor/Category/CategoryChildrenOptionsPanel.vue)2
-rw-r--r--components/editor/category/OptionsPanel.vue (renamed from components/Editor/Category/CategoryOptionsPanel.vue)6
-rw-r--r--components/editor/quest/OptionsPanel.vue (renamed from components/Editor/Quest/QuestOptionsPanel.vue)20
-rw-r--r--components/editor/quest/TasksOptionsPanel.vue (renamed from components/Editor/Quest/QuestTasksOptionsPanel.vue)36
-rw-r--r--components/editor/quest/modal/Delete.vue (renamed from components/Editor/Quest/Modal/DeleteQuestModal.vue)16
-rw-r--r--components/editor/quest/modal/Duplicate.vue (renamed from components/Editor/Quest/Modal/DuplicateQuestModal.vue)17
-rw-r--r--components/editor/quest/modal/Rename.vue (renamed from components/Editor/Quest/Modal/RenameQuestModal.vue)17
-rw-r--r--components/editor/task/Configuration.vue (renamed from components/Editor/Quest/Task/TaskConfiguration.vue)67
-rw-r--r--components/editor/task/ConfigurationRow.vue (renamed from components/Editor/Quest/Task/TaskConfigurationRow.vue)12
-rw-r--r--components/editor/task/modal/Change.vue (renamed from components/Editor/Quest/Task/Modal/ChangeTaskModal.vue)26
-rw-r--r--components/editor/task/modal/Create.vue (renamed from components/Editor/Quest/Task/Modal/AddTaskModal.vue)25
-rw-r--r--components/header/SiteHeader.vue (renamed from components/Header/SiteHeader.vue)0
-rw-r--r--layouts/editor.vue2
-rw-r--r--lib/util.ts12
-rw-r--r--nuxt.config.ts5
-rw-r--r--pages/category/[id].vue7
-rw-r--r--pages/quest/[id].vue28
29 files changed, 116 insertions, 466 deletions
diff --git a/components/Editor/EditorPane.vue b/components/Editor/EditorPane.vue
deleted file mode 100644
index bf9532a..0000000
--- a/components/Editor/EditorPane.vue
+++ /dev/null
@@ -1,216 +0,0 @@
-<script setup lang="ts">
-import { useSessionStore } from '@/stores/session';
-import { computed, ref } from 'vue';
-import { stripColorCodes } from '@/lib/util';
-import QuestOptionsPanel from '@/components/Editor/Quest/QuestOptionsPanel.vue';
-import QuestTasksOptionsPanel from '@/components/Editor/Quest/QuestTasksOptionsPanel.vue';
-import CategoryOptionsPanel from '@/components/Editor/Category/CategoryOptionsPanel.vue';
-import CategoryChildrenOptionsPanel from '@/components/Editor/Category/CategoryChildrenOptionsPanel.vue';
-import Button from '@/components/Control/Button.vue';
-import DeleteQuestModal from '@/components/Editor/Quest/Modal/DeleteQuestModal.vue';
-import RenameQuestModal from '@/components/Editor/Quest/Modal/RenameQuestModal.vue';
-import DuplicateQuestModal from '@/components/Editor/Quest/Modal/DuplicateQuestModal.vue';
-
-const sessionStore = useSessionStore();
-
-const selectedType = computed(() => sessionStore.editor.selected.type);
-const selectedId = computed(() => sessionStore.editor.selected.id);
-
-const selectedName = computed(() => {
- if (selectedType.value === 'Quest' && selectedId.value) {
- return sessionStore.getQuestById(selectedId.value)?.display.name;
- } else if (selectedType.value === 'Category' && selectedId.value) {
- return sessionStore.getCategoryById(selectedId.value)?.display.name;
- } else {
- return '';
- }
-});
-
-const categoryFromSelectedQuest = computed(() => {
- if (!selectedId.value || selectedType.value !== 'Quest') return null;
-
- const quest = sessionStore.getQuestById(selectedId.value);
- if (quest) {
- return sessionStore.getCategoryById(quest.options.category) || null;
- } else {
- return null;
- }
-});
-
-const showDeleteModal = ref(false);
-const showRenameModal = ref(false);
-const showDuplicateModal = ref(false);
-
-const renameQuest = (oldId: string, newId: string) => {
- sessionStore.changeQuestId(oldId, newId);
- sessionStore.editor.selected.id = newId;
- showRenameModal.value = false;
-};
-
-const deleteQuest = (questId: string) => {
- sessionStore.deleteQuest(questId);
- sessionStore.setEditorSelected(null, null);
- showDeleteModal.value = false;
-};
-
-const duplicateQuest = (oldId: string, newId: string) => {
- sessionStore.duplicateQuest(oldId, newId);
- sessionStore.editor.selected.id = newId;
- showDuplicateModal.value = false;
-};
-</script>
-
-<template>
- <div id="pane-container" v-if="!selectedId || !selectedType">
- <h1 class="none-selected">Select a quest or category from the sidebar to continue</h1>
- </div>
- <div id="pane-container" v-if="selectedId && selectedType">
- <div id="header">
- <span id="path">
- <template v-if="selectedType === 'Quest'">
- <template v-if="categoryFromSelectedQuest">
- <font-awesome-icon class="icon" :icon="['fas', 'fa-folder']"/>
- {{ stripColorCodes(categoryFromSelectedQuest?.display.name) }}
- <font-awesome-icon class="chevron" :icon="['fas', 'fa-chevron-right']"/>
- </template>
- <font-awesome-icon class="icon" :icon="['far', 'fa-compass']"/>
- <span class="title">{{ stripColorCodes(selectedName!) }} </span>
- <code>({{ selectedId }})</code>
- </template>
- <template v-if="selectedType === 'Category'">
- <font-awesome-icon class="icon" :icon="['fas', 'fa-folder']"/>
- <span class="title">{{ stripColorCodes(selectedName!) }} </span>
- <code>({{ selectedId }})</code>
- </template>
- </span>
- <span id="controls" class="control-group">
- <Button
- v-if="selectedType === 'Quest'"
- :icon="['fas', 'fa-code']"
- :label="'YAML'"
- ></Button>
- <Button
- v-if="selectedType === 'Quest'"
- :icon="['fas', 'fa-copy']"
- :label="'Duplicate'"
- @click="showDuplicateModal = true"
- ></Button>
- <Button
- :icon="['fas', 'fa-pen']"
- :label="'Rename'"
- @click="showRenameModal = true"
- ></Button>
- <Button
- :icon="['fas', 'fa-trash']"
- :label="'Delete'"
- @click="showDeleteModal = true"
- ></Button>
- <Button
- type="solid"
- :disabled="true"
- :icon="['fas', 'fa-save']"
- :label="'Save'"
- ></Button>
- </span>
- </div>
-
- <div id="options-container">
- <QuestOptionsPanel v-if="selectedType === 'Quest'" :questId="selectedId" />
- <QuestTasksOptionsPanel v-if="selectedType === 'Quest'" :questId="selectedId" />
-
- <CategoryOptionsPanel v-if="selectedType === 'Category'" :categoryId="selectedId" />
- <CategoryChildrenOptionsPanel v-if="selectedType === 'Category'" :categoryId="selectedId" />
- </div>
- </div>
-
- <DeleteQuestModal
- v-if="selectedType === 'Quest' && selectedId"
- v-model="showDeleteModal"
- :key="`delete-quest-${selectedId}`"
- :questId="selectedId"
- @delete="() => selectedId && deleteQuest(selectedId)"
- />
- <RenameQuestModal
- v-if="selectedType === 'Quest' && selectedId"
- v-model="showRenameModal"
- :key="`rename-quest-${selectedId}`"
- :questId="selectedId"
- @update="newId => selectedId && renameQuest(selectedId, newId)"
- />
- <DuplicateQuestModal
- v-if="selectedType === 'Quest' && selectedId"
- v-model="showDuplicateModal"
- :key="`duplicate-quest-${selectedId}`"
- :questId="selectedId"
- @duplicate="newId => selectedId && duplicateQuest(selectedId, newId)"
- />
-</template>
-
-<style scoped>
-#header {
- padding: 1rem 1rem 0.5rem 1rem;
- background-color: var(--color-background);
- border-bottom: 1px solid var(--color-border);
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
- width: 100%;
- height: 55px;
- display: flex;
- align-items: left;
- justify-content: space-between;
- gap: 1rem;
-
- #path {
- font-size: 1.2rem;
- display: flex;
- gap: 0.5rem;
- align-items: center;
-
- .icon {
- font-size: 0.8rem;
- }
-
- .chevron {
- font-size: 0.8rem;
- color: var(--color-text-mute);
- }
-
- .title {
- font-weight: 700;
- }
-
- code {
- font-size: 0.8rem;
- color: var(--color-text-mute);
- }
- }
-}
-
-.none-selected {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 1rem;
- font-size: 1.2rem;
- color: var(--color-text-mute);
-}
-
-#pane-container {
- width: 100%;
- flex-grow: 1;
- height: calc(100vh - 73px);
- max-height: calc(100vh - 73px);
-}
-
-#options-container {
- width: 100%;
- display: flex;
- gap: 1rem;
- padding: 1rem;
- overflow: scroll;
- max-height: calc(100% - 55px);
-}
-
-header {
- border-bottom: 1px solid var(--color-border);
-}
-</style> \ No newline at end of file
diff --git a/components/Control/Button.vue b/components/base/Button.vue
index efd91c2..efd91c2 100644
--- a/components/Control/Button.vue
+++ b/components/base/Button.vue
diff --git a/components/Control/Checkbox.vue b/components/base/Checkbox.vue
index e0325e7..e0325e7 100644
--- a/components/Control/Checkbox.vue
+++ b/components/base/Checkbox.vue
diff --git a/components/Control/ItemStackForm.vue b/components/base/ItemStackForm.vue
index 250e8c9..250e8c9 100644
--- a/components/Control/ItemStackForm.vue
+++ b/components/base/ItemStackForm.vue
diff --git a/components/Control/ItemStackModal.vue b/components/base/ItemStackModal.vue
index 642c5f9..9a1e2b5 100644
--- a/components/Control/ItemStackModal.vue
+++ b/components/base/ItemStackModal.vue
@@ -1,9 +1,6 @@
<script setup lang="ts">
-import Modal from '@/components/Control/Modal.vue';
-import Button from '@/components/Control/Button.vue';
import { computed, ref } from 'vue';
import materials from '@/lib/materials';
-import ItemStackForm from './ItemStackForm.vue';
const model = defineModel();
@@ -21,10 +18,10 @@ const isQuestItem = computed(() => {
});
const isItemStack = computed(() => {
return (
- typeof value.value === 'object'
+ typeof value.value === 'object'
&& (
- value.value?.item !== undefined
- || value.value?.type !== undefined
+ value.value?.item !== undefined
+ || value.value?.type !== undefined
|| value.value?.material !== undefined
))
});
@@ -33,12 +30,12 @@ const isMaterial = computed(() => {
});
const selectedType = ref(
- isQuestItem.value
- ? 'questitem'
- : isItemStack.value
- ? 'itemstack'
- : isMaterial.value
- ? 'material'
+ isQuestItem.value
+ ? 'questitem'
+ : isItemStack.value
+ ? 'itemstack'
+ : isMaterial.value
+ ? 'material'
: ''
);
@@ -69,21 +66,21 @@ const confirm = () => {
<template v-slot:body>
<div id="type">
- <span class="option" @click="setSelectedType('questitem')" :class="{selected: selectedType === 'questitem'}">
+ <span class="option" @click="setSelectedType('questitem')" :class="{ selected: selectedType === 'questitem' }">
<span>
<font-awesome-icon :icon="['fas', 'fa-tag']" />
Quest Item
</span>
<p v-if="noTypeSelected">Re-use a quest item.</p>
</span>
- <span class="option" @click="setSelectedType('itemstack')" :class="{selected: selectedType === 'itemstack'}">
+ <span class="option" @click="setSelectedType('itemstack')" :class="{ selected: selectedType === 'itemstack' }">
<span>
<font-awesome-icon :icon="['fas', 'fa-cube']" />
ItemStack
</span>
<p v-if="noTypeSelected">Define a new item stack.</p>
</span>
- <span class="option" @click="setSelectedType('material')" :class="{selected: selectedType === 'material'}">
+ <span class="option" @click="setSelectedType('material')" :class="{ selected: selectedType === 'material' }">
<span>
<font-awesome-icon :icon="['fas', 'fa-apple-whole']" />
Material
@@ -91,31 +88,21 @@ const confirm = () => {
<p v-if="noTypeSelected">Define a specific item type.</p>
</span>
</div>
-
+
<div id="material" class="option-group" v-if="selectedType === 'material'">
<label for="material">Material</label>
- <multiselect v-model="value"
- :options="materials" :searchable="true" placeholder="Enter material name" />
+ <multiselect v-model="value" :options="materials" :searchable="true" placeholder="Enter material name" />
</div>
-
+
<div id="itemstack" class="option-group" v-if="selectedType === 'itemstack'">
<ItemStackForm v-model="value" />
</div>
-
+
<div id="confirm" class="control-group">
- <Button
- :icon="['fas', 'fa-times']"
- :label="'Cancel'"
- @click="model = false"
- ></Button>
- <Button
- type="solid"
- :icon="['fas', 'fa-check']"
- :label="'Confirm'"
- @click="confirm"
- ></Button>
- <!-- :disabled="noTypeSelected || noValue" -->
+ <Button :icon="['fas', 'fa-times']" :label="'Cancel'" @click="model = false"></Button>
+ <Button type="solid" :icon="['fas', 'fa-check']" :label="'Confirm'" @click="confirm"></Button>
+ <!-- :disabled="noTypeSelected || noValue" -->
</div>
</template>
</Modal>
@@ -134,7 +121,7 @@ const confirm = () => {
gap: 0.25rem;
user-select: none;
margin-bottom: 1rem;
-
+
.option {
border: 1px solid var(--color-border);
cursor: pointer;
@@ -147,23 +134,23 @@ const confirm = () => {
padding: 0.5rem;
background-color: var(--color-background-soft);
transition: background-color 0.3s;
-
+
span {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 700;
}
-
+
p {
text-align: center;
font-size: 0.8rem;
}
-
+
&:hover {
background-color: var(--color-hover);
}
-
+
&.selected {
background-color: var(--color-primary-mute);
}
diff --git a/components/Control/ItemStackPicker.vue b/components/base/ItemStackPicker.vue
index d16090e..d16090e 100644
--- a/components/Control/ItemStackPicker.vue
+++ b/components/base/ItemStackPicker.vue
diff --git a/components/Control/Modal.vue b/components/base/Modal.vue
index 46d5da5..46d5da5 100644
--- a/components/Control/Modal.vue
+++ b/components/base/Modal.vue
diff --git a/components/Control/TrueFalseSwitch.vue b/components/base/TrueFalseSwitch.vue
index a0a3392..a0a3392 100644
--- a/components/Control/TrueFalseSwitch.vue
+++ b/components/base/TrueFalseSwitch.vue
diff --git a/components/Editor/EditorOptionsPanel.vue b/components/editor/OptionsPanel.vue
index 1415d84..1415d84 100644
--- a/components/Editor/EditorOptionsPanel.vue
+++ b/components/editor/OptionsPanel.vue
diff --git a/components/Editor/EditorSidebar.vue b/components/editor/Sidebar.vue
index c9539fa..a46cdbe 100644
--- a/components/Editor/EditorSidebar.vue
+++ b/components/editor/Sidebar.vue
@@ -1,8 +1,6 @@
<script setup lang="ts">
import { useSessionStore } from '@/stores/session';
import { storeToRefs } from 'pinia';
-import EditorSidebarCategory from '@/components/Editor/EditorSidebarCategory.vue';
-import EditorSidebarQuest from '@/components/Editor/EditorSidebarQuest.vue';
const sessionStore = useSessionStore();
@@ -12,7 +10,9 @@ const { session } = storeToRefs(sessionStore);
<template>
<div id="sidebar-container">
<EditorSidebarCategory v-for="category in session.categories" :key="category.id" :category="category" />
- <EditorSidebarQuest v-for="quest in session.quests.filter((q) => (!session.categories.some((c) => c.id === q.options.category)))" :key="quest.id" :quest="quest" />
+ <EditorSidebarQuest
+ v-for="quest in session.quests.filter((q) => (!session.categories.some((c) => c.id === q.options.category)))"
+ :key="quest.id" :quest="quest" />
</div>
</template>
diff --git a/components/Editor/EditorSidebarCategory.vue b/components/editor/SidebarCategory.vue
index 071ebc8..3e0db9b 100644
--- a/components/Editor/EditorSidebarCategory.vue
+++ b/components/editor/SidebarCategory.vue
@@ -2,7 +2,6 @@
import { useSessionStore, type EditorCategory } from '@/stores/session';
import { computed, ref, toRefs } from 'vue';
import { stripColorCodes } from '@/lib/util';
-import EditorSidebarQuest from '@/components/Editor/EditorSidebarQuest.vue';
const props = defineProps<{
category: EditorCategory;
diff --git a/components/Editor/EditorSidebarQuest.vue b/components/editor/SidebarQuest.vue
index a7b3e3f..a7b3e3f 100644
--- a/components/Editor/EditorSidebarQuest.vue
+++ b/components/editor/SidebarQuest.vue
diff --git a/components/Editor/Category/CategoryChildrenOptionsPanel.vue b/components/editor/category/ChildrenOptionsPanel.vue
index 6e96f64..57dcef2 100644
--- a/components/Editor/Category/CategoryChildrenOptionsPanel.vue
+++ b/components/editor/category/ChildrenOptionsPanel.vue
@@ -1,7 +1,6 @@
<script setup lang="ts">
import { useSessionStore, type EditorCategory } from '@/stores/session';
import { computed } from 'vue';
-import EditorOptionsPanel from '../EditorOptionsPanel.vue';
const props = defineProps<{
categoryId: string;
@@ -50,4 +49,3 @@ h2 {
border-bottom: 1px solid var(--color-border);
}
</style>
-
diff --git a/components/Editor/Category/CategoryOptionsPanel.vue b/components/editor/category/OptionsPanel.vue
index f7d548c..112b063 100644
--- a/components/Editor/Category/CategoryOptionsPanel.vue
+++ b/components/editor/category/OptionsPanel.vue
@@ -1,8 +1,6 @@
<script setup lang="ts">
import { useSessionStore, type EditorCategory } from '@/stores/session';
import { computed } from 'vue';
-import EditorOptionsPanel from '../EditorOptionsPanel.vue';
-import Checkbox from '@/components/Control/Checkbox.vue';
const props = defineProps<{
categoryId: string;
@@ -20,7 +18,8 @@ const category = computed(() => {
<div id="options">
<div class="option-group">
<Checkbox id="category-permissionrequired" label="Require permission for category"
- description="Players must have permission to open and start quests in this category." v-model="category.permissionRequired" />
+ description="Players must have permission to open and start quests in this category."
+ v-model="category.permissionRequired" />
</div>
</div>
</EditorOptionsPanel>
@@ -51,4 +50,3 @@ h2 {
border-bottom: 1px solid var(--color-border);
}
</style>
-
diff --git a/components/Editor/Quest/QuestOptionsPanel.vue b/components/editor/quest/OptionsPanel.vue
index a462126..de32abb 100644
--- a/components/Editor/Quest/QuestOptionsPanel.vue
+++ b/components/editor/quest/OptionsPanel.vue
@@ -1,8 +1,6 @@
<script setup lang="ts">
import { useSessionStore, type EditorQuest } from '@/stores/session';
import { computed, ref } from 'vue';
-import EditorOptionsPanel from '@/components/Editor/EditorOptionsPanel.vue';
-import Checkbox from '@/components/Control/Checkbox.vue';
const props = defineProps<{
questId: string;
@@ -27,24 +25,14 @@ const knownQuests = computed(() => {
<div id="options">
<div class="option-group">
<label for="quest-category">Category</label>
- <multiselect
- id="quest-category"
- v-model="quest.options.category"
- :options="knownCategories"
- :searchable="true"
+ <multiselect id="quest-category" v-model="quest.options.category" :options="knownCategories" :searchable="true"
placeholder="No category"></multiselect>
</div>
<div class="option-group">
<label for="quest-requirements">Requirements</label>
- <multiselect
- id="quest-requirements"
- v-model="quest.options.requirements"
- :options="knownQuests"
- :searchable="true"
- :taggable="true"
- :multiple="true"
- placeholder="Add requirement"></multiselect>
+ <multiselect id="quest-requirements" v-model="quest.options.requirements" :options="knownQuests"
+ :searchable="true" :taggable="true" :multiple="true" placeholder="Add requirement"></multiselect>
<p class="description">
This quest will only be available if the player has completed all of the quests listed above.
</p>
@@ -140,6 +128,4 @@ const knownQuests = computed(() => {
h2 {
border-bottom: 1px solid var(--color-border);
}
-
</style>
-
diff --git a/components/Editor/Quest/QuestTasksOptionsPanel.vue b/components/editor/quest/TasksOptionsPanel.vue
index a79e636..7742408 100644
--- a/components/Editor/Quest/QuestTasksOptionsPanel.vue
+++ b/components/editor/quest/TasksOptionsPanel.vue
@@ -1,10 +1,6 @@
<script setup lang="ts">
import { useSessionStore, type EditorQuest } from '@/stores/session';
import { computed, ref } from 'vue';
-import EditorOptionsPanel from '@/components/Editor/EditorOptionsPanel.vue';
-import TaskConfiguration from '@/components/Editor/Quest/Task/TaskConfiguration.vue';
-import Button from '@/components/Control/Button.vue';
-import AddTaskModal from './Task/Modal/AddTaskModal.vue';
const props = defineProps<{
questId: string;
@@ -25,7 +21,7 @@ const addTask = (newId: string, newType: string) => {
type: newType,
},
};
-
+
showAddTaskModal.value = false;
};
</script>
@@ -34,28 +30,19 @@ const addTask = (newId: string, newType: string) => {
<EditorOptionsPanel v-if="quest">
<div id="options">
<h2>Tasks <code>({{ Object.keys(quest.tasks).length }})</code></h2>
-
+
<p v-if="Object.keys(quest.tasks).length === 0" class="error-text">This quest does not have any tasks.</p>
- <TaskConfiguration v-for="(task, taskId) in quest.tasks" :key="taskId" :taskId="String(taskId)" :quest="quest" />
-
+ <EditorTaskConfiguration v-for="(task, taskId) in quest.tasks" :key="taskId" :taskId="String(taskId)"
+ :quest="quest" />
+
<div id="controls">
- <Button
- id="add-task"
- :icon="['fas', 'fa-plus']"
- type="solid"
- label="Add task"
- @click="showAddTaskModal = true"
- />
+ <Button id="add-task" :icon="['fas', 'fa-plus']" type="solid" label="Add task"
+ @click="showAddTaskModal = true" />
</div>
</div>
</EditorOptionsPanel>
-
- <AddTaskModal
- v-if="quest"
- v-model="showAddTaskModal"
- :questId="questId"
- @add="addTask"
- />
+
+ <AddTaskModal v-if="quest" v-model="showAddTaskModal" :questId="questId" @add="addTask" />
</template>
@@ -64,7 +51,7 @@ const addTask = (newId: string, newType: string) => {
display: flex;
flex-direction: column;
gap: 1rem;
-
+
#controls {
display: flex;
justify-content: flex-end;
@@ -88,11 +75,10 @@ label {
h2 {
border-bottom: 1px solid var(--color-border);
-
+
code {
font-size: 0.8em;
color: var(--color-text-mute);
}
}
</style>
-
diff --git a/components/Editor/Quest/Modal/DeleteQuestModal.vue b/components/editor/quest/modal/Delete.vue
index d0b0c5a..47c6388 100644
--- a/components/Editor/Quest/Modal/DeleteQuestModal.vue
+++ b/components/editor/quest/modal/Delete.vue
@@ -1,7 +1,4 @@
<script setup lang="ts">
-import Modal from '@/components/Control/Modal.vue';
-import Button from '@/components/Control/Button.vue';
-
const model = defineModel();
const emit = defineEmits(['delete']);
@@ -18,17 +15,8 @@ defineProps({
</template>
<p>Are you sure you want to delete this quest? The quests editor does not have undo functionality (yet)! </p>
<div id="confirm" class="control-group">
- <Button
- :icon="['fas', 'fa-times']"
- :label="'Cancel'"
- @click="model = false"
- ></Button>
- <Button
- type="solid"
- :icon="['fas', 'fa-trash']"
- :label="'Delete'"
- @click="emit('delete')"
- ></Button>
+ <Button :icon="['fas', 'fa-times']" :label="'Cancel'" @click="model = false"></Button>
+ <Button type="solid" :icon="['fas', 'fa-trash']" :label="'Delete'" @click="emit('delete')"></Button>
</div>
</Modal>
</template>
diff --git a/components/Editor/Quest/Modal/DuplicateQuestModal.vue b/components/editor/quest/modal/Duplicate.vue
index bcd3782..e089222 100644
--- a/components/Editor/Quest/Modal/DuplicateQuestModal.vue
+++ b/components/editor/quest/modal/Duplicate.vue
@@ -1,6 +1,4 @@
<script setup lang="ts">
-import Modal from '@/components/Control/Modal.vue';
-import Button from '@/components/Control/Button.vue';
import { computed, ref } from 'vue';
import { useSessionStore } from '@/stores/session';
@@ -37,18 +35,9 @@ const isDuplicate = computed(() => {
<p v-if="isDuplicate" class="error-text">Name is not unique.</p>
<p>A Quest ID must be unique, alphanumeric, and not contain any spaces.</p>
<div id="confirm" class="control-group">
- <Button
- :icon="['fas', 'fa-times']"
- :label="'Cancel'"
- @click="model = false"
- ></Button>
- <Button
- type="solid"
- :icon="['fas', 'fa-check']"
- :label="'Duplicate'"
- :disabled="isDuplicate"
- @click="emit('duplicate', newQuestId)"
- ></Button>
+ <Button :icon="['fas', 'fa-times']" :label="'Cancel'" @click="model = false"></Button>
+ <Button type="solid" :icon="['fas', 'fa-check']" :label="'Duplicate'" :disabled="isDuplicate"
+ @click="emit('duplicate', newQuestId)"></Button>
</div>
</div>
</template>
diff --git a/components/Editor/Quest/Modal/RenameQuestModal.vue b/components/editor/quest/modal/Rename.vue
index 2ad1481..7339219 100644
--- a/components/Editor/Quest/Modal/RenameQuestModal.vue
+++ b/components/editor/quest/modal/Rename.vue
@@ -1,6 +1,4 @@
<script setup lang="ts">
-import Modal from '@/components/Control/Modal.vue';
-import Button from '@/components/Control/Button.vue';
import { computed, ref } from 'vue';
import { useSessionStore } from '@/stores/session';
@@ -37,18 +35,9 @@ const isDuplicate = computed(() => {
<p v-if="isDuplicate" class="error-text">Name is not unique.</p>
<p>A Quest ID must be unique, alphanumeric, and not contain any spaces.</p>
<div id="confirm" class="control-group">
- <Button
- :icon="['fas', 'fa-times']"
- :label="'Cancel'"
- @click="model = false"
- ></Button>
- <Button
- type="solid"
- :icon="['fas', 'fa-check']"
- :label="'Rename'"
- :disabled="isDuplicate"
- @click="emit('update', newQuestId)"
- ></Button>
+ <Button :icon="['fas', 'fa-times']" :label="'Cancel'" @click="model = false"></Button>
+ <Button type="solid" :icon="['fas', 'fa-check']" :label="'Rename'" :disabled="isDuplicate"
+ @click="emit('update', newQuestId)"></Button>
</div>
</div>
</template>
diff --git a/components/Editor/Quest/Task/TaskConfiguration.vue b/components/editor/task/Configuration.vue
index 0646ad4..f408726 100644
--- a/components/Editor/Quest/Task/TaskConfiguration.vue
+++ b/components/editor/task/Configuration.vue
@@ -1,9 +1,6 @@
<script setup lang="ts">
import { useSessionStore, type EditorQuest } from '@/stores/session';
import { computed, ref } from 'vue';
-import Button from '@/components/Control/Button.vue';
-import TaskConfigurationRow from '@/components/Editor/Quest/Task/TaskConfigurationRow.vue';
-import ChangeTaskModal from './Modal/ChangeTaskModal.vue';
const props = defineProps<{
taskId: string;
@@ -82,69 +79,47 @@ const deleteTaskType = (taskId: string) => {
<div id="task-header">
<p id="task-id">
<span id="task-name">
- {{ props.taskId }}
+ {{ props.taskId }}
</span>
<code>
({{ taskType }})
</code>
</p>
<div id="task-controls" class="control-group">
- <Button
- :icon="['fas', 'fa-pen']"
- :label="'Change'"
- @click="showChangeModal = true"
- ></Button>
- <Button
- :icon="['fas', 'fa-trash']"
- :label="'Delete'"
- @click="deleteTaskType(props.taskId)"
- ></Button>
+ <Button :icon="['fas', 'fa-pen']" :label="'Change'" @click="showChangeModal = true"></Button>
+ <Button :icon="['fas', 'fa-trash']" :label="'Delete'" @click="deleteTaskType(props.taskId)"></Button>
</div>
</div>
<div id="task-configuration">
<div v-if="!taskDefintion" class="error">
- <font-awesome-icon id="error-icon" :icon="['fas', 'fa-triangle-exclamation']"/>
+ <font-awesome-icon id="error-icon" :icon="['fas', 'fa-triangle-exclamation']" />
<p id="error-message">
Unable to edit task <code>{{ props.taskId }}</code>.
</p>
<p id="error-description">
- The quests web editor does not know how to configure task
+ The quests web editor does not know how to configure task
type <code>{{ taskType }}</code> as it has no task definition.
</p>
</div>
-
+
<div v-if="taskDefintion">
- <TaskConfigurationRow
- v-for="fieldName in [...givenRequiredFields, ...missingFields, ...remainingGivenFields]"
- :key="`${quest.id}-${props.taskId}-${taskType}-${fieldName}`"
- :required="requiredFields.includes(fieldName)"
- :configKey="fieldName"
- :initialValue="taskConfig[fieldName]"
- :taskType="taskType"
+ <EditorTaskConfigurationRow
+ v-for="fieldName in [...givenRequiredFields, ...missingFields, ...remainingGivenFields]"
+ :key="`${quest.id}-${props.taskId}-${taskType}-${fieldName}`" :required="requiredFields.includes(fieldName)"
+ :configKey="fieldName" :initialValue="taskConfig[fieldName]" :taskType="taskType"
:type="(taskDefintion.configuration[fieldName].type as string)"
- @update="newValue => updateValue(fieldName, newValue)"
- @delete="() => deleteValue(fieldName)"
- />
+ @update="(newValue: any) => updateValue(fieldName, newValue)" @delete="() => deleteValue(fieldName)" />
<div id="add-option">
- <multiselect
- class="multiselect"
- :options="configKeysOptions"
- :searchable="true"
- @select="onAddOption"
+ <multiselect class="multiselect" :options="configKeysOptions" :searchable="true" @select="onAddOption"
placeholder="Add option...">
</multiselect>
</div>
</div>
</div>
</div>
-
- <ChangeTaskModal
- v-model="showChangeModal"
- :taskId="props.taskId"
- :currentTaskType="taskType"
- :key="`change-task-${props.taskId}`"
- @update="updateTaskType"
- />
+
+ <EditorTaskModalChange v-model="showChangeModal" :taskId="props.taskId" :currentTaskType="taskType"
+ :key="`change-task-${props.taskId}`" @update="updateTaskType" />
</template>
<style scoped>
@@ -155,18 +130,18 @@ const deleteTaskType = (taskId: string) => {
float: left;
margin: 5px 0 0 -20px;
}
-
+
#error-message {
font-weight: 700;
}
-
+
}
-
+
#task-configuration-table {
display: flex;
flex-direction: column;
border: 1px solid var(--color-border);
-
+
#task-header {
display: flex;
justify-content: space-between;
@@ -176,7 +151,7 @@ const deleteTaskType = (taskId: string) => {
#task-id {
font-size: 1.2em;
-
+
#task-name {
font-weight: 700;
}
@@ -204,6 +179,4 @@ const deleteTaskType = (taskId: string) => {
.multiselect::v-deep .multiselect__select {
background: transparent !important;
}
-
</style>
-
diff --git a/components/Editor/Quest/Task/TaskConfigurationRow.vue b/components/editor/task/ConfigurationRow.vue
index f68ce97..fa512b6 100644
--- a/components/Editor/Quest/Task/TaskConfigurationRow.vue
+++ b/components/editor/task/ConfigurationRow.vue
@@ -1,8 +1,6 @@
<script setup lang="ts">
import { useSessionStore } from '@/stores/session';
import { computed, ref, toRefs, watch } from 'vue';
-import TrueFalseSwitch from '@/components/Control/TrueFalseSwitch.vue';
-import ItemStackPicker from '@/components/Control/ItemStackPicker.vue';
import materials from '@/lib/materials';
const props = defineProps({
@@ -78,13 +76,13 @@ const addValue = (searchQuery: any) => {
<TrueFalseSwitch v-else-if="props.type === 'boolean'" :value="!!currentValue" @update="updateValue" />
<!-- Data type 'material-list' -->
- <multiselect v-else-if="props.type === 'material-list'" v-model="currentValue"
- :options="materials" :multiple="true" :taggable="true" :searchable="true" placeholder="Enter material name" />
-
+ <multiselect v-else-if="props.type === 'material-list'" v-model="currentValue" :options="materials"
+ :multiple="true" :taggable="true" :searchable="true" placeholder="Enter material name" />
+
<!-- Data type 'string-list' -->
<multiselect v-else-if="props.type === 'string-list'" v-model="currentValue" :options="[]" @tag="addValue"
:multiple="true" :taggable="true" :searchable="true" placeholder="Enter string" />
-
+
<!-- Data type 'itemstack' -->
<ItemStackPicker v-else-if="props.type === 'itemstack'" :value="currentValue" @update="updateValue" />
@@ -150,7 +148,7 @@ const addValue = (searchQuery: any) => {
width: 75%;
background-color: var(--color-background);
border-left: 1px solid var(--color-border);
-
+
#value-container {
display: flex;
flex-direction: column;
diff --git a/components/Editor/Quest/Task/Modal/ChangeTaskModal.vue b/components/editor/task/modal/Change.vue
index c6b5921..db7d96b 100644
--- a/components/Editor/Quest/Task/Modal/ChangeTaskModal.vue
+++ b/components/editor/task/modal/Change.vue
@@ -1,6 +1,4 @@
<script setup lang="ts">
-import Modal from '@/components/Control/Modal.vue';
-import Button from '@/components/Control/Button.vue';
import { computed, ref } from 'vue';
import { useSessionStore } from '@/stores/session';
@@ -33,30 +31,16 @@ const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(ne
<div id="body">
<div class="option-group">
<label for="new-type">New type</label>
- <multiselect
- id="new-type"
- v-model="newType"
- :options="knownTaskTypes"
- :searchable="true"
- placeholder="Select a new type"
- ></multiselect>
+ <multiselect id="new-type" v-model="newType" :options="knownTaskTypes" :searchable="true"
+ placeholder="Select a new type"></multiselect>
</div>
<p v-if="unknownTaskType" class="error-text">Invalid task type.</p>
<p v-if="newTypeDescription">{{ newTypeDescription }}</p>
<p>Any configured options for this task will be overwritten.</p>
<div id="confirm" class="control-group">
- <Button
- :icon="['fas', 'fa-times']"
- :label="'Cancel'"
- @click="model = false"
- ></Button>
- <Button
- type="solid"
- :icon="['fas', 'fa-check']"
- :label="'Change'"
- :disabled="unknownTaskType || noChange"
- @click="emit('update', newType)"
- ></Button>
+ <Button :icon="['fas', 'fa-times']" :label="'Cancel'" @click="model = false"></Button>
+ <Button type="solid" :icon="['fas', 'fa-check']" :label="'Change'" :disabled="unknownTaskType || noChange"
+ @click="emit('update', newType)"></Button>
</div>
</div>
</template>
diff --git a/components/Editor/Quest/Task/Modal/AddTaskModal.vue b/components/editor/task/modal/Create.vue
index 57139bb..d286759 100644
--- a/components/Editor/Quest/Task/Modal/AddTaskModal.vue
+++ b/components/editor/task/modal/Create.vue
@@ -1,6 +1,4 @@
<script setup lang="ts">
-import Modal from '@/components/Control/Modal.vue';
-import Button from '@/components/Control/Button.vue';
import { computed, ref } from 'vue';
import { useSessionStore } from '@/stores/session';
import { validateTaskId } from '@/lib/util';
@@ -46,30 +44,17 @@ const newTypeDescription = computed(() => session.getTaskDefinitionByTaskType(ne
</div>
<div class="option-group">
<label for="new-type">Task type</label>
- <multiselect
- id="new-type"
- v-model="newType"
- :options="knownTaskTypes"
- :searchable="true"
- placeholder="Select a new type"
- ></multiselect>
+ <multiselect id="new-type" v-model="newType" :options="knownTaskTypes" :searchable="true"
+ placeholder="Select a new type"></multiselect>
<p v-if="unknownTaskType" class="error-text">Invalid task type.</p>
</div>
<p v-if="newTypeDescription">{{ newTypeDescription }}</p>
<p>A task ID must be unique, alphanumeric, and not contain any spaces.</p>
<div id="confirm" class="control-group">
- <Button
- :icon="['fas', 'fa-times']"
- :label="'Cancel'"
- @click="model = false"
- ></Button>
- <Button
- type="solid"
- :icon="['fas', 'fa-check']"
- :label="'Confirm'"
+ <Button :icon="['fas', 'fa-times']" :label="'Cancel'" @click="model = false"></Button>
+ <Button type="solid" :icon="['fas', 'fa-check']" :label="'Confirm'"
:disabled="unknownTaskType || invalidTaskId || duplicateTaskId"
- @click="emit('add', newId, newType)"
- ></Button>
+ @click="emit('add', newId, newType)"></Button>
</div>
</div>
</template>
diff --git a/components/Header/SiteHeader.vue b/components/header/SiteHeader.vue
index f51e970..f51e970 100644
--- a/components/Header/SiteHeader.vue
+++ b/components/header/SiteHeader.vue
diff --git a/layouts/editor.vue b/layouts/editor.vue
index 873e55c..884160f 100644
--- a/layouts/editor.vue
+++ b/layouts/editor.vue
@@ -1,8 +1,6 @@
<script setup lang="ts">
import { useSessionStore } from '@/stores/session';
import { loadQuestsFromJson, loadCategoriesFromJson } from '@/lib/questsLoader';
-import SiteHeader from '@/components/Header/SiteHeader.vue';
-import EditorSidebar from '@/components/Editor/EditorSidebar.vue';
import testData from '@/data/testData.json';
import taskDefinitions from '@/data/taskDefinitions.json';
diff --git a/lib/util.ts b/lib/util.ts
index b8be8cb..3a7e9aa 100644
--- a/lib/util.ts
+++ b/lib/util.ts
@@ -15,4 +15,16 @@ export function validateCategoryId(id: string): boolean {
export function validateTaskId(id: string): boolean {
return VALID_ID_REGEX.test(id);
+}
+
+export function navigateToEditorPane(type: 'quest' | 'category' | null, id?: string) {
+ if (id) {
+ if (type === 'category') {
+ navigateTo({ path: '/category/' + id })
+ } else if (type === 'quest') {
+ navigateTo({ path: '/quest/' + id })
+ }
+ } else if (!id && !type) {
+ navigateTo({ path: '/' })
+ }
} \ No newline at end of file
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 5a04e12..b208d99 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -1,5 +1,10 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
+ components: [
+ { path: '~/components', pathPrefix: true },
+ { path: '~/components/base', pathPrefix: false },
+ { path: '~/components/header', pathPrefix: false },
+ ],
devtools: { enabled: true },
modules: [
// ...
diff --git a/pages/category/[id].vue b/pages/category/[id].vue
index 66ad26c..59993e7 100644
--- a/pages/category/[id].vue
+++ b/pages/category/[id].vue
@@ -1,9 +1,6 @@
<script setup lang="ts">
import { useSessionStore } from '@/stores/session';
import { stripColorCodes } from '@/lib/util';
-import CategoryOptionsPanel from '@/components/Editor/Category/CategoryOptionsPanel.vue';
-import CategoryChildrenOptionsPanel from '@/components/Editor/Category/CategoryChildrenOptionsPanel.vue';
-import Button from '@/components/Control/Button.vue';
definePageMeta({
layout: 'editor'
@@ -30,8 +27,8 @@ const categoryName = sessionStore.getCategoryById(categoryId)?.display.name;
</div>
<div id="options-container">
- <CategoryOptionsPanel :categoryId="categoryId" />
- <CategoryChildrenOptionsPanel :categoryId="categoryId" />
+ <EditorCategoryOptionsPanel :categoryId="categoryId" />
+ <EditorCategoryChildrenOptionsPanel :categoryId="categoryId" />
</div>
</template>
diff --git a/pages/quest/[id].vue b/pages/quest/[id].vue
index 18d2d04..a3a393d 100644
--- a/pages/quest/[id].vue
+++ b/pages/quest/[id].vue
@@ -1,13 +1,7 @@
<script setup lang="ts">
import { useSessionStore } from '@/stores/session';
import { computed, ref } from 'vue';
-import { stripColorCodes } from '@/lib/util';
-import QuestOptionsPanel from '@/components/Editor/Quest/QuestOptionsPanel.vue';
-import QuestTasksOptionsPanel from '@/components/Editor/Quest/QuestTasksOptionsPanel.vue';
-import Button from '@/components/Control/Button.vue';
-import DeleteQuestModal from '@/components/Editor/Quest/Modal/DeleteQuestModal.vue';
-import RenameQuestModal from '@/components/Editor/Quest/Modal/RenameQuestModal.vue';
-import DuplicateQuestModal from '@/components/Editor/Quest/Modal/DuplicateQuestModal.vue';
+import { navigateToEditorPane, stripColorCodes } from '@/lib/util';
definePageMeta({
layout: 'editor'
@@ -35,19 +29,19 @@ const showDuplicateModal = ref(false);
const renameQuest = (oldId: string, newId: string) => {
sessionStore.changeQuestId(oldId, newId);
- sessionStore.editor.selected.id = newId;
+ navigateToEditorPane('quest', newId);
showRenameModal.value = false;
};
const deleteQuest = (questId: string) => {
sessionStore.deleteQuest(questId);
- sessionStore.setEditorSelected(null, null);
+ navigateToEditorPane(null);
showDeleteModal.value = false;
};
const duplicateQuest = (oldId: string, newId: string) => {
sessionStore.duplicateQuest(oldId, newId);
- sessionStore.editor.selected.id = newId;
+ navigateToEditorPane('quest', newId);
showDuplicateModal.value = false;
};
</script>
@@ -74,16 +68,16 @@ const duplicateQuest = (oldId: string, newId: string) => {
</div>
<div id="options-container">
- <QuestOptionsPanel :questId="questId" />
- <QuestTasksOptionsPanel :questId="questId" />
+ <EditorQuestOptionsPanel :questId="questId" />
+ <EditorQuestTasksOptionsPanel :questId="questId" />
</div>
- <DeleteQuestModal v-model="showDeleteModal" :key="`delete-quest-${questId}`" :questId="questId"
+ <EditorQuestModalDelete v-model="showDeleteModal" :key="`delete-quest-${questId}`" :questId="questId"
@delete="() => questId && deleteQuest(questId)" />
- <RenameQuestModal v-model="showRenameModal" :key="`rename-quest-${questId}`" :questId="questId"
- @update="newId => questId && renameQuest(questId, newId)" />
- <DuplicateQuestModal v-model="showDuplicateModal" :key="`duplicate-quest-${questId}`" :questId="questId"
- @duplicate="newId => questId && duplicateQuest(questId, newId)" />
+ <EditorQuestModalRename v-model="showRenameModal" :key="`rename-quest-${questId}`" :questId="questId"
+ @update="(newId: any) => questId && renameQuest(questId, newId)" />
+ <EditorQuestModalDuplicate v-model="showDuplicateModal" :key="`duplicate-quest-${questId}`" :questId="questId"
+ @duplicate="(newId: any) => questId && duplicateQuest(questId, newId)" />
</template>
<style scoped>