You've already forked AstralRinth
forked from didirus/AstralRinth
Library improvements (#126)
* Base impl * Add grouping * Fix some styling things * Run linter * add missing features * add dev mode --------- Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
<script setup>
|
||||
import Instance from '@/components/ui/Instance.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { SearchIcon, DropdownSelect, Card, formatCategoryHeader } from 'omorphia'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const props = defineProps({
|
||||
instances: {
|
||||
@@ -13,16 +16,143 @@ const props = defineProps({
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const search = ref('')
|
||||
const group = ref('Category')
|
||||
const filters = ref('All profiles')
|
||||
const sortBy = ref('Name')
|
||||
|
||||
const filteredResults = computed(() => {
|
||||
let instances = props.instances.filter((instance) => {
|
||||
return instance.metadata.name.toLowerCase().includes(search.value.toLowerCase())
|
||||
})
|
||||
|
||||
if (sortBy.value === 'Name') {
|
||||
instances.sort((a, b) => {
|
||||
return a.metadata.name.localeCompare(b.metadata.name)
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Game version') {
|
||||
instances.sort((a, b) => {
|
||||
return a.metadata.name.localeCompare(b.metadata.game_version)
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Last played') {
|
||||
instances.sort((a, b) => {
|
||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Date created') {
|
||||
instances.sort((a, b) => {
|
||||
return dayjs(b.metadata.date_created).diff(dayjs(a.metadata.date_created))
|
||||
})
|
||||
}
|
||||
|
||||
if (sortBy.value === 'Date modified') {
|
||||
instances.sort((a, b) => {
|
||||
return dayjs(b.metadata.date_modified).diff(dayjs(a.metadata.date_modified))
|
||||
})
|
||||
}
|
||||
|
||||
if (filters.value === 'Custom instances') {
|
||||
instances = instances.filter((instance) => {
|
||||
return !instance.metadata?.linked_data
|
||||
})
|
||||
} else if (filters.value === 'Downloaded modpacks') {
|
||||
instances = instances.filter((instance) => {
|
||||
return instance.metadata?.linked_data
|
||||
})
|
||||
}
|
||||
|
||||
const instanceMap = new Map()
|
||||
|
||||
if (group.value === 'Loader') {
|
||||
instances.forEach((instance) => {
|
||||
const loader = formatCategoryHeader(instance.metadata.loader)
|
||||
if (!instanceMap.has(loader)) {
|
||||
instanceMap.set(loader, [])
|
||||
}
|
||||
|
||||
instanceMap.get(loader).push(instance)
|
||||
})
|
||||
} else if (group.value === 'Game version') {
|
||||
instances.forEach((instance) => {
|
||||
if (!instanceMap.has(instance.metadata.game_version)) {
|
||||
instanceMap.set(instance.metadata.game_version, [])
|
||||
}
|
||||
|
||||
instanceMap.get(instance.metadata.game_version).push(instance)
|
||||
})
|
||||
} else if (group.value === 'Category') {
|
||||
instances.forEach((instance) => {
|
||||
if (instance.metadata.groups.length === 0) {
|
||||
instance.metadata.groups.push('None')
|
||||
}
|
||||
|
||||
for (const category of instance.metadata.groups) {
|
||||
if (!instanceMap.has(category)) {
|
||||
instanceMap.set(category, [])
|
||||
}
|
||||
|
||||
instanceMap.get(category).push(instance)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return instanceMap.set('None', instances)
|
||||
}
|
||||
|
||||
return instanceMap
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="header">
|
||||
<p>{{ props.label }}</p>
|
||||
<hr />
|
||||
<Card class="header">
|
||||
<div class="iconified-input">
|
||||
<SearchIcon />
|
||||
<input v-model="search" type="text" placeholder="Search" class="search-input" />
|
||||
</div>
|
||||
<div class="labeled_button">
|
||||
<span>Sort by</span>
|
||||
<DropdownSelect
|
||||
v-model="sortBy"
|
||||
class="sort-dropdown"
|
||||
:options="['Name', 'Last played', 'Date created', 'Date modified', 'Game version']"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
<div class="labeled_button">
|
||||
<span>Filter by</span>
|
||||
<DropdownSelect
|
||||
v-model="filters"
|
||||
class="filter-dropdown"
|
||||
:options="['All profiles', 'Custom instances', 'Downloaded modpacks']"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
<div class="labeled_button">
|
||||
<span>Group by</span>
|
||||
<DropdownSelect
|
||||
v-model="group"
|
||||
class="group-dropdown"
|
||||
:options="['Category', 'Loader', 'Game version', 'None']"
|
||||
placeholder="Select..."
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
<div
|
||||
v-for="instanceSection in Array.from(filteredResults, ([key, value]) => ({ key, value }))"
|
||||
:key="instanceSection.key"
|
||||
class="row"
|
||||
>
|
||||
<div v-if="instanceSection.key !== 'None'" class="divider">
|
||||
<p>{{ instanceSection.key }}</p>
|
||||
<hr aria-hidden="true" />
|
||||
</div>
|
||||
<section class="instances">
|
||||
<Instance
|
||||
v-for="instance in props.instances"
|
||||
v-for="instance in instanceSection.value"
|
||||
:key="instance.id"
|
||||
display="card"
|
||||
:instance="instance"
|
||||
@@ -34,17 +164,17 @@ const props = defineProps({
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
|
||||
.header {
|
||||
.divider {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: inherit;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
@@ -59,50 +189,52 @@ const props = defineProps({
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: inherit;
|
||||
align-items: inherit;
|
||||
|
||||
svg {
|
||||
background: var(--color-raised-bg);
|
||||
border-radius: var(--radius-lg);
|
||||
width: 1.3rem;
|
||||
height: 1.2rem;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
transition: all ease-in-out 0.1s;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(150%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
align-items: inherit;
|
||||
transition: all ease-in-out 0.4s;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.instances {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
margin-right: auto;
|
||||
scroll-behavior: smooth;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dark-mode {
|
||||
.row {
|
||||
&:nth-child(odd) {
|
||||
background-color: rgb(30, 31, 34);
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
align-items: inherit;
|
||||
margin: 1rem 1rem 0 !important;
|
||||
padding: 1rem;
|
||||
width: calc(100% - 2rem);
|
||||
|
||||
.iconified-input {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.sort-dropdown {
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
.filter-dropdown {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
.group-dropdown {
|
||||
width: 10rem;
|
||||
}
|
||||
|
||||
.labeled_button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.instances {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
margin-right: auto;
|
||||
scroll-behavior: smooth;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -152,9 +152,7 @@ onUnmounted(() => {
|
||||
|
||||
.dark-mode {
|
||||
.row {
|
||||
&:nth-child(odd) {
|
||||
background-color: rgb(30, 31, 34);
|
||||
}
|
||||
background-color: rgb(30, 31, 34);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -362,6 +362,7 @@ onUnmounted(() => unlisten())
|
||||
padding: 0.75rem !important; /* overrides card class */
|
||||
transition: 0.1s ease-in-out all !important; /* overrides Omorphia defaults */
|
||||
background: hsl(220, 11%, 17%) !important;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(1) !important;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { profile_listener } from '@/helpers/events'
|
||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||
import { useFetch } from '@/helpers/fetch.js'
|
||||
import { handleError } from '@/store/notifications.js'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const featuredModpacks = ref({})
|
||||
const featuredMods = ref({})
|
||||
@@ -21,7 +22,9 @@ const recentInstances = shallowRef([])
|
||||
|
||||
const getInstances = async () => {
|
||||
const profiles = await list(true).catch(handleError)
|
||||
recentInstances.value = Object.values(profiles)
|
||||
recentInstances.value = Object.values(profiles).sort((a, b) => {
|
||||
return dayjs(b.metadata.last_played ?? 0).diff(dayjs(a.metadata.last_played ?? 0))
|
||||
})
|
||||
|
||||
let filters = []
|
||||
for (const instance of recentInstances.value) {
|
||||
|
||||
@@ -19,7 +19,7 @@ fetchSettings.javaArgs = fetchSettings.custom_java_args.join(' ')
|
||||
fetchSettings.envArgs = fetchSettings.custom_env_args.map((x) => x.join('=')).join(' ')
|
||||
|
||||
const settings = ref(fetchSettings)
|
||||
const maxMemory = ref((await get_max_memory().catch(handleError)) / 1024)
|
||||
const maxMemory = ref(Math.floor((await get_max_memory().catch(handleError)) / 1024))
|
||||
|
||||
watch(settings.value, async (oldSettings, newSettings) => {
|
||||
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
||||
|
||||
@@ -82,6 +82,35 @@
|
||||
Edit versions
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="adjacent-input">
|
||||
<label>
|
||||
<span class="label__title">Categories</span>
|
||||
<span class="label__description">
|
||||
Set the categories of this instance, for display in the library page. This is purely
|
||||
cosmetic.
|
||||
</span>
|
||||
</label>
|
||||
<multiselect
|
||||
v-model="groups"
|
||||
:options="availableGroups"
|
||||
:multiple="true"
|
||||
:searchable="true"
|
||||
:show-no-results="false"
|
||||
:close-on-select="false"
|
||||
:clear-search-on-select="false"
|
||||
:show-labels="false"
|
||||
:taggable="true"
|
||||
tag-placeholder="Add new category"
|
||||
placeholder="Select categories..."
|
||||
@tag="
|
||||
(newTag) => {
|
||||
groups.push(newTag)
|
||||
availableGroups.push(newTag)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<Card>
|
||||
<div class="label">
|
||||
@@ -283,9 +312,10 @@ import {
|
||||
SaveIcon,
|
||||
HammerIcon,
|
||||
} from 'omorphia'
|
||||
import { Multiselect } from 'vue-multiselect'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { edit, edit_icon, get_optimal_jre_key, install, remove } from '@/helpers/profile.js'
|
||||
import { computed, onMounted, readonly, ref, shallowRef, watch } from 'vue'
|
||||
import { edit, edit_icon, get_optimal_jre_key, install, list, remove } from '@/helpers/profile.js'
|
||||
import { computed, readonly, ref, shallowRef, watch } from 'vue'
|
||||
import { get_max_memory } from '@/helpers/jre.js'
|
||||
import { get } from '@/helpers/settings.js'
|
||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||
@@ -306,6 +336,16 @@ const props = defineProps({
|
||||
|
||||
const title = ref(props.instance.metadata.name)
|
||||
const icon = ref(props.instance.metadata.icon)
|
||||
const groups = ref(props.instance.metadata.groups)
|
||||
|
||||
const instancesList = Object.values(await list(true))
|
||||
const availableGroups = ref([
|
||||
...new Set(
|
||||
instancesList.reduce((acc, obj) => {
|
||||
return acc.concat(obj.metadata.groups)
|
||||
}, [])
|
||||
),
|
||||
])
|
||||
|
||||
async function resetIcon() {
|
||||
icon.value = null
|
||||
@@ -347,7 +387,7 @@ const envVars = ref(
|
||||
|
||||
const overrideMemorySettings = ref(!!props.instance.memory)
|
||||
const memory = ref(props.instance.memory ?? globalSettings.memory)
|
||||
const maxMemory = (await get_max_memory().catch(handleError)) / 1024
|
||||
const maxMemory = Math.floor((await get_max_memory().catch(handleError)) / 1024)
|
||||
|
||||
const overrideWindowSettings = ref(!!props.instance.resolution)
|
||||
const resolution = ref(props.instance.resolution ?? globalSettings.game_resolution)
|
||||
@@ -358,6 +398,7 @@ const hooks = ref(props.instance.hooks ?? globalSettings.hooks)
|
||||
watch(
|
||||
[
|
||||
title,
|
||||
groups.value,
|
||||
overrideJavaInstall,
|
||||
javaInstall,
|
||||
overrideJavaArgs,
|
||||
@@ -375,6 +416,7 @@ watch(
|
||||
const editProfile = {
|
||||
metadata: {
|
||||
name: title.value,
|
||||
groups: groups.value,
|
||||
},
|
||||
java: {},
|
||||
}
|
||||
@@ -520,8 +562,6 @@ async function saveGvLoaderEdits() {
|
||||
editing.value = false
|
||||
changeVersionsModal.value.hide()
|
||||
}
|
||||
|
||||
onMounted(() => console.log(loader.value))
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user