mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-13 12:47:28 +01:00
Tags redesign in edit-task modal (#9122)
* Truncate tags list to maximum number of tasks This commit truncates the list of tags in the edit task modal and displays the remaining selected tasks as a number. * Align tags-select dropdown with "Tags" label * Add tags popup component * Use solid purple for tags-select dropdown * Remove shadow when tags-select is active * Add border-radius to tags-select * Re-add previously disabled transitions * Remove unused template element * Add Clear Tags button to footer of tags popup * Decrease column size for tags to better match design * Truncate tag name to avoid overflows * Add tag name as title to show full name on hover * Grow inline tags select from left to right * Style none button * Add spacing to streak reset button to line up with tags select * Add top offset to tags dropdown toggle to line up with label
This commit is contained in:
committed by
Sabe Jones
parent
d977656e96
commit
c4e5633e48
107
website/client/components/tasks/tagsPopup.vue
Normal file
107
website/client/components/tasks/tagsPopup.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template lang="pug">
|
||||
.tags-popup
|
||||
.tags-category.d-flex
|
||||
.tags-header
|
||||
strong(v-once) {{ $t('tags') }}
|
||||
.tags-list.container
|
||||
.row
|
||||
.col-4(v-for="tag in tags")
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="selectedTags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(:title='tag.name') {{tag.name}}
|
||||
.tags-footer
|
||||
span.clear-tags(@click="clearTags()") {{$t("clearTags")}}
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.tags-popup {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
max-width: 593px;
|
||||
z-index: 9999;
|
||||
background: $white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 2px 0 rgba($black, 0.16), 0 1px 4px 0 rgba($black, 0.12);
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.tags-category {
|
||||
border-bottom: 1px solid $gray-600;
|
||||
padding-bottom: 24px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.tags-header {
|
||||
flex-basis: 96px;
|
||||
flex-shrink: 0;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
line-height: 1.33;
|
||||
color: $blue-10;
|
||||
margin-top: 4px;
|
||||
|
||||
&:focus, &:hover, &:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-list {
|
||||
.custom-control-description {
|
||||
color: $gray-50 !important;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre;
|
||||
width: 8em;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-footer {
|
||||
border-top: 1px solid $gray-600;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.clear-tags {
|
||||
cursor: pointer;
|
||||
margin: 1.1em 0;
|
||||
color: $red-50;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['tags', 'value'],
|
||||
data () {
|
||||
return {
|
||||
selectedTags: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
selectedTags () {
|
||||
this.$emit('input', this.selectedTags);
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.selectedTags = this.value;
|
||||
},
|
||||
methods: {
|
||||
clearTags () {
|
||||
this.selectedTags = [];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -110,29 +110,22 @@
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description {{ $t('dayOfWeek') }}
|
||||
|
||||
.option(v-if="isUserTask")
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0') {{$t('none')}}
|
||||
span.category-select(v-else)
|
||||
.category-label(v-for='tagName in getTagsFor(task)') {{tagName}}
|
||||
.category-box(v-if="showTagsSelect")
|
||||
.container
|
||||
.row
|
||||
.form-check.col-6(
|
||||
v-for="tag in user.tags",
|
||||
:key="tag.id",
|
||||
)
|
||||
label.custom-control.custom-checkbox
|
||||
input.custom-control-input(type="checkbox", :value="tag.id", v-model="task.tags")
|
||||
span.custom-control-indicator
|
||||
span.custom-control-description(v-once) {{ tag.name }}
|
||||
.row
|
||||
button.btn.btn-primary(@click="showTagsSelect = !showTagsSelect") {{$t('close')}}
|
||||
.tags-select.option(v-if="isUserTask")
|
||||
.tags-inline
|
||||
label(v-once) {{ $t('tags') }}
|
||||
.category-wrap(@click="showTagsSelect = !showTagsSelect", v-bind:class="{ active: showTagsSelect }")
|
||||
span.category-select(v-if='task.tags && task.tags.length === 0')
|
||||
.tags-none {{$t('none')}}
|
||||
.dropdown-toggle
|
||||
span.category-select(v-else)
|
||||
.category-label(v-for='tagName in truncatedSelectedTags', :title="tagName") {{ tagName }}
|
||||
.tags-more(v-if='remainingSelectedTags.length > 0') +{{ $t('more', { count: remainingSelectedTags.length }) }}
|
||||
.dropdown-toggle
|
||||
tags-popup(v-if="showTagsSelect", :tags="user.tags", v-model="task.tags")
|
||||
|
||||
.option(v-if="task.type === 'habit'")
|
||||
label(v-once) {{ $t('resetStreak') }}
|
||||
b-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown.streak-dropdown(:text="$t(task.frequency)")
|
||||
b-dropdown-item(v-for="frequency in ['daily', 'weekly', 'monthly']", :key="frequency", @click="task.frequency = frequency", :class="{active: task.frequency === frequency}")
|
||||
| {{ $t(frequency) }}
|
||||
|
||||
@@ -328,6 +321,88 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tags-select {
|
||||
position: relative;
|
||||
|
||||
.tags-inline {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.category-wrap {
|
||||
cursor: inherit;
|
||||
position: relative;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
margin-left: 4em;
|
||||
|
||||
&.active {
|
||||
border-color: $purple-500;
|
||||
|
||||
.category-select {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.category-select {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: .6em;
|
||||
padding-right: 2.8em;
|
||||
width: 100%;
|
||||
|
||||
.tags-none {
|
||||
margin: .26em 0 .26em .6em;
|
||||
|
||||
& + .dropdown-toggle {
|
||||
right: 1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-more {
|
||||
color: #a5a1ac;
|
||||
flex: 0 1 auto;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
left: .5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
top: .8em;
|
||||
}
|
||||
|
||||
.category-label {
|
||||
min-width: 68px;
|
||||
overflow: hidden;
|
||||
padding: .5em 1em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 68px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags-popup {
|
||||
position: absolute;
|
||||
top: 3.5em;
|
||||
left: 6.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.streak-dropdown {
|
||||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.checklist-group {
|
||||
border-top: 1px solid $gray-500;
|
||||
}
|
||||
@@ -418,6 +493,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import TagsPopup from './tagsPopup';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import { mapGetters, mapActions, mapState } from 'client/libs/store';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
@@ -441,6 +517,7 @@ import goldIcon from 'assets/svg/gold.svg';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TagsPopup,
|
||||
bModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
@@ -453,6 +530,7 @@ export default {
|
||||
props: ['task', 'purpose', 'challengeId', 'groupId'], // purpose is either create or edit, task is the task created or edited
|
||||
data () {
|
||||
return {
|
||||
maxTags: 3,
|
||||
showTagsSelect: false,
|
||||
showAssignedSelect: false,
|
||||
newChecklistItem: null,
|
||||
@@ -574,6 +652,15 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
selectedTags () {
|
||||
return this.getTagsFor(this.task);
|
||||
},
|
||||
truncatedSelectedTags () {
|
||||
return this.selectedTags.slice(0, this.maxTags);
|
||||
},
|
||||
remainingSelectedTags () {
|
||||
return this.selectedTags.slice(this.maxTags);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions({saveTask: 'tasks:save', destroyTask: 'tasks:destroy', createTask: 'tasks:create'}),
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"subscriberItemText": "Each month, subscribers will receive a mystery item. This is usually released about one week before the end of the month. See the wiki's 'Mystery Item' page for more information.",
|
||||
"all": "All",
|
||||
"none": "None",
|
||||
"more": "<%= count %> more",
|
||||
"and": "and",
|
||||
"loginSuccess": "Login successful!",
|
||||
"youSure": "Are you sure?",
|
||||
|
||||
Reference in New Issue
Block a user