Files
habitica/website/client/src/components/ui/selectList.vue
T
Fiz a8062ad615 Implement stat allocation & auto stat allocation (#15551)
* fix(responsive): adjustments for small screens by @Hafizzle

* Fixed cut off modal issues

- Fixed limited time banner being cut off on smaller devices
- Fixed Pin on purchase quest modal being cut off

* Update order of views on Stats tab

* Update styling of stat boxes & add stat allocation info

* Live stat allocation & auto allocation implemented

- Update stat allocation UI/UX cont.
- Update stats live (versus pressing save)
- Dynamically show the distribute dropdown based on if auto allocate switch is enabled

* Remove duplicate string value, convert line endings

* Fix auto allocation toggle alignment

* lint fixes

* Auto allocate UI fixes

- remove info icon in each dropdown option, and replace info below each option
- disable auto allocate dropdown instead of hiding it
- removed radio buttons drop auto allocate options dropdown
- Points available & auto allocate text share same baseline now

* Stat allocation updates

* Assigned Stat

* lint fixes

* stat feedback UI updates

* tweak

* stat allocation UI tweaks

refactor allocation mode selection with selectList component & other UI tweaks

* Fix hover dropdown padding & stat allocation chip always showing

* Update stat allocation dropdown styling

* Remove bold text on hover

* Stat allocation by task dropdown

* remove empty lines

* show selected attribute on component itself

* task stat selection UI updates

* fix(cr): remove weird line endings

---------

Co-authored-by: Kalista Payne <kalista@habitica.com>
2026-01-06 16:08:34 -06:00

170 lines
3.5 KiB
Vue

<template>
<div>
<b-dropdown
class="select-list"
:class="{'inline-dropdown':inlineDropdown}"
:toggle-class="isOpened ? 'active' : null"
:disabled="disabled"
:right="right"
@show="isOpened = true"
@hide="isOpened = false"
>
<template #button-content>
<slot
name="item"
:item="selectedItem || placeholder"
:button="true"
>
<!-- Fallback content -->
{{ value }}
</slot>
</template>
<b-dropdown-item
v-for="item in items"
:key="getKeyProp(item)"
:disabled="isDisabled(item)"
:active="isSelected(item)"
:class="{
active: isSelected(item),
selectListItem: true,
showIcon: !hideIcon && isSelected(item)
}"
@click="selectItem(item)"
>
<slot
name="item"
:item="item"
:button="false"
>
<!-- Fallback content -->
{{ item }}
</slot>
<div
v-once
class="svg-icon color check-icon"
v-html="icons.check"
></div>
</b-dropdown-item>
</b-dropdown>
</div>
</template>
<style lang="scss" scoped>
@import '@/assets/scss/colors.scss';
.select-list ::v-deep {
.dropdown-toggle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 25px; /* To allow enough room for the down arrow to be displayed */
}
.selectListItem {
position: relative;
.dropdown-item {
display: flex;
align-items: center;
justify-content: space-between;
}
&:not(.showIcon) {
.svg-icon.check-icon {
display: none;
}
}
.svg-icon.check-icon.color {
margin-left: 10px; /* So the flex item (checkmark) will have some spacing from the text */
width: 0.77rem;
height: 0.615rem;
color: $purple-300;
}
}
}
</style>
<script>
import svgCheck from '@/assets/svg/check.svg?raw';
export default {
props: {
items: {
type: Array,
},
disabled: {
type: Boolean,
},
value: [String, Number, Object],
keyProp: {
type: String,
},
activeKeyProp: {
type: String,
},
directSelect: {
type: Boolean,
default: false,
},
disabledProp: {
type: String,
},
hideIcon: {
type: Boolean,
},
right: {
type: Boolean,
},
inlineDropdown: {
type: Boolean,
default: true,
},
placeholder: {
type: String,
},
},
data () {
return {
isOpened: false,
selected: this.value,
icons: Object.freeze({
check: svgCheck,
}),
};
},
computed: {
selectedItem () {
if (this.activeKeyProp) {
return this.items.find(item => item[this.activeKeyProp] === this.selected);
}
return this.items.find(item => item === this.selected);
},
},
methods: {
getKeyProp (item) {
return this.keyProp ? item[this.keyProp] : item.key || item.identifier;
},
isDisabled (item) {
return typeof item[this.disabledProp] === 'undefined' ? false : item[this.disabledProp];
},
selectItem (item) {
if (this.directSelect) {
this.selected = item;
} else {
this.selected = this.getKeyProp(item);
}
this.$emit('select', item);
},
isSelected (item) {
if (this.activeKeyProp) {
return item[this.activeKeyProp] === this.selected;
}
return item === this.selected;
},
},
};
</script>