Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Simplification and Optimization of Mod Management UI #1226

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions src/components/views/LocalModList/SearchAndSort.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import ManagerSettings from '../../../r2mm/manager/ManagerSettings';
export default class SearchAndSort extends Vue {
settings = new ManagerSettings();

toggleSortingDirection() {
this.direction = this.direction === SortDirection.STANDARD ? SortDirection.REVERSE : SortDirection.STANDARD;
}

get order() {
return this.$store.state.profile.order;
}
Expand Down Expand Up @@ -96,22 +100,19 @@ export default class SearchAndSort extends Vue {

<div class="input-group margin-right">
<label for="local-sort-order" class="non-selectable">Sort</label>
<select
v-model="order"
id="local-sort-order"
class="select select--content-spacing margin-right margin-right--half-width">
<option v-for="(option) in orderOptions" :key="`order-option-${option}`">
{{option}}
</option>
</select>
<select
v-model="direction"
id="local-sort-direction"
class="select select--content-spacing">
<option v-for="(option) in directionOptions" :key="`direction-option-${option}`">
{{option}}
</option>
</select>
<div class="sort-container"> <!-- This div wraps the selector and the icon -->
<select
v-model="order"
id="local-sort-order"
class="select select--content-spacing margin-right margin-right--half-width">
<option v-for="(option) in orderOptions" :key="`order-option-${option}`">
{{ option }}
</option>
</select>
<div class="icon-container" @click="toggleSortingDirection" :style="{ cursor: 'pointer' }"> <!-- New div to center the icon vertically -->
<i :class="['fas', 'fa-caret-up', 'sorting-icon', { 'rotated': direction === 'Reverse' }]" class="icon--margin-right"></i>
</div>
</div>
</div>

<div class="input-group">
Expand Down
99 changes: 63 additions & 36 deletions src/components/views/OnlineModView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,33 @@
</div>
<div class="input-group margin-right">
<label for="thunderstore-sort">Sort</label>
<select v-model="sortingStyleModel"
id="thunderstore-sort"
class="select select--content-spacing margin-right margin-right--half-width"
>
<option v-for="(key) in getSortOptions()" v-bind:key="key">{{key}}</option>
</select>
<select v-model="sortingDirectionModel"
class="select select--content-spacing"
:disabled="sortingStyleModel === 'Default'"
>
<option v-for="(key) in getSortDirections()" v-bind:key="key">{{key}}</option>
</select>
</div>
<div class="input-group">
<div class="input-group input-group--flex">
<label for="thunderstore-category-filter">Additional filters</label>
<button
id="thunderstore-category-filter"
class="button"
@click="$store.commit('openCategoryFilterModal')"
<div class="sort-container"> <!-- This div wraps the selector and the icon -->
<select v-model="sortingStyleModel"
id="thunderstore-sort"
class="select select--content-spacing margin-right--half-width"
@change="sortThunderstoreModList"
>
Filter categories
</button>
<option v-for="(key) in getSortOptions()" :key="key">{{ key }}</option>
</select>
<div class="icon-container"> <!-- New div to center the icon vertically -->
<i :class="['fas', 'fa-caret-up', 'sorting-icon', { 'rotated': sortingDirectionModel === 'REVERSE' }]"
@click="toggleSortingDirection"
class="icon--margin-right"
:style="{ cursor: sortingStyleModel !== 'Default' ? 'pointer' : 'not-allowed' }"
></i>
</div>
</div>
</div>
<div class="input-group category-filter-container">
<label for="thunderstore-category-filter">Filters</label>
<button
id="thunderstore-category-filter"
class="button category-filter"
@click="$store.commit('openCategoryFilterModal')"
>
<i class="fas fa-filter" />
</button>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -135,6 +137,13 @@ export default class OnlineModView extends Vue {
this.filterThunderstoreModList();
}

@Watch('sortingDirectionModel')
onSortingDirectionChanged(newVal: string, oldVal: string) {
if (newVal !== oldVal && this.sortingStyleModel !== 'Default') {
this.sortThunderstoreModList();
}
}

@Watch("$store.state.modFilters.allowNsfw")
@Watch("$store.state.modFilters.categoryFilterMode")
@Watch("$store.state.modFilters.selectedCategories")
Expand Down Expand Up @@ -177,32 +186,40 @@ export default class OnlineModView extends Vue {
@Watch("sortingStyleModel")
@Watch("thunderstoreModList")
sortThunderstoreModList() {
const sortDescending = this.sortingDirectionModel == SortingDirection.STANDARD;
const sortAscending = this.sortingDirectionModel !== 'STANDARD';
const sortedList = [...this.thunderstoreModList];
sortedList.sort((a: ThunderstoreMod, b: ThunderstoreMod) => {
let result: boolean;
// Initialize result with a default value
let result = 0;

switch (this.sortingStyleModel) {
case SortingStyle.LAST_UPDATED:
result = sortDescending ? a.getDateUpdated() < b.getDateUpdated() : a.getDateUpdated() > b.getDateUpdated();
break;
case SortingStyle.ALPHABETICAL:
result = sortDescending ? a.getName().localeCompare(b.getName()) > 0 : a.getName().localeCompare(b.getName()) < 0;
// Make sure you have Date objects before calling getTime
const dateA = a.getDateUpdated() instanceof Date ? a.getDateUpdated() : new Date(a.getDateUpdated());
const dateB = b.getDateUpdated() instanceof Date ? b.getDateUpdated() : new Date(b.getDateUpdated());
result = (sortAscending ? dateA.getTime() - dateB.getTime() : dateB.getTime() - dateA.getTime());
break;
case SortingStyle.DOWNLOADS:
result = sortDescending ? a.getDownloadCount() < b.getDownloadCount() : a.getDownloadCount() > b.getDownloadCount();
// Make sure you have numbers before comparing
const downloadsA = Number(a.getDownloadCount());
const downloadsB = Number(b.getDownloadCount());
result = (sortAscending ? downloadsA - downloadsB : downloadsB - downloadsA);
break;
case SortingStyle.RATING:
result = sortDescending ? a.getRating() < b.getRating() : a.getRating() > b.getRating();
break;
case SortingStyle.DEFAULT:
result = true;
// Make sure you have numbers before comparing
const ratingA = Number(a.getRating());
const ratingB = Number(b.getRating());
result = (sortAscending ? ratingA - ratingB : ratingB - ratingA);
break;
default:
result = true;
case SortingStyle.ALPHABETICAL:
result = a.getName().localeCompare(b.getName()) * (sortAscending ? -1 : 1);
break;
// No need for a default case if all cases are covered
}
return result ? 1 : -1;

return result;
});

this.sortedThunderstoreModList = sortedList;
this.filterThunderstoreModList();
}
Expand All @@ -217,7 +234,17 @@ export default class OnlineModView extends Vue {
}

async created() {
this.sortingDirectionModel = 'STANDARD'; // Ensuring the default sort is standard when component is created
this.sortThunderstoreModList();
}

toggleSortingDirection() {
if (this.sortingStyleModel !== 'Default') {
this.sortingDirectionModel = this.sortingDirectionModel === 'STANDARD' ? 'REVERSE' : 'STANDARD';
// Adding a key to force Vue to re-render the icon
this.$forceUpdate(); // This will force the entire component to update, which can be helpful if Vue is not reactive enough
this.sortThunderstoreModList();
}
}
};
</script>
34 changes: 34 additions & 0 deletions src/css/custom.scss
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,37 @@ code {
border-radius: 8px;
background-color: rgba(0, 0, 0, 0.15);
}

.category-filter:focus {
color: #fff;
}

.category-filter-container {
display: flex;
flex-direction: column;
align-items: flex-start;
}

.category-filter {
align-self: flex-end; /* This aligns the button to the right */
margin-left: auto; /* This also pushes the button to the right */
}

.sort-container {
display: flex;
align-items: center; /* This centers the selector and the icon vertically */
}

.icon-container {
display: flex;
align-items: center; /* This centers the icon within the div */
margin-left: 0.5em;
}

.sorting-icon {
transition: transform 0.3s ease;
}

.rotated {
transform: rotate(180deg);
}