Skip to content

Commit b4aabfa

Browse files
authored
feat: open modal on category creation (#293)
1 parent 889a911 commit b4aabfa

File tree

5 files changed

+173
-121
lines changed

5 files changed

+173
-121
lines changed

src/components/CategoryEditModal.vue

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<template lang="pug">
2+
// The category edit modal
3+
b-modal(id="edit" ref="edit" title="Edit category" @show="resetModal" @hidden="hidden" @ok="handleOk")
4+
div.my-1
5+
b-input-group.my-1(prepend="Name")
6+
b-form-input(v-model="editing.name")
7+
b-input-group(prepend="Parent")
8+
b-select(v-model="editing.parent", :options="allCategories")
9+
10+
hr
11+
div.my-1
12+
b Rule
13+
b-input-group.my-1(prepend="Type")
14+
b-select(v-model="editing.rule.type", :options="allRuleTypes")
15+
div(v-if="editing.rule.type === 'regex'")
16+
b-input-group.my-1(prepend="Pattern")
17+
b-form-input(v-model="editing.rule.regex")
18+
b-form-checkbox(v-model="editing.rule.ignore_case" switch)
19+
| Ignore case
20+
21+
hr
22+
div.my-1
23+
b Color
24+
25+
b-form-checkbox(v-model="editing.inherit_color" switch)
26+
| Inherit parent color
27+
div.mt-1(v-show="!editing.inherit_color")
28+
color-picker(v-model="editing.color")
29+
30+
//
31+
div.my-1
32+
b Productivity score
33+
b-input-group.my-1(prepend="Points")
34+
b-form-input(v-model="editing.productivity")
35+
36+
hr
37+
div.my-1
38+
b-btn(variant="danger", @click="removeClass(categoryId); $refs.edit.hide()")
39+
icon(name="trash")
40+
| Remove category
41+
</template>
42+
43+
<script>
44+
import _ from 'lodash';
45+
import ColorPicker from '~/components/ColorPicker';
46+
47+
export default {
48+
name: 'CategoryEditModal',
49+
components: {
50+
'color-picker': ColorPicker,
51+
},
52+
props: {
53+
categoryId: { type: Number, required: true },
54+
},
55+
data: function () {
56+
return {
57+
editing: {
58+
id: 0, // FIXME: Use ID assigned to category in vuex store, in order for saves to be uniquely targeted
59+
name: null,
60+
rule: {},
61+
parent: [],
62+
inherit_color: true,
63+
color: null,
64+
},
65+
};
66+
},
67+
computed: {
68+
allCategories: function () {
69+
const categories = this.$store.getters['categories/all_categories'];
70+
const entries = categories.map(c => {
71+
return { text: c.join('->'), value: c };
72+
});
73+
return [{ value: [], text: 'None' }].concat(entries);
74+
},
75+
allRuleTypes: function () {
76+
return [
77+
{ value: null, text: 'None' },
78+
{ value: 'regex', text: 'Regular Expression' },
79+
//{ value: 'glob', text: 'Glob pattern' },
80+
];
81+
},
82+
},
83+
watch: {
84+
categoryId: function (new_value) {
85+
if (new_value !== null) {
86+
this.showModal();
87+
}
88+
},
89+
},
90+
mounted: function () {
91+
if (this.categoryId !== null) {
92+
this.showModal();
93+
}
94+
},
95+
methods: {
96+
showModal() {
97+
this.$refs.edit.show();
98+
},
99+
hidden() {
100+
this.$emit('hidden');
101+
},
102+
removeClass() {
103+
// TODO: Show a confirmation dialog
104+
// TODO: Remove children as well?
105+
this.$store.commit('categories/removeClass', this.categoryId);
106+
},
107+
checkFormValidity() {
108+
// FIXME
109+
return true;
110+
},
111+
handleOk(event) {
112+
// Prevent modal from closing
113+
event.preventDefault();
114+
// Trigger submit handler
115+
this.handleSubmit();
116+
},
117+
handleSubmit() {
118+
// Exit when the form isn't valid
119+
if (!this.checkFormValidity()) {
120+
return;
121+
}
122+
123+
// Save the category
124+
const new_class = {
125+
id: this.editing.id,
126+
name: this.editing.parent.concat(this.editing.name),
127+
rule: this.editing.rule.type !== null ? this.editing.rule : { type: null },
128+
data: { color: this.editing.inherit_color === true ? undefined : this.editing.color },
129+
};
130+
this.$store.commit('categories/updateClass', new_class);
131+
132+
// Hide the modal manually
133+
this.$nextTick(() => {
134+
this.$refs.edit.hide();
135+
});
136+
},
137+
resetModal() {
138+
const cat = this.$store.getters['categories/get_category_by_id'](this.categoryId);
139+
const color = cat.data ? cat.data.color : undefined;
140+
const inherit_color = !color;
141+
this.editing = {
142+
id: cat.id,
143+
name: cat.subname,
144+
rule: _.cloneDeep(cat.rule),
145+
color,
146+
inherit_color,
147+
parent: cat.parent ? cat.parent : [],
148+
};
149+
},
150+
},
151+
};
152+
</script>

src/components/CategoryEditTree.vue

Lines changed: 14 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -16,55 +16,16 @@ div
1616
span(v-if="_class.rule.type === 'regex'") Rule ({{_class.rule.type}}): #[code {{_class.rule.regex}}]
1717
span(v-else, style="color: #888") No rule
1818
span.float-right
19-
b-btn.ml-1(size="sm", variant="outline-secondary", @click="showEditModal()" style="border: 0;" pill)
19+
b-btn.ml-1(size="sm", variant="outline-secondary", @click="showEditModal(_class.id)" style="border: 0;" pill)
2020
icon(name="edit")
2121
b-btn.ml-1(size="sm", variant="outline-success", @click="addSubclass(_class); expanded = true" style="border: 0;" pill)
2222
icon(name="plus")
2323
div
2424
div.pa-2(v-for="child in _class.children", style="background: rgba(0, 0, 0, 0);", v-show="expanded")
2525
CategoryEditTree(:_class="child", :depth="depth+1")
2626

27-
b-modal(id="edit" ref="edit" title="Edit category" @show="resetModal" @hidden="resetModal" @ok="handleOk")
28-
div.my-1
29-
b-input-group.my-1(prepend="Name")
30-
b-form-input(v-model="editing.name")
31-
b-input-group(prepend="Parent")
32-
b-select(v-model="editing.parent", :options="allCategories")
33-
34-
hr
35-
36-
div.my-1
37-
b Rule
38-
b-input-group.my-1(prepend="Type")
39-
b-select(v-model="editing.rule.type", :options="allRuleTypes")
40-
div(v-if="editing.rule.type === 'regex'")
41-
b-input-group.my-1(prepend="Pattern")
42-
b-form-input(v-model="editing.rule.regex")
43-
b-form-checkbox(v-model="editing.rule.ignore_case" switch)
44-
| Ignore case
45-
46-
hr
47-
48-
div.my-1
49-
b Color
50-
51-
b-form-checkbox(v-model="editing.inherit_color" switch)
52-
| Inherit parent color
53-
div.mt-1(v-show="!editing.inherit_color")
54-
color-picker(v-model="editing.color")
55-
56-
//
57-
div.my-1
58-
b Productivity score
59-
b-input-group.my-1(prepend="Points")
60-
b-form-input(v-model="editing.productivity")
61-
62-
hr
63-
64-
div.my-1
65-
b-btn(variant="danger", @click="removeClass(_class); $refs.edit.hide()")
66-
icon(name="trash")
67-
| Remove category
27+
div(v-if="editingId !== null")
28+
CategoryEditModal(:categoryId='editingId', @hidden="hideEditModal()")
6829
</template>
6930

7031
<script>
@@ -76,14 +37,14 @@ import 'vue-awesome/icons/trash';
7637
import 'vue-awesome/icons/plus';
7738
import 'vue-awesome/icons/edit';
7839
79-
import ColorPicker from '~/components/ColorPicker';
40+
import CategoryEditModal from './CategoryEditModal.vue';
8041
8142
import _ from 'lodash';
8243
8344
export default {
8445
name: 'CategoryEditTree',
8546
components: {
86-
'color-picker': ColorPicker,
47+
CategoryEditModal: CategoryEditModal,
8748
},
8849
props: {
8950
_class: Object,
@@ -95,32 +56,10 @@ export default {
9556
data: function () {
9657
return {
9758
expanded: this.depth < 1,
98-
color_focused: false,
99-
editing: {
100-
id: 0, // FIXME: Use ID assigned to category in vuex store, in order for saves to be uniquely targeted
101-
name: null,
102-
rule: {},
103-
parent: [],
104-
inherit_color: true,
105-
color: null,
106-
},
59+
editingId: null,
10760
};
10861
},
10962
computed: {
110-
allCategories: function () {
111-
const categories = this.$store.getters['categories/all_categories'];
112-
const entries = categories.map(c => {
113-
return { text: c.join('->'), value: c };
114-
});
115-
return [{ value: [], text: 'None' }].concat(entries);
116-
},
117-
allRuleTypes: function () {
118-
return [
119-
{ value: null, text: 'None' },
120-
{ value: 'regex', text: 'Regular Expression' },
121-
//{ value: 'glob', text: 'Glob pattern' },
122-
];
123-
},
12463
totalChildren: function () {
12564
function countChildren(node) {
12665
return node.children.length + _.sum(_.map(node.children, countChildren));
@@ -134,58 +73,16 @@ export default {
13473
name: parent.name.concat(['New class']),
13574
rule: { type: 'regex', regex: 'FILL ME' },
13675
});
137-
},
138-
removeClass: function (_class) {
139-
// TODO: Show a confirmation dialog
140-
// TODO: Remove children as well?
141-
// TODO: Move button to edit modal?
142-
this.$store.commit('categories/removeClass', _class);
143-
},
144-
showEditModal() {
145-
this.$refs.edit.show();
146-
},
147-
checkFormValidity() {
148-
// FIXME
149-
return true;
150-
},
151-
handleOk(event) {
152-
// Prevent modal from closing
153-
event.preventDefault();
154-
// Trigger submit handler
155-
this.handleSubmit();
156-
},
157-
handleSubmit() {
158-
// Exit when the form isn't valid
159-
if (!this.checkFormValidity()) {
160-
return;
161-
}
162-
163-
// Save the category
164-
const new_class = {
165-
id: this.editing.id,
166-
name: this.editing.parent.concat(this.editing.name),
167-
rule: this.editing.rule.type !== null ? this.editing.rule : { type: null },
168-
data: { color: this.editing.inherit_color === true ? undefined : this.editing.color },
169-
};
170-
this.$store.commit('categories/updateClass', new_class);
17176
172-
// Hide the modal manually
173-
this.$nextTick(() => {
174-
this.$refs.edit.hide();
175-
});
77+
// Find the category with the max ID, and open an editor for it
78+
const lastId = _.max(_.map(this.$store.state.categories.classes, 'id'));
79+
this.editingId = lastId;
80+
},
81+
showEditModal: function () {
82+
this.editingId = this._class.id;
17683
},
177-
resetModal() {
178-
const color = this._class.data ? this._class.data.color : undefined;
179-
const inherit_color = !color;
180-
this.editing = {
181-
id: this._class.id,
182-
name: this._class.subname,
183-
rule: _.cloneDeep(this._class.rule),
184-
color,
185-
inherit_color,
186-
parent: this._class.parent ? this._class.parent : [],
187-
};
188-
//console.log(this.editing);
84+
hideEditModal: function () {
85+
this.editingId = null;
18986
},
19087
},
19188
};

src/components/ColorPicker.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ export default {
6161
},
6262
},
6363
mounted() {
64-
console.log('setting color', this.value);
6564
this.setColor(this.value);
6665
},
6766
methods: {

src/store/modules/categories.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
defaultCategories,
66
build_category_hierarchy,
77
createMissingParents,
8+
annotate,
89
} from '~/util/classes';
910

1011
// initial state
@@ -37,6 +38,9 @@ const getters = {
3738
get_category: state => category_arr => {
3839
return state.classes.find(c => _.isEqual(c.name, category_arr));
3940
},
41+
get_category_by_id: state => id => {
42+
return annotate(_.cloneDeep(state.classes.find(c => c.id == id)));
43+
},
4044
};
4145

4246
// actions
@@ -95,8 +99,8 @@ const mutations = {
9599
state.classes.push(new_class);
96100
state.classes_unsaved_changes = true;
97101
},
98-
removeClass(state, cls) {
99-
state.classes = state.classes.filter(c => c.id !== cls.id);
102+
removeClass(state, classId) {
103+
state.classes = state.classes.filter(c => c.id !== classId);
100104
state.classes_unsaved_changes = true;
101105
},
102106
restoreDefaultClasses(state) {

src/util/classes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const defaultCategories: Category[] = [
7878
{ name: ['Uncategorized'], rule: { type: null }, data: { color: COLOR_UNCAT } },
7979
];
8080

81-
function annotate(c: Category) {
81+
export function annotate(c: Category) {
8282
const ch = c.name;
8383
c.name_pretty = ch.join(level_sep);
8484
c.subname = ch.slice(-1)[0];

0 commit comments

Comments
 (0)