Skip to content

Commit 64c4a47

Browse files
committed
Re-implemented several settings options
1 parent ae580a9 commit 64c4a47

12 files changed

Lines changed: 569 additions & 2 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script lang="ts" setup>
2+
import { provide, toRef } from 'vue';
3+
4+
const props = defineProps<{ name: string }>();
5+
6+
provide('settingsCategory', toRef(props, 'name'));
7+
</script>
8+
9+
<template>
10+
<section>
11+
<slot></slot>
12+
</section>
13+
</template>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<script lang="ts" setup>
2+
import { computed, ref } from 'vue';
3+
import VersionNumber from '../../../model/VersionNumber';
4+
import ManagerInformation from '../../../_managerinf/ManagerInformation';
5+
import { Hero } from '../../all';
6+
import SettingsSection from './SettingsSection.vue';
7+
import GameDirectory from './entries/GameDirectory.vue';
8+
import DataDirectory from './entries/DataDirectory.vue';
9+
import ExportProfile from './entries/ExportProfile.vue';
10+
import Theme from './entries/Theme.vue';
11+
import ExpandCards from './entries/ExpandCards.vue';
12+
import FunkyMode from './entries/FunkyMode.vue';
13+
import RefreshOnlineModList from './entries/RefreshOnlineModList.vue';
14+
15+
const managerVersionNumber = ref<VersionNumber>(ManagerInformation.VERSION);
16+
const appName = computed(() => ManagerInformation.APP_NAME);
17+
18+
const categories = ['All', 'Directories', 'Profile', 'Debugging', 'Modpacks', 'Other'] as const;
19+
type Category = typeof categories[number];
20+
21+
const activeCategory = ref<Category>('All');
22+
23+
function isVisible(section: Category): boolean {
24+
return activeCategory.value === 'All' || activeCategory.value === section;
25+
}
26+
</script>
27+
28+
<template>
29+
<div id="settings-view">
30+
<Hero
31+
title="Settings"
32+
:subtitle="`Advanced options for ${appName}: ${managerVersionNumber.toString()}`"
33+
heroType="primary"
34+
/>
35+
<div class="settings-shell">
36+
<aside class="menu settings-nav">
37+
<p class="menu-label">Sections</p>
38+
<ul class="menu-list">
39+
<li v-for="category in categories" :key="category">
40+
<a
41+
:class="{ 'is-active': activeCategory === category }"
42+
@click="activeCategory = category"
43+
>
44+
{{ category }}
45+
</a>
46+
</li>
47+
</ul>
48+
</aside>
49+
50+
<div class="settings-list">
51+
<SettingsSection v-if="isVisible('Directories')" name="Directories">
52+
<GameDirectory />
53+
<DataDirectory />
54+
</SettingsSection>
55+
56+
<SettingsSection v-if="isVisible('Profile')" name="Profile">
57+
<ExportProfile />
58+
</SettingsSection>
59+
60+
<SettingsSection v-if="isVisible('Other')" name="Other">
61+
<Theme />
62+
<ExpandCards />
63+
<FunkyMode />
64+
<RefreshOnlineModList />
65+
</SettingsSection>
66+
</div>
67+
</div>
68+
</div>
69+
</template>
70+
71+
<style lang="scss" scoped>
72+
#settings-view {
73+
width: 100%;
74+
display: flex;
75+
flex-direction: column;
76+
flex: 1;
77+
min-height: 0;
78+
overflow-y: auto;
79+
}
80+
81+
.settings-shell {
82+
display: flex;
83+
align-items: flex-start;
84+
flex: 1 0 auto;
85+
}
86+
87+
.settings-nav {
88+
flex: 0 0 200px;
89+
padding: 1.25rem 1rem;
90+
position: sticky;
91+
top: 0;
92+
align-self: flex-start;
93+
max-height: 100vh;
94+
overflow-y: auto;
95+
}
96+
97+
.settings-list {
98+
flex: 1;
99+
min-width: 0;
100+
padding: 1rem 1.25rem 1rem;
101+
}
102+
</style>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<script lang="ts" setup>
2+
import { inject, ref, type Ref } from 'vue';
3+
4+
const category = inject<Ref<string>>('settingsCategory', ref(''));
5+
</script>
6+
7+
<template>
8+
<div class="settings-entry">
9+
<p v-if="category" class="settings-entry__caption">{{ category }}</p>
10+
<p class="settings-entry__title">
11+
<slot name="title"></slot>
12+
</p>
13+
<p v-if="$slots.description" class="settings-entry__description">
14+
<slot name="description"></slot>
15+
</p>
16+
<div class="settings-entry__control">
17+
<slot></slot>
18+
</div>
19+
</div>
20+
</template>
21+
22+
<style lang="scss" scoped>
23+
.settings-entry {
24+
display: flex;
25+
flex-direction: column;
26+
padding: 1.1rem 0;
27+
border-bottom: 1px solid var(--border, #e1e1e1);
28+
29+
&__caption {
30+
font-size: 0.7rem;
31+
font-weight: 600;
32+
text-transform: uppercase;
33+
letter-spacing: 0.05em;
34+
color: var(--text-secondary, #6b6464);
35+
margin-bottom: 0.2rem;
36+
}
37+
38+
&__title {
39+
font-size: 0.95rem;
40+
font-weight: 600;
41+
color: var(--text-strong, #363636);
42+
margin-bottom: 0.35rem;
43+
line-height: 1.35;
44+
}
45+
46+
&__description {
47+
font-size: 0.9rem;
48+
color: var(--text-secondary, #6b6464);
49+
line-height: 1.45;
50+
margin-bottom: 0.6rem;
51+
max-width: 46rem;
52+
}
53+
54+
&__control {
55+
display: flex;
56+
align-items: center;
57+
flex-wrap: wrap;
58+
gap: 0.5rem;
59+
}
60+
}
61+
</style>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<script lang="ts" setup>
2+
import { ref } from 'vue';
3+
import PathResolver from '../../../../r2mm/manager/PathResolver';
4+
import SettingsViewWrapper from '../SettingsViewWrapper.vue';
5+
6+
const dataDirectory = ref<string>(PathResolver.ROOT || 'Not set');
7+
8+
function selectDirectory() {
9+
console.log("Should select");
10+
}
11+
</script>
12+
13+
<template>
14+
<SettingsViewWrapper>
15+
<template #title>Data folder</template>
16+
<template #description>
17+
The folder where mods are stored for all games and profiles.
18+
The folder will not be deleted, and existing profiles will not carry across.
19+
</template>
20+
<div class="data-directory-setting">
21+
<div class="data-directory-setting__field">
22+
<input
23+
class="input data-directory-setting__input"
24+
type="text"
25+
:value="dataDirectory"
26+
readonly
27+
/>
28+
<button class="button" @click="selectDirectory">Change</button>
29+
<button class="button" @click="selectDirectory">Browse</button>
30+
</div>
31+
</div>
32+
</SettingsViewWrapper>
33+
</template>
34+
35+
<style lang="scss" scoped>
36+
.data-directory-setting {
37+
display: flex;
38+
flex-direction: column;
39+
gap: 0.4rem;
40+
flex: 1;
41+
42+
&__field {
43+
display: flex;
44+
align-items: center;
45+
gap: 0.5rem;
46+
}
47+
48+
&__input {
49+
flex: 1;
50+
min-width: 14rem;
51+
max-width: 30rem;
52+
}
53+
}
54+
</style>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<script lang="ts" setup>
2+
import { onMounted, ref } from 'vue';
3+
import { getStore } from '../../../../providers/generic/store/StoreProvider';
4+
import { State } from '../../../../store';
5+
import ManagerSettings from '../../../../r2mm/manager/ManagerSettings';
6+
import SettingsViewWrapper from '../SettingsViewWrapper.vue';
7+
8+
const store = getStore<State>();
9+
const settings = ref<ManagerSettings | null>(null);
10+
const expandedCards = ref<boolean>(false);
11+
12+
onMounted(async () => {
13+
settings.value = await ManagerSettings.getSingleton(store.state.activeGame);
14+
expandedCards.value = settings.value.getContext().global.expandedCards;
15+
});
16+
17+
async function setExpanded(expanded: boolean) {
18+
expandedCards.value = expanded;
19+
if (expanded) {
20+
await settings.value?.expandCards();
21+
} else {
22+
await settings.value?.collapseCards();
23+
}
24+
}
25+
</script>
26+
27+
<template>
28+
<SettingsViewWrapper>
29+
<template #title>Expand cards by default</template>
30+
<template #description>
31+
Show mod cards fully expanded rather than collapsed when opening a mod list.
32+
</template>
33+
<div class="field" @click.prevent.stop="setExpanded(!expandedCards)">
34+
<input
35+
id="switch-expand-cards"
36+
type="checkbox"
37+
:class="['switch', { 'is-info': expandedCards }]"
38+
:checked="expandedCards"
39+
/>
40+
<label for="switch-expand-cards">{{ expandedCards ? 'Expanded' : 'Collapsed' }}</label>
41+
</div>
42+
</SettingsViewWrapper>
43+
</template>
44+
45+
<style scoped lang="scss">
46+
.switch {
47+
position: relative;
48+
}
49+
</style>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<script lang="ts" setup>
2+
import { computed, ref } from 'vue';
3+
import { getStore } from '../../../../providers/generic/store/StoreProvider';
4+
import { State } from '../../../../store';
5+
import R2Error from '../../../../model/errors/R2Error';
6+
import SettingsViewWrapper from '../SettingsViewWrapper.vue';
7+
8+
const store = getStore<State>();
9+
10+
const activeExport = ref<'file' | 'code' | null>(null);
11+
const isExporting = computed(() => activeExport.value !== null);
12+
13+
async function exportProfile(as: 'file' | 'code', action: string) {
14+
if (isExporting.value) {
15+
return;
16+
}
17+
activeExport.value = as;
18+
try {
19+
await store.dispatch(action);
20+
} catch (e) {
21+
store.commit('error/handleError', R2Error.fromThrownValue(e));
22+
} finally {
23+
activeExport.value = null;
24+
}
25+
}
26+
</script>
27+
28+
<template>
29+
<SettingsViewWrapper>
30+
<template #title>Export profile</template>
31+
<template #description>
32+
Export your mod list and configs to share with friends and get an identical profile quickly and easily.
33+
</template>
34+
<div class="export-profile-setting">
35+
<button
36+
class="button"
37+
:class="{ 'is-loading': activeExport === 'file' }"
38+
:disabled="isExporting"
39+
@click="exportProfile('file', 'profileExport/exportProfileAsFile')"
40+
>
41+
As a file
42+
</button>
43+
<button
44+
class="button"
45+
:class="{ 'is-loading': activeExport === 'code' }"
46+
:disabled="isExporting"
47+
@click="exportProfile('code', 'profileExport/exportProfileAsCode')"
48+
>
49+
As a code
50+
</button>
51+
</div>
52+
</SettingsViewWrapper>
53+
</template>
54+
55+
<style lang="scss" scoped>
56+
.export-profile-setting {
57+
display: flex;
58+
align-items: center;
59+
gap: 0.5rem;
60+
flex-wrap: wrap;
61+
}
62+
</style>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script lang="ts" setup>
2+
import { onMounted, ref } from 'vue';
3+
import { getStore } from '../../../../providers/generic/store/StoreProvider';
4+
import { State } from '../../../../store';
5+
import ManagerSettings from '../../../../r2mm/manager/ManagerSettings';
6+
import SettingsViewWrapper from '../SettingsViewWrapper.vue';
7+
8+
const store = getStore<State>();
9+
const settings = ref<ManagerSettings | null>(null);
10+
const funkyModeEnabled = ref<boolean>(false);
11+
12+
onMounted(async () => {
13+
settings.value = await ManagerSettings.getSingleton(store.state.activeGame);
14+
funkyModeEnabled.value = settings.value.getContext().global.funkyModeEnabled;
15+
});
16+
17+
async function setFunkyMode(enabled: boolean) {
18+
funkyModeEnabled.value = enabled;
19+
await settings.value?.setFunkyMode(enabled);
20+
}
21+
</script>
22+
23+
<template>
24+
<SettingsViewWrapper>
25+
<template #title>Enable funky mode</template>
26+
<template #description>
27+
Adds a more colourful, playful flair to the manager. Purely cosmetic.
28+
</template>
29+
<div class="field" @click.prevent.stop="setFunkyMode(!funkyModeEnabled)">
30+
<input
31+
id="switch-funky-mode"
32+
type="checkbox"
33+
:class="['switch', { 'is-info': funkyModeEnabled }]"
34+
:checked="funkyModeEnabled"
35+
/>
36+
<label for="switch-funky-mode">{{ funkyModeEnabled ? 'Enabled' : 'Disabled' }}</label>
37+
</div>
38+
</SettingsViewWrapper>
39+
</template>
40+
41+
<style scoped lang="scss">
42+
.switch {
43+
position: relative;
44+
}
45+
</style>

0 commit comments

Comments
 (0)