Add category editing dialog
This commit is contained in:
parent
b5add9b8a0
commit
f149e9d702
6
components.d.ts
vendored
6
components.d.ts
vendored
@ -7,17 +7,13 @@ export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
copy: typeof import('./src/composables/EditGoalDialog copy.vue')['default']
|
||||
EditGoal: typeof import('./src/composables/EditGoal.vue')['default']
|
||||
EditCategoryDialog: typeof import('./src/composables/EditCategoryDialog.vue')['default']
|
||||
EditGoalDialog: typeof import('./src/composables/EditGoalDialog.vue')['default']
|
||||
EditGoalListDialog: typeof import('./src/composables/EditGoalListDialog.vue')['default']
|
||||
EditorComponent: typeof import('./src/components/EditorComponent.vue')['default']
|
||||
GameEditor: typeof import('./src/composables/GameEditor.vue')['default']
|
||||
GameEditorDialog: typeof import('./src/composables/GameEditorDialog.vue')['default']
|
||||
GameList: typeof import('./src/composables/GameList.vue')['default']
|
||||
GameListDialog__UNUSED_NEEDS_EDITS: typeof import('./src/composables/GameListDialog__UNUSED_NEEDS_EDITS.vue')['default']
|
||||
GeneratorComponent: typeof import('./src/components/GeneratorComponent.vue')['default']
|
||||
GoalEditorDialog: typeof import('./src/components/GoalEditorDialog.vue')['default']
|
||||
MarkdownRenderer: typeof import('./src/composables/MarkdownRenderer.vue')['default']
|
||||
NavbarComponent: typeof import('./src/components/NavbarComponent.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
|
198
src/composables/EditCategoryDialog.vue
Normal file
198
src/composables/EditCategoryDialog.vue
Normal file
@ -0,0 +1,198 @@
|
||||
|
||||
<template>
|
||||
<q-card
|
||||
flat
|
||||
bordered
|
||||
style="min-width: 700px; max-width: 80vw; min-height: 10vh; max-height: 80vh;"
|
||||
>
|
||||
<q-card-section class="q-gutter-y-md column">
|
||||
<q-input
|
||||
v-model="category_name"
|
||||
:error="!!name_error.length"
|
||||
:error-message="name_error"
|
||||
square
|
||||
filled
|
||||
counter
|
||||
clearable
|
||||
maxlength="40"
|
||||
label="Name"
|
||||
type="text"
|
||||
></q-input>
|
||||
|
||||
<q-input
|
||||
class="col-10"
|
||||
v-model="category_description"
|
||||
square
|
||||
filled
|
||||
counter
|
||||
autogrow
|
||||
maxlength="500"
|
||||
label="Description"
|
||||
hint="Optional"
|
||||
type="text"
|
||||
>
|
||||
<template v-slot:after>
|
||||
<q-btn
|
||||
label="Preview"
|
||||
outline
|
||||
color="green"
|
||||
@click="markdown_preview = true"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-card-section>
|
||||
|
||||
<q-separator />
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn
|
||||
v-if="data.category"
|
||||
flat
|
||||
color="red"
|
||||
icon="delete"
|
||||
:label="delete_label"
|
||||
@click="deleteCategory()"
|
||||
:disabled="!delete_actually_enabled"
|
||||
>
|
||||
<q-tooltip v-if="has_goal_lists">
|
||||
Cannot delete category with goal lists
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
|
||||
<q-space/>
|
||||
|
||||
<q-btn
|
||||
flat
|
||||
color="red"
|
||||
icon="cancel"
|
||||
label="Cancel"
|
||||
@click="cancel()"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
color="green"
|
||||
icon="save"
|
||||
label="Save"
|
||||
@click="saveCategory()"
|
||||
:disabled="!canSave()"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
|
||||
<q-dialog v-model="markdown_preview">
|
||||
<q-card
|
||||
flat
|
||||
bordered
|
||||
>
|
||||
<q-card-section>
|
||||
<MarkdownRenderer :text="category_description"/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import BingoCategory from '@/js/lib/BingoCategory.ts';
|
||||
import {
|
||||
stringCompare
|
||||
} from '@/js/lib/Util.ts';
|
||||
|
||||
const emit = defineEmits([
|
||||
'cancel',
|
||||
'createCategory',
|
||||
'updateCategory',
|
||||
'deleteCategory'
|
||||
]);
|
||||
|
||||
const {
|
||||
data
|
||||
} = defineProps<{
|
||||
data: {
|
||||
reserved_names: string[],
|
||||
category?: BingoCategory,
|
||||
},
|
||||
}>();
|
||||
|
||||
const markdown_preview = ref(false);
|
||||
|
||||
const delete_label = ref('Delete (5)');
|
||||
const delete_enabled = ref(false);
|
||||
|
||||
const has_goal_lists = computed(() => {
|
||||
return data.category?.goal_lists?.length;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (has_goal_lists) {
|
||||
delete_label.value = 'Delete';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let countdown = 5;
|
||||
let interval = setInterval(() => {
|
||||
if (--countdown <= 0) {
|
||||
clearInterval(interval);
|
||||
delete_label.value = 'Delete';
|
||||
delete_enabled.value = true;
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
delete_label.value = `Delete (${ countdown })`;
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
const delete_actually_enabled = computed(() => {
|
||||
return delete_enabled.value && !has_goal_lists;
|
||||
});
|
||||
|
||||
const category_name = ref('');
|
||||
const category_description = ref('');
|
||||
|
||||
category_name.value = data.category?.name ?? '';
|
||||
category_description.value = data.category?.description ?? '';
|
||||
|
||||
const name_error = computed(() => {
|
||||
if (isNameReserved(category_name.value)) {
|
||||
return 'Name is reserved';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
function isNameReserved(name: string) {
|
||||
return name.length && data.reserved_names.some(reserved_name =>
|
||||
reserved_name !== data.category?.name
|
||||
&& stringCompare(reserved_name, name));
|
||||
}
|
||||
|
||||
function canSave() {
|
||||
return !isNameReserved(category_name.value);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
emit('cancel');
|
||||
}
|
||||
|
||||
function saveCategory() {
|
||||
const category = new BingoCategory(category_name.value);
|
||||
category.description = category_description.value;
|
||||
|
||||
// Create new category
|
||||
if (!data.category) {
|
||||
emit('createCategory', category);
|
||||
}
|
||||
// Update existing category
|
||||
else {
|
||||
category.goal_lists = data.category.goal_lists;
|
||||
|
||||
emit('updateCategory', data.category, category);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCategory() {
|
||||
emit('deleteCategory', data.category);
|
||||
}
|
||||
</script>
|
||||
|
@ -145,6 +145,14 @@
|
||||
@click="event => openEditCategoryDialog(prop.node, event)"
|
||||
/>
|
||||
<div>{{ prop.node.item?.name || prop.node.label }}</div>
|
||||
<q-btn
|
||||
v-if="prop.node.item?.description?.length"
|
||||
icon="help_outline"
|
||||
flat
|
||||
round
|
||||
size="sm"
|
||||
@click="event => openInfoDialog(prop.node.item?.description, event)"
|
||||
></q-btn>
|
||||
<q-badge
|
||||
outline
|
||||
color="blue"
|
||||
@ -312,6 +320,16 @@
|
||||
@delete-goal-list="delete_goal_list"
|
||||
/>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="edit_category_dialog" persistent>
|
||||
<EditCategoryDialog
|
||||
:data="edit_category_data"
|
||||
@cancel="edit_category_dialog = false"
|
||||
@create-category="create_category"
|
||||
@update-category="update_category"
|
||||
@delete-category="delete_category"
|
||||
/>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -573,10 +591,12 @@ const delete_goal_list = (goal_list: BingoGoalList) => {
|
||||
/* Edit Category Dialog */
|
||||
|
||||
interface EditCategoryData {
|
||||
reserved_names: string[];
|
||||
category?: BingoCategory;
|
||||
}
|
||||
|
||||
const edit_category_data = ref<EditCategoryData>({
|
||||
reserved_names: [] as string[]
|
||||
});
|
||||
|
||||
const edit_category_dialog = ref(false);
|
||||
@ -589,19 +609,23 @@ function openEditCategoryDialog(node: Node, event: Event) {
|
||||
edit_category_dialog.value = true;
|
||||
}
|
||||
|
||||
const emit_category = (category: BingoCategory) => {
|
||||
// Add new category
|
||||
if (!edit_category_data.value.category) {
|
||||
const create_category = (category: BingoCategory) => {
|
||||
game.value?.items.push(category);
|
||||
}
|
||||
// Replace existing category
|
||||
else {
|
||||
const index = game.value?.items.findIndex(g => g === category);
|
||||
game.value?.items.splice(index, 1, category);
|
||||
}
|
||||
|
||||
// Reset values
|
||||
edit_category_data.value = {
|
||||
reserved_names: []
|
||||
};
|
||||
edit_category_dialog.value = false;
|
||||
};
|
||||
|
||||
const update_category = (old_category: BingoCategory, category: BingoCategory) => {
|
||||
const index = game.value?.items.findIndex(g => g === old_category);
|
||||
game.value?.items.splice(index, 1, category);
|
||||
|
||||
// Reset values
|
||||
edit_category_data.value = {
|
||||
reserved_names: []
|
||||
};
|
||||
edit_category_dialog.value = false;
|
||||
};
|
||||
@ -612,10 +636,40 @@ const delete_category = (category: BingoCategory) => {
|
||||
|
||||
// Reset values
|
||||
edit_category_data.value = {
|
||||
reserved_names: []
|
||||
};
|
||||
edit_category_dialog.value = false;
|
||||
};
|
||||
|
||||
// const emit_category = (category: BingoCategory) => {
|
||||
// // Add new category
|
||||
// if (!edit_category_data.value.category) {
|
||||
// game.value?.items.push(category);
|
||||
// }
|
||||
// // Replace existing category
|
||||
// else {
|
||||
// const index = game.value?.items.findIndex(g => g === category);
|
||||
// game.value?.items.splice(index, 1, category);
|
||||
// }
|
||||
|
||||
// // Reset values
|
||||
// edit_category_data.value = {
|
||||
// reserved_names: []
|
||||
// };
|
||||
// edit_category_dialog.value = false;
|
||||
// };
|
||||
|
||||
// const delete_category = (category: BingoCategory) => {
|
||||
// const index = game.value?.items.findIndex(g => g === category);
|
||||
// game.value?.items.splice(index, 1);
|
||||
|
||||
// // Reset values
|
||||
// edit_category_data.value = {
|
||||
// reserved_names: []
|
||||
// };
|
||||
// edit_category_dialog.value = false;
|
||||
// };
|
||||
|
||||
/* End Edit Category Dialog */
|
||||
|
||||
const nodes = computed(() => {
|
||||
|
@ -8,6 +8,8 @@ import {
|
||||
export default class BingoCategory {
|
||||
name: string;
|
||||
|
||||
description: string = '';
|
||||
|
||||
@Type(() => BingoGoalList)
|
||||
goal_lists: BingoGoalList[] = [];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user