refactor: update modpack export modal, exclude /mods/.connector (#6032)
* refactor: update modpack export modal, exclude /mods/.connector * Add slash suffix to folders * prepr * preprr
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { PlusIcon, XIcon } from '@modrinth/assets'
|
import { WrenchIcon,XIcon } from '@modrinth/assets'
|
||||||
import {
|
import {
|
||||||
|
Accordion,
|
||||||
ButtonStyled,
|
ButtonStyled,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
commonMessages,
|
commonMessages,
|
||||||
@@ -9,7 +10,7 @@ import {
|
|||||||
StyledInput,
|
StyledInput,
|
||||||
useVIntl,
|
useVIntl,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
import { open } from '@tauri-apps/plugin-dialog'
|
import { save } from '@tauri-apps/plugin-dialog'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
import { PackageIcon, VersionIcon } from '@/assets/icons'
|
||||||
@@ -40,9 +41,10 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
selectFilesLabel: {
|
selectFilesLabel: {
|
||||||
id: 'app.export-modal.select-files-label',
|
id: 'app.export-modal.select-files-label',
|
||||||
defaultMessage: 'Select files and folders to include in pack',
|
defaultMessage: 'Configure which files are included in this export',
|
||||||
},
|
},
|
||||||
exportButton: { id: 'app.export-modal.export-button', defaultMessage: 'Export' },
|
exportButton: { id: 'app.export-modal.export-button', defaultMessage: 'Export' },
|
||||||
|
includeFile: { id: 'app.export-modal.include-file-accessibility-label', defaultMessage: 'Include "{file}"?' },
|
||||||
})
|
})
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -65,7 +67,6 @@ const exportDescription = ref('')
|
|||||||
const versionInput = ref('1.0.0')
|
const versionInput = ref('1.0.0')
|
||||||
const files = ref([])
|
const files = ref([])
|
||||||
const folders = ref([])
|
const folders = ref([])
|
||||||
const showingFiles = ref(false)
|
|
||||||
|
|
||||||
const initFiles = async () => {
|
const initFiles = async () => {
|
||||||
const newFolders = new Map()
|
const newFolders = new Map()
|
||||||
@@ -87,7 +88,12 @@ const initFiles = async () => {
|
|||||||
folder.startsWith('modrinth_logs') ||
|
folder.startsWith('modrinth_logs') ||
|
||||||
folder.startsWith('.fabric'),
|
folder.startsWith('.fabric'),
|
||||||
}))
|
}))
|
||||||
.filter((pathData) => !pathData.path.includes('.DS_Store'))
|
.filter(
|
||||||
|
(pathData) =>
|
||||||
|
!pathData.path.includes('.DS_Store') &&
|
||||||
|
pathData.path !== 'mods/.connector' &&
|
||||||
|
!pathData.path.startsWith('mods/.connector/'),
|
||||||
|
)
|
||||||
.forEach((pathData) => {
|
.forEach((pathData) => {
|
||||||
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
|
const parent = pathData.path.split(sep).slice(0, -1).join(sep)
|
||||||
if (parent !== '') {
|
if (parent !== '') {
|
||||||
@@ -121,15 +127,20 @@ const exportPack = async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const outputPath = await open({
|
const outputPath = await save({
|
||||||
directory: true,
|
defaultPath: `${nameInput.value} ${versionInput.value}.mrpack`,
|
||||||
multiple: false,
|
filters: [
|
||||||
|
{
|
||||||
|
name: 'Modrinth Modpack',
|
||||||
|
extensions: ['mrpack'],
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
if (outputPath) {
|
if (outputPath) {
|
||||||
export_profile_mrpack(
|
export_profile_mrpack(
|
||||||
props.instance.path,
|
props.instance.path,
|
||||||
outputPath + `/${nameInput.value} ${versionInput.value}.mrpack`,
|
outputPath,
|
||||||
filesToExport,
|
filesToExport,
|
||||||
versionInput.value,
|
versionInput.value,
|
||||||
exportDescription.value,
|
exportDescription.value,
|
||||||
@@ -142,97 +153,81 @@ const exportPack = async () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ModalWrapper ref="exportModal" :header="formatMessage(messages.header)">
|
<ModalWrapper ref="exportModal" :header="formatMessage(messages.header)">
|
||||||
<div class="modal-body">
|
<div class="flex flex-col gap-4 w-[40rem]">
|
||||||
<div class="labeled_input">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<p>{{ formatMessage(messages.modpackNameLabel) }}</p>
|
|
||||||
<StyledInput
|
|
||||||
v-model="nameInput"
|
|
||||||
:icon="PackageIcon"
|
|
||||||
type="text"
|
|
||||||
:placeholder="formatMessage(messages.modpackNamePlaceholder)"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="labeled_input">
|
|
||||||
<p>{{ formatMessage(messages.versionNumberLabel) }}</p>
|
|
||||||
<StyledInput
|
|
||||||
v-model="versionInput"
|
|
||||||
:icon="VersionIcon"
|
|
||||||
type="text"
|
|
||||||
:placeholder="formatMessage(messages.versionNumberPlaceholder)"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="adjacent-input">
|
|
||||||
<div class="labeled_input">
|
<div class="labeled_input">
|
||||||
<p>{{ formatMessage(commonMessages.descriptionLabel) }}</p>
|
<p>{{ formatMessage(messages.modpackNameLabel) }}</p>
|
||||||
|
|
||||||
<StyledInput
|
<StyledInput
|
||||||
v-model="exportDescription"
|
v-model="nameInput"
|
||||||
multiline
|
:icon="PackageIcon"
|
||||||
:placeholder="formatMessage(messages.descriptionPlaceholder)"
|
type="text"
|
||||||
|
:placeholder="formatMessage(messages.modpackNamePlaceholder)"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="labeled_input">
|
||||||
|
<p>{{ formatMessage(messages.versionNumberLabel) }}</p>
|
||||||
|
<StyledInput
|
||||||
|
v-model="versionInput"
|
||||||
|
:icon="VersionIcon"
|
||||||
|
type="text"
|
||||||
|
:placeholder="formatMessage(messages.versionNumberPlaceholder)"
|
||||||
|
clearable
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
<div class="table">
|
<p class="m-0">{{ formatMessage(commonMessages.descriptionLabel) }}</p>
|
||||||
<div class="table-head">
|
<StyledInput
|
||||||
<div class="table-cell row-wise">
|
v-model="exportDescription"
|
||||||
{{ formatMessage(messages.selectFilesLabel) }}
|
multiline
|
||||||
<ButtonStyled circular>
|
:placeholder="formatMessage(messages.descriptionPlaceholder)"
|
||||||
<button @click="() => (showingFiles = !showingFiles)">
|
/>
|
||||||
<PlusIcon v-if="!showingFiles" />
|
</div>
|
||||||
<XIcon v-else />
|
<Accordion class="w-full bg-surface-4 border border-solid border-surface-5 rounded-2xl overflow-clip" button-class="p-4 w-full border-b border-solid border-b-surface-5 bg-surface-2 -mb-px hover:brightness-[--hover-brightness] group">
|
||||||
</button>
|
<template #title>
|
||||||
</ButtonStyled>
|
<span class="flex items-center gap-3 text-contrast group-active:scale-[0.98]">
|
||||||
</div>
|
<WrenchIcon aria-hidden="true" class="size-5 text-secondary" />
|
||||||
</div>
|
Configure which files are included in this export
|
||||||
<div v-if="showingFiles" class="table-content">
|
</span>
|
||||||
<div v-for="[path, children] in folders" :key="path.name" class="table-row">
|
</template>
|
||||||
<div class="table-cell file-entry">
|
<div class="flex flex-col [&>*:nth-child(even)]:bg-surface-3">
|
||||||
<div class="file-primary">
|
<div v-for="[path, children] in folders" :key="path.name" class="flex flex-col">
|
||||||
|
<Accordion class="flex flex-col" button-class="flex gap-3 pr-4 hover:bg-surface-5 group">
|
||||||
|
<template #title>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
:model-value="children.every((child) => child.selected)"
|
:model-value="children.every((child) => child.selected)"
|
||||||
:label="path.name"
|
:indeterminate="!children.every((child) => child.selected) && children.some((child) => child.selected)"
|
||||||
class="select-checkbox"
|
:description="formatMessage(messages.includeFile, { file: path.name })"
|
||||||
|
class="pl-4 py-2"
|
||||||
:disabled="children.every((x) => x.disabled)"
|
:disabled="children.every((x) => x.disabled)"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(newValue) => children.forEach((child) => (child.selected = newValue))
|
(newValue) => children.forEach((child) => (child.selected = newValue))
|
||||||
"
|
"
|
||||||
|
@click.stop
|
||||||
/>
|
/>
|
||||||
|
<span class="ml-2 group-active:scale-95">{{ path.name }}/</span>
|
||||||
|
</template>
|
||||||
|
<div v-for="child in children" :key="child.path">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-model="path.showingMore"
|
v-model="child.selected"
|
||||||
class="select-checkbox dropdown"
|
:label="child.name"
|
||||||
collapsing-toggle-style
|
class="w-full px-8 py-2 hover:bg-surface-4 text-primary"
|
||||||
|
:disabled="child.disabled"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="path.showingMore" class="file-secondary">
|
</Accordion>
|
||||||
<div v-for="child in children" :key="child.path" class="file-secondary-row">
|
|
||||||
<Checkbox
|
|
||||||
v-model="child.selected"
|
|
||||||
:label="child.name"
|
|
||||||
class="select-checkbox"
|
|
||||||
:disabled="child.disabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="file in files" :key="file.path" class="table-row">
|
|
||||||
<div class="table-cell file-entry">
|
|
||||||
<div class="file-primary">
|
|
||||||
<Checkbox
|
|
||||||
v-model="file.selected"
|
|
||||||
:label="file.name"
|
|
||||||
:disabled="file.disabled"
|
|
||||||
class="select-checkbox"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
v-for="file in files" :key="file.path"
|
||||||
|
v-model="file.selected"
|
||||||
|
:label="file.name"
|
||||||
|
:disabled="file.disabled"
|
||||||
|
class="w-full px-4 py-2 hover:bg-surface-4 text-primary"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Accordion>
|
||||||
<div class="button-row push-right">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<ButtonStyled type="outlined">
|
<ButtonStyled type="outlined">
|
||||||
<button @click="exportModal.hide">
|
<button @click="exportModal.hide">
|
||||||
<XIcon />
|
<XIcon />
|
||||||
@@ -249,83 +244,3 @@ const exportPack = async () => {
|
|||||||
</div>
|
</div>
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.modal-body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.labeled_input {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-checkbox {
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
|
|
||||||
button.checkbox {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dropdown {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-content {
|
|
||||||
max-height: 18rem;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
border: 1px solid var(--color-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-entry {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-primary {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-secondary {
|
|
||||||
margin-left: var(--gap-xl);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
height: 100%;
|
|
||||||
vertical-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-secondary-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-row {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--gap-sm);
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row-wise {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -164,6 +164,9 @@
|
|||||||
"app.export-modal.header": {
|
"app.export-modal.header": {
|
||||||
"message": "Export modpack"
|
"message": "Export modpack"
|
||||||
},
|
},
|
||||||
|
"app.export-modal.include-file-accessibility-label": {
|
||||||
|
"message": "Include \"{file}\"?"
|
||||||
|
},
|
||||||
"app.export-modal.modpack-name-label": {
|
"app.export-modal.modpack-name-label": {
|
||||||
"message": "Modpack Name"
|
"message": "Modpack Name"
|
||||||
},
|
},
|
||||||
@@ -171,7 +174,7 @@
|
|||||||
"message": "Modpack name"
|
"message": "Modpack name"
|
||||||
},
|
},
|
||||||
"app.export-modal.select-files-label": {
|
"app.export-modal.select-files-label": {
|
||||||
"message": "Select files and folders to include in pack"
|
"message": "Configure which files are included in this export"
|
||||||
},
|
},
|
||||||
"app.export-modal.version-number-label": {
|
"app.export-modal.version-number-label": {
|
||||||
"message": "Version number"
|
"message": "Version number"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
import type { FunctionalComponent, SVGAttributes } from 'vue'
|
import type { FunctionalComponent, SVGAttributes } from 'vue'
|
||||||
|
|
||||||
|
export type IconComponent = FunctionalComponent<SVGAttributes>
|
||||||
|
|
||||||
import _AffiliateIcon from './icons/affiliate.svg?component'
|
import _AffiliateIcon from './icons/affiliate.svg?component'
|
||||||
import _AlignLeftIcon from './icons/align-left.svg?component'
|
import _AlignLeftIcon from './icons/align-left.svg?component'
|
||||||
import _ArchiveIcon from './icons/archive.svg?component'
|
import _ArchiveIcon from './icons/archive.svg?component'
|
||||||
@@ -395,8 +397,6 @@ import _XCircleIcon from './icons/x-circle.svg?component'
|
|||||||
import _ZoomInIcon from './icons/zoom-in.svg?component'
|
import _ZoomInIcon from './icons/zoom-in.svg?component'
|
||||||
import _ZoomOutIcon from './icons/zoom-out.svg?component'
|
import _ZoomOutIcon from './icons/zoom-out.svg?component'
|
||||||
|
|
||||||
export type IconComponent = FunctionalComponent<SVGAttributes>
|
|
||||||
|
|
||||||
export const AffiliateIcon = _AffiliateIcon
|
export const AffiliateIcon = _AffiliateIcon
|
||||||
export const AlignLeftIcon = _AlignLeftIcon
|
export const AlignLeftIcon = _AlignLeftIcon
|
||||||
export const ArchiveIcon = _ArchiveIcon
|
export const ArchiveIcon = _ArchiveIcon
|
||||||
|
|||||||
@@ -14,12 +14,11 @@
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="w-5 h-5 rounded-md flex items-center justify-center border-[1px] border-solid"
|
class="w-5 h-5 rounded-md flex items-center justify-center border-[1px] border-solid"
|
||||||
:class="
|
:class="{
|
||||||
(modelValue
|
'bg-brand border-button-border text-brand-inverted': modelValue,
|
||||||
? 'bg-brand border-button-border text-brand-inverted'
|
'bg-surface-2 border-surface-5 text-primary': !modelValue,
|
||||||
: 'bg-surface-2 border-surface-5') +
|
'checkbox-shadow group-active:scale-95': disabled,
|
||||||
(disabled ? '' : ' checkbox-shadow group-active:scale-95')
|
}"
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<MinusIcon v-if="indeterminate" aria-hidden="true" stroke-width="3" />
|
<MinusIcon v-if="indeterminate" aria-hidden="true" stroke-width="3" />
|
||||||
<CheckIcon v-else-if="modelValue" aria-hidden="true" stroke-width="3" />
|
<CheckIcon v-else-if="modelValue" aria-hidden="true" stroke-width="3" />
|
||||||
|
|||||||
Reference in New Issue
Block a user