Move many things over from Knossos (and other rearrangements) (#102)

This commit is contained in:
Emma Alexia
2023-10-16 21:18:23 -04:00
committed by GitHub
parent 46a6fee81d
commit 8369330053
68 changed files with 852 additions and 342 deletions

View File

@@ -4,7 +4,7 @@
ref="img"
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''} ${
pixelated ? 'pixelated' : ''
}`"
} ${raised ? 'raised' : ''}`"
:src="src"
:alt="alt"
:loading="loading"
@@ -12,7 +12,9 @@
/>
<svg
v-else
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''}`"
:class="`avatar size-${size} ${circle ? 'circle' : ''} ${noShadow ? 'no-shadow' : ''} ${
raised ? 'raised' : ''
}`"
xml:space="preserve"
fill-rule="evenodd"
stroke-linecap="round"
@@ -32,51 +34,48 @@
</svg>
</template>
<script>
export default {
props: {
src: {
type: String,
default: null,
},
alt: {
type: String,
default: '',
},
size: {
type: String,
default: 'sm',
validator(value) {
return ['xs', 'sm', 'md', 'lg', 'none'].includes(value)
},
},
circle: {
type: Boolean,
default: false,
},
noShadow: {
type: Boolean,
default: false,
},
loading: {
type: String,
default: 'lazy',
<script setup>
import { ref } from 'vue'
const pixelated = ref(false)
const img = ref(null)
defineProps({
src: {
type: String,
default: null,
},
alt: {
type: String,
default: '',
},
size: {
type: String,
default: 'sm',
validator(value) {
return ['xxs', 'xs', 'sm', 'md', 'lg', 'none'].includes(value)
},
},
data() {
return {
pixelated: false,
}
circle: {
type: Boolean,
default: false,
},
methods: {
updatePixelated() {
if (this.$refs.img && this.$refs.img.naturalWidth && this.$refs.img.naturalWidth <= 96) {
this.pixelated = true
} else {
this.pixelated = false
}
},
noShadow: {
type: Boolean,
default: false,
},
loading: {
type: String,
default: 'lazy',
},
raised: {
type: Boolean,
default: false,
},
})
function updatePixelated() {
pixelated.value = !!(img.value && img.value.naturalWidth && img.value.naturalWidth <= 96)
}
</script>
@@ -91,6 +90,12 @@ export default {
max-width: var(--size) !important;
max-height: var(--size) !important;
&.size-xxs {
--size: 1.25rem;
box-shadow: var(--shadow-inset), var(--shadow-card);
border-radius: var(--radius-sm);
}
&.size-xs {
--size: 2.5rem;
box-shadow: var(--shadow-inset), var(--shadow-card);
@@ -128,5 +133,9 @@ export default {
&.pixelated {
image-rendering: pixelated;
}
&.raised {
background-color: var(--color-raised-bg);
}
}
</style>

View File

@@ -1,27 +1,38 @@
<template>
<span :class="'version-badge ' + color + ' type--' + type">
<template v-if="color"> <span class="circle" /> {{ type }} </template>
<template v-if="color"> <span class="circle" /> {{ capitalizeString(type) }}</template>
<!-- User roles -->
<template v-else-if="type === 'admin'"> <ModrinthIcon /> Modrinth Team </template>
<template v-else-if="type === 'moderator'"> <ScaleIcon /> Moderator </template>
<template v-else-if="type === 'admin'"> <ModrinthIcon /> Modrinth Team</template>
<template v-else-if="type === 'moderator'"> <ScaleIcon /> Moderator</template>
<template v-else-if="type === 'creator'"><BoxIcon /> Creator</template>
<!-- Project statuses -->
<template v-else-if="type === 'approved'"><ListIcon /> Listed</template>
<template v-else-if="type === 'approved-general'"><CheckIcon /> Approved</template>
<template v-else-if="type === 'unlisted'"><EyeOffIcon /> Unlisted</template>
<template v-else-if="type === 'withheld'"><EyeOffIcon /> Withheld</template>
<template v-else-if="type === 'private'"><LockIcon /> Private</template>
<template v-else-if="type === 'scheduled'"> <CalendarIcon /> Scheduled </template>
<template v-else-if="type === 'scheduled'"> <CalendarIcon /> Scheduled</template>
<template v-else-if="type === 'draft'"><FileTextIcon /> Draft</template>
<template v-else-if="type === 'archived'"> <ArchiveIcon /> Archived </template>
<template v-else-if="type === 'archived'"> <ArchiveIcon /> Archived</template>
<template v-else-if="type === 'rejected'"><XIcon /> Rejected</template>
<template v-else-if="type === 'processing'"> <UpdatedIcon /> Under review </template>
<template v-else-if="type === 'processing'"> <UpdatedIcon /> Under review</template>
<!-- Team members -->
<template v-else-if="type === 'accepted'"><CheckIcon /> Accepted</template>
<template v-else-if="type === 'pending'"> <UpdatedIcon /> Pending </template>
<template v-else> <span class="circle" /> {{ type }} </template>
<template v-else-if="type === 'pending'"> <UpdatedIcon /> Pending</template>
<!-- Transaction statuses (pending, processing reused) -->
<template v-else-if="type === 'processed'"><CheckIcon /> Processed</template>
<template v-else-if="type === 'failed'"><XIcon /> Failed</template>
<template v-else-if="type === 'returned'"><XIcon /> Returned</template>
<!-- Report status -->
<template v-else-if="type === 'closed'"> <XIcon /> Closed</template>
<!-- Other -->
<template v-else> <span class="circle" /> {{ capitalizeString(type) }} </template>
</span>
</template>
@@ -39,7 +50,8 @@ import {
CheckIcon,
LockIcon,
CalendarIcon,
} from '@/components'
capitalizeString,
} from '@'
defineProps({
type: {
@@ -52,7 +64,6 @@ defineProps({
},
})
</script>
<style lang="scss" scoped>
.version-badge {
display: flex;
@@ -75,8 +86,11 @@ defineProps({
margin-right: 0.25rem;
}
&.type--closed,
&.type--withheld,
&.type--rejected,
&.type--returned,
&.type--failed,
&.red {
--badge-color: var(--color-red);
}
@@ -91,7 +105,8 @@ defineProps({
&.type--accepted,
&.type--admin,
&.type--success,
&.type--processed,
&.type--approved-general,
&.green {
--badge-color: var(--color-green);
}

View File

@@ -1,5 +1,5 @@
<script setup>
import { ExternalIcon, UnknownIcon } from '@/components'
import { ExternalIcon, UnknownIcon } from '@'
import { computed } from 'vue'

View File

@@ -1,5 +1,5 @@
<script setup>
import { Button, DropdownIcon } from '@/components'
import { Button, DropdownIcon } from '@'
import { reactive } from 'vue'

View File

@@ -24,7 +24,7 @@
</div>
</template>
<script setup>
import { CheckIcon, DropdownIcon } from '@/components'
import { CheckIcon, DropdownIcon } from '@'
</script>
<script>
import { defineComponent } from 'vue'

View File

@@ -4,7 +4,7 @@
v-for="item in items"
:key="item"
class="btn"
:class="{ selected: selected === item }"
:class="{ selected: selected === item, capitalize: capitalize }"
@click="toggleItem(item)"
>
<CheckIcon v-if="selected === item" />
@@ -13,7 +13,7 @@
</div>
</template>
<script setup>
import { CheckIcon, Button } from '@/components'
import { CheckIcon, Button } from '@'
</script>
<script>
import { defineComponent } from 'vue'
@@ -36,6 +36,10 @@ export default defineComponent({
default: (x) => x,
type: Function,
},
capitalize: {
type: Boolean,
default: true,
},
},
emits: ['update:modelValue'],
computed: {
@@ -72,7 +76,9 @@ export default defineComponent({
flex-wrap: wrap;
.btn {
text-transform: capitalize;
&.capitalize {
text-transform: capitalize;
}
svg {
width: 1em;

View File

@@ -0,0 +1,21 @@
<template>
<router-link v-if="isLink" :to="to">
<slot />
</router-link>
<span v-else>
<slot />
</span>
</template>
<script setup>
defineProps({
to: {
type: String,
required: true,
},
isLink: {
type: Boolean,
required: true,
},
})
</script>

View File

@@ -1,13 +1,13 @@
<template>
<button class="code" :class="{ copied }" title="Copy code to clipboard" @click="copyText">
{{ text }}
<span>{{ text }}</span>
<CheckIcon v-if="copied" />
<ClipboardCopyIcon v-else />
</button>
</template>
<script setup>
import { CheckIcon, ClipboardCopyIcon } from '@/components'
import { CheckIcon, ClipboardCopyIcon } from '@'
</script>
<script>
@@ -34,7 +34,7 @@ export default {
<style lang="scss" scoped>
.code {
display: flex;
display: inline-flex;
grid-gap: 0.5rem;
font-family: var(--mono-font);
font-size: var(--font-size-sm);
@@ -47,6 +47,12 @@ export default {
transition: opacity 0.5s ease-in-out, filter 0.2s ease-in-out, transform 0.05s ease-in-out,
outline 0.2s ease-in-out;
span {
max-width: 10rem;
overflow: hidden;
text-overflow: ellipsis;
}
svg {
width: 1em;
height: 1em;

View File

@@ -0,0 +1,34 @@
<template>
<div class="double-icon">
<slot name="primary" />
<div class="secondary">
<slot name="secondary" />
</div>
</div>
</template>
<style lang="scss" scoped>
.double-icon {
position: relative;
height: fit-content;
line-height: 0;
.secondary {
position: absolute;
bottom: -4px;
right: -4px;
background-color: var(--color-bg);
padding: var(--spacing-card-xs);
border-radius: 50%;
aspect-ratio: 1 / 1;
width: fit-content;
height: fit-content;
line-height: 0;
svg {
width: 1rem;
height: 1rem;
}
}
}
</style>

View File

@@ -61,7 +61,7 @@
</template>
<script setup>
import { DropdownIcon } from '@/components'
import { DropdownIcon } from '@'
import { computed, ref, watch } from 'vue'
const props = defineProps({

View File

@@ -4,7 +4,11 @@
A {{ type }}
</span>
<span
v-else-if="!['resourcepack', 'shader'].includes(type) && !(type === 'plugin' && search)"
v-else-if="
!['resourcepack', 'shader'].includes(type) &&
!(type === 'plugin' && search) &&
!categories.includes('datapack')
"
class="environment"
>
<template v-if="clientSide === 'optional' && serverSide === 'optional'">
@@ -44,7 +48,7 @@
</span>
</template>
<script setup>
import { GlobeIcon, ClientIcon, ServerIcon, InfoIcon } from '@/components'
import { GlobeIcon, ClientIcon, ServerIcon, InfoIcon } from '@'
</script>
<script>
import { defineComponent } from 'vue'
@@ -80,6 +84,13 @@ export default defineComponent({
required: false,
default: false,
},
categories: {
type: Array,
required: false,
default() {
return []
},
},
},
})
</script>

View File

@@ -13,7 +13,7 @@
</template>
<script>
import { fileIsValid } from '@/helpers/utils.js'
import { fileIsValid } from '@'
import { defineComponent } from 'vue'
export default defineComponent({
props: {

View File

@@ -1,140 +0,0 @@
<template>
<Modal ref="modal" :header="`Report ${props.itemType}`" :noblur="noblur">
<div class="modal-report">
<div class="markdown-body">
<p>
Modding should be safe for everyone, so we take abuse and malicious intent seriously at
Modrinth. We want to hear about harmful content on the site that violates our
<router-link to="/legal/terms">ToS</router-link>
and
<router-link to="/legal/rules">Rules</router-link>
. Rest assured, well keep your identifying information private.
</p>
<p v-if="props.itemType === 'project' || props.itemType === 'version'">
Please <strong>do not</strong> use this to report bugs with the project itself. This form
is only for submitting a report to Modrinth staff. If the project has an Issues link or a
Discord invite, consider reporting it there.
</p>
</div>
<div>
<label class="report-label" for="report-type">
<span>
<strong>Reason</strong>
</span>
</label>
<DropdownSelect
id="report-type"
v-model="reportType"
:options="props.reportTypes"
default-value="Choose report type"
class="multiselect"
/>
</div>
<label class="report-label" for="additional-information">
<strong>Additional information</strong>
<span> Include links and images if possible. Markdown formatting is supported. </span>
</label>
<div>
<div v-if="bodyViewType === 'source'" class="text-input textarea-wrapper">
<Chips v-model="bodyViewType" class="separator" :items="['source', 'preview']" />
<textarea id="body" v-model="body" spellcheck="true" />
</div>
<div v-else class="preview" v-html="renderString(body)"></div>
</div>
<div class="input-group push-right">
<Button @click="cancel">
<XIcon />
Cancel
</Button>
<Button color="primary" @click="submitReport">
<CheckIcon />
Report
</Button>
</div>
</div>
</Modal>
</template>
<script setup>
import { Modal, Chips, XIcon, CheckIcon, DropdownSelect } from '@/components'
import { renderString } from '@/helpers/parse.js'
import { ref } from 'vue'
const props = defineProps({
itemType: {
type: String,
default: '',
},
itemId: {
type: String,
default: '',
},
reportTypes: {
type: Array,
default: () => [],
},
submitReport: {
type: Function,
default: () => {},
},
noblur: {
type: Boolean,
default: false,
},
})
const reportType = ref('')
const body = ref('')
const bodyViewType = ref('source')
const modal = ref(null)
function cancel() {
reportType.value = ''
body.value = ''
bodyViewType.value = 'source'
modal.value.hide()
}
function show() {
modal.value.show()
}
defineExpose({
show,
})
</script>
<style scoped lang="scss">
.modal-report {
padding: var(--gap-lg);
display: flex;
flex-direction: column;
gap: 1rem;
}
.markdown-body {
margin-bottom: 1rem;
}
.report-label {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-bottom: 0.5rem;
}
.text-input {
height: 12rem;
gap: 1rem;
textarea {
// here due to a bug in safari
max-height: 9rem;
}
.preview {
overflow-y: auto;
}
}
</style>

View File

@@ -115,6 +115,14 @@ function stopTimer(notif) {
margin: 0;
}
}
@media screen and (max-width: 750px) {
bottom: calc(var(--size-mobile-navbar-height, 15px) + 10px) !important;
&.browse-menu-open {
bottom: calc(var(--size-mobile-navbar-height-expanded, 15px) + 10px) !important;
}
}
}
.notifs-enter-active,

View File

@@ -36,8 +36,7 @@
<script setup>
import { ref } from 'vue'
import PopoutMenu from '@/components/base/PopoutMenu.vue'
import Button from '@/components/base/Button.vue'
import { Button, PopoutMenu } from '@'
defineProps({
options: {

View File

@@ -50,7 +50,7 @@
</div>
</template>
<script setup>
import { GapIcon, LeftArrowIcon, RightArrowIcon } from '@/components'
import { GapIcon, LeftArrowIcon, RightArrowIcon } from '@'
</script>
<script>
import { defineComponent } from 'vue'
@@ -77,7 +77,7 @@ export default defineComponent({
pages() {
let pages = []
if (this.count > 4) {
if (this.count > 7) {
if (this.page + 3 >= this.count) {
pages = [
1,
@@ -88,7 +88,7 @@ export default defineComponent({
this.count - 1,
this.count,
]
} else if (this.page > 4) {
} else if (this.page > 5) {
pages = [1, '-', this.page - 1, this.page, this.page + 1, '-', this.count]
} else {
pages = [1, 2, 3, 4, 5, '-', this.count]
@@ -103,6 +103,9 @@ export default defineComponent({
methods: {
switchPage(newPage) {
this.$emit('switch-page', newPage)
if (newPage !== null && newPage !== '' && !isNaN(newPage)) {
this.$emit('switch-page', Math.min(Math.max(newPage, 1), this.count))
}
},
},
})

View File

@@ -34,6 +34,7 @@
:server-side="serverSide"
:type="projectTypeDisplay"
:search="search"
:categories="categories"
/>
</Categories>
<div class="stats">
@@ -65,7 +66,9 @@
</div>
</article>
</template>
<script setup>
import { formatNumber } from '@/helpers'
import {
Badge,
HeartIcon,
@@ -75,12 +78,13 @@ import {
Avatar,
Categories,
EnvironmentIndicator,
} from '@/components'
import { formatNumber } from '@/helpers/utils.js'
} from '@'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
</script>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
@@ -152,10 +156,6 @@ export default defineComponent({
type: String,
default: null,
},
hasModMessage: {
type: Boolean,
default: false,
},
serverSide: {
type: String,
required: false,
@@ -306,8 +306,7 @@ export default defineComponent({
img,
svg {
border-radius: var(--radius-lg);
border: 4px solid var(--color-raised-bg);
border-bottom: none;
box-shadow: -2px -2px 0 2px var(--color-raised-bg), 2px -2px 0 2px var(--color-raised-bg);
}
}

View File

@@ -25,9 +25,11 @@
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import BisectIcon from '@/assets/external/bh.svg'
import { BisectIcon } from '@'
const props = defineProps({
external: {
type: Boolean,
@@ -38,6 +40,7 @@ const props = defineProps({
default: '',
},
})
const target = computed(() => (props.external ? '_blank' : '_self'))
</script>

View File

@@ -33,5 +33,3 @@ export default {
},
}
</script>
<style scoped></style>

View File

@@ -18,8 +18,9 @@
/>
</g>
</g>
</g></svg
><svg
</g>
</svg>
<svg
class="rotate inner"
width="100%"
height="100%"
@@ -45,9 +46,7 @@
viewBox="0 0 590 591"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:space="preserve"
xmlns:serif="http://www.serif.com/"
style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 2"
>
<g transform="matrix(1,0,0,1,652.392,-0.400578)">
@@ -72,12 +71,6 @@
</div>
</template>
<script>
export default {
name: 'AnimatedLogo',
}
</script>
<style lang="scss" scoped>
div {
height: 5rem;

View File

@@ -7,7 +7,7 @@
stroke-miterlimit="2"
clip-rule="evenodd"
viewBox="0 0 3307 593"
:class="{ animate: animate }"
:class="{ animate }"
>
<path
fill-rule="nonzero"
@@ -30,16 +30,13 @@
</svg>
</template>
<script>
export default {
name: 'TextLogo',
props: {
animate: {
type: Boolean,
default: false,
},
<script setup>
defineProps({
animate: {
type: Boolean,
default: false,
},
}
})
</script>
<style lang="scss" scoped>

View File

@@ -1,5 +1,5 @@
<script setup>
import { defineProps, ref } from 'vue'
import { ref } from 'vue'
import { Bar } from 'vue-chartjs'
import {
Chart as ChartJS,

View File

@@ -1,43 +1,76 @@
// Base content
export { default as Avatar } from './base/Avatar.vue'
export { default as Badge } from './base/Badge.vue'
export { default as Button } from './base/Button.vue'
export { default as Card } from './base/Card.vue'
export { default as Checkbox } from './base/Checkbox.vue'
export { default as Chips } from './base/Chips.vue'
export { default as ConditionalNuxtLink } from './base/ConditionalNuxtLink.vue'
export { default as CopyCode } from './base/CopyCode.vue'
export { default as DoubleIcon } from './base/DoubleIcon.vue'
export { default as DropArea } from './base/DropArea.vue'
export { default as DropdownSelect } from './base/DropdownSelect.vue'
export { default as EnvironmentIndicator } from './base/EnvironmentIndicator.vue'
export { default as FileInput } from './base/FileInput.vue'
export { default as Notifications } from './base/Notifications.vue'
export { default as OverflowMenu } from './base/OverflowMenu.vue'
export { default as Page } from './base/Page.vue'
export { default as Slider } from './base/Slider.vue'
export { default as AnimatedLogo } from './brand/AnimatedLogo.vue'
export { default as TextLogo } from './brand/TextLogo.vue'
export { default as Pagination } from './base/Pagination.vue'
export { default as Modal } from './base/Modal.vue'
export { default as ModalReport } from './base/ModalReport.vue'
export { default as PopoutMenu } from './base/PopoutMenu.vue'
export { default as ProjectCard } from './base/ProjectCard.vue'
export { default as Promotion } from './base/Promotion.vue'
export { default as EnvironmentIndicator } from './base/EnvironmentIndicator.vue'
export { default as DropdownSelect } from './base/DropdownSelect.vue'
export { default as FileInput } from './base/FileInput.vue'
export { default as DropArea } from './base/DropArea.vue'
export { default as Slider } from './base/Slider.vue'
export { default as Toggle } from './base/Toggle.vue'
export { default as CopyCode } from './base/CopyCode.vue'
export { default as Notifications } from './base/Notifications.vue'
export { default as ModalConfirm } from './base/ModalConfirm.vue'
export { default as Breadcrumbs } from './base/Breadcrumbs.vue'
export { default as ShareModal } from './base/ShareModal.vue'
export { default as LineChart } from './base/LineChart.vue'
export { default as PieChart } from './base/PieChart.vue'
export { default as BarChart } from './base/BarChart.vue'
export { default as SearchDropdown } from './search/SearchDropdown.vue'
export { default as Categories } from './search/Categories.vue'
export { default as SearchFilter } from './search/SearchFilter.vue'
// Branding
export { default as ModrinthIcon } from '@/assets/branding/logo.svg?component'
export { default as AnimatedLogo } from './brand/AnimatedLogo.vue'
export { default as TextLogo } from './brand/TextLogo.vue'
export { default as FourOhFourNotFound } from '@/assets/branding/404.svg?component'
// Charts
export { default as BarChart } from './chart/BarChart.vue'
export { default as LineChart } from './chart/LineChart.vue'
export { default as PieChart } from './chart/PieChart.vue'
// Modals
export { default as Modal } from './modal/Modal.vue'
export { default as ConfirmModal } from './modal/ConfirmModal.vue'
export { default as ReportModal } from './modal/ReportModal.vue'
export { default as ShareModal } from './modal/ShareModal.vue'
// Navigation
export { default as Breadcrumbs } from './nav/Breadcrumbs.vue'
export { default as NavItem } from './nav/NavItem.vue'
export { default as NavRow } from './nav/NavRow.vue'
export { default as NavStack } from './nav/NavStack.vue'
export { default as PopoutMenu } from './base/PopoutMenu.vue'
export { default as OverflowMenu } from './base/OverflowMenu.vue'
// Search
export { default as Categories } from './search/Categories.vue'
export { default as SearchDropdown } from './search/SearchDropdown.vue'
export { default as SearchFilter } from './search/SearchFilter.vue'
// External Icons
export { default as SSODiscordIcon } from '@/assets/external/sso/discord.svg?component'
export { default as SSOGitHubIcon } from '@/assets/external/sso/github.svg?component'
export { default as SSOGitLabIcon } from '@/assets/external/sso/gitlab.svg?component'
export { default as SSOGoogleIcon } from '@/assets/external/sso/google.svg?component'
export { default as SSOMicrosoftIcon } from '@/assets/external/sso/microsoft.svg?component'
export { default as SSOSteamIcon } from '@/assets/external/sso/steam.svg?component'
export { default as AppleIcon } from '@/assets/external/apple.svg?component'
export { default as BisectIcon } from '@/assets/external/bh.svg?component'
export { default as BuyMeACoffeeIcon } from '@/assets/external/bmac.svg?component'
export { default as DiscordIcon } from '@/assets/external/discord.svg?component'
export { default as KoFiIcon } from '@/assets/external/kofi.svg?component'
export { default as MastodonIcon } from '@/assets/external/mastodon.svg?component'
export { default as OpenCollectiveIcon } from '@/assets/external/opencollective.svg?component'
export { default as PatreonIcon } from '@/assets/external/patreon.svg?component'
export { default as PayPalIcon } from '@/assets/external/paypal.svg?component'
export { default as RedditIcon } from '@/assets/external/reddit.svg?component'
export { default as TwitterIcon } from '@/assets/external/twitter.svg?component'
export { default as WindowsIcon } from '@/assets/external/windows.svg?component'
// Icons
export { default as AlignLeftIcon } from '@/assets/icons/align-left.svg?component'
export { default as ArchiveIcon } from '@/assets/icons/archive.svg?component'
export { default as AsteriskIcon } from '@/assets/icons/asterisk.svg?component'
@@ -49,6 +82,7 @@ export { default as BoxIcon } from '@/assets/icons/box.svg?component'
export { default as CalendarIcon } from '@/assets/icons/calendar.svg?component'
export { default as ChartIcon } from '@/assets/icons/chart.svg?component'
export { default as CheckIcon } from '@/assets/icons/check.svg?component'
export { default as CheckCheckIcon } from '@/assets/icons/check-check.svg?component'
export { default as CheckCircleIcon } from '@/assets/icons/check-circle.svg?component'
export { default as ChevronLeftIcon } from '@/assets/icons/chevron-left.svg?component'
export { default as ChevronRightIcon } from '@/assets/icons/chevron-right.svg?component'
@@ -89,14 +123,20 @@ export { default as HomeIcon } from '@/assets/icons/home.svg?component'
export { default as ImageIcon } from '@/assets/icons/image.svg?component'
export { default as InfoIcon } from '@/assets/icons/info.svg?component'
export { default as IssuesIcon } from '@/assets/icons/issues.svg?component'
export { default as KeyIcon } from '@/assets/icons/key.svg?component'
export { default as LanguagesIcon } from '@/assets/icons/languages.svg?component'
export { default as LeftArrowIcon } from '@/assets/icons/left-arrow.svg?component'
export { default as LibraryIcon } from '@/assets/icons/library.svg?component'
export { default as LightBulbIcon } from '@/assets/icons/light-bulb.svg?component'
export { default as LinkIcon } from '@/assets/icons/link.svg?component'
export { default as ListIcon } from '@/assets/icons/list.svg?component'
export { default as ListEndIcon } from '@/assets/icons/list-end.svg?component'
export { default as LockIcon } from '@/assets/icons/lock.svg?component'
export { default as LogInIcon } from '@/assets/icons/log-in.svg?component'
export { default as LogOutIcon } from '@/assets/icons/log-out.svg?component'
export { default as MailIcon } from '@/assets/icons/mail.svg?component'
export { default as MessageIcon } from '@/assets/icons/message.svg?component'
export { default as MicrophoneIcon } from '@/assets/icons/microphone.svg?component'
export { default as MoonIcon } from '@/assets/icons/moon.svg?component'
export { default as MoreHorizontalIcon } from '@/assets/icons/more-horizontal.svg?component'
export { default as MoreVerticalIcon } from '@/assets/icons/more-vertical.svg?component'
@@ -104,6 +144,9 @@ export { default as OmorphiaIcon } from '@/assets/icons/omorphia.svg?component'
export { default as PaintBrushIcon } from '@/assets/icons/paintbrush.svg?component'
export { default as PlayIcon } from '@/assets/icons/play.svg?component'
export { default as PlusIcon } from '@/assets/icons/plus.svg?component'
export { default as RadioButtonIcon } from '@/assets/icons/radio-button.svg?component'
export { default as RadioButtonChecked } from '@/assets/icons/radio-button-checked.svg?component'
export { default as ReplyIcon } from '@/assets/icons/reply.svg?component'
export { default as ReportIcon } from '@/assets/icons/report.svg?component'
export { default as RightArrowIcon } from '@/assets/icons/right-arrow.svg?component'
export { default as SaveIcon } from '@/assets/icons/save.svg?component'
@@ -112,8 +155,11 @@ export { default as SearchIcon } from '@/assets/icons/search.svg?component'
export { default as SendIcon } from '@/assets/icons/send.svg?component'
export { default as ServerIcon } from '@/assets/icons/server.svg?component'
export { default as SettingsIcon } from '@/assets/icons/settings.svg?component'
export { default as ShareIcon } from '@/assets/icons/share.svg?component'
export { default as ShieldIcon } from '@/assets/icons/shield.svg?component'
export { default as SlashIcon } from '@/assets/icons/slash.svg?component'
export { default as SortAscendingIcon } from '@/assets/icons/sort-asc.svg?component'
export { default as SortDescendingIcon } from '@/assets/icons/sort-desc.svg?component'
export { default as StarIcon } from '@/assets/icons/star.svg?component'
export { default as StopCircleIcon } from '@/assets/icons/stop-circle.svg?component'
export { default as SunIcon } from '@/assets/icons/sun.svg?component'
@@ -136,11 +182,3 @@ export { default as VersionIcon } from '@/assets/icons/version.svg?component'
export { default as WikiIcon } from '@/assets/icons/wiki.svg?component'
export { default as XIcon } from '@/assets/icons/x.svg?component'
export { default as XCircleIcon } from '@/assets/icons/x-circle.svg?component'
export { default as MailIcon } from '@/assets/icons/mail.svg?component'
export { default as ShareIcon } from '@/assets/icons/share.svg?component'
export { default as MastodonIcon } from '@/assets/external/mastodon.svg?component'
export { default as RedditIcon } from '@/assets/external/reddit.svg?component'
export { default as TwitterIcon } from '@/assets/external/twitter.svg?component'
export { default as ModrinthIcon } from '@/assets/branding/logo.svg?component'

View File

@@ -1,17 +1,17 @@
<template>
<Modal ref="modal" :header="props.title" :noblur="noblur">
<Modal ref="modal" :header="title" :noblur="noblur">
<div class="modal-delete">
<div class="markdown-body" v-html="renderString(props.description)" />
<label v-if="props.hasToType" for="confirmation" class="confirmation-label">
<div class="markdown-body" v-html="renderString(description)" />
<label v-if="hasToType" for="confirmation" class="confirmation-label">
<span>
<strong>To verify, type</strong>
<em class="confirmation-text">{{ props.confirmationText }}</em>
<em class="confirmation-text">{{ confirmationText }}</em>
<strong>below:</strong>
</span>
</label>
<div class="confirmation-input">
<input
v-if="props.hasToType"
v-if="hasToType"
id="confirmation"
v-model="confirmation_typed"
type="text"
@@ -26,7 +26,7 @@
</button>
<button class="btn btn-danger" :disabled="action_disabled" @click="proceed">
<TrashIcon />
{{ props.proceedLabel }}
{{ proceedLabel }}
</button>
</div>
</div>
@@ -34,8 +34,7 @@
</template>
<script setup>
import { renderString } from '@/helpers/parse'
import { XIcon, TrashIcon, Modal } from '@/components'
import { Modal, TrashIcon, XIcon, renderString } from '@'
import { ref } from 'vue'
const props = defineProps({

View File

@@ -32,7 +32,7 @@
</template>
<script setup>
import { XIcon } from '@/components'
import { XIcon } from '@'
import { ref } from 'vue'
const props = defineProps({

View File

@@ -0,0 +1,123 @@
<template>
<Modal ref="modal" :header="`Report ${itemType}`" :noblur="noblur">
<div class="modal-report universal-labels">
<div class="markdown-body">
<p>
Modding should be safe for everyone, so we take abuse and malicious intent seriously at
Modrinth. We want to hear about harmful content on the site that violates our
<router-link to="/legal/terms">ToS</router-link> and
<router-link to="/legal/rules">Rules</router-link>. Rest assured, we'll keep your
identifying information private.
</p>
<p v-if="itemType === 'project' || itemType === 'version'">
Please <strong>do not</strong> use this to report bugs with the project itself. This form
is only for submitting a report to Modrinth staff. If the project has an Issues link or a
Discord invite, consider reporting it there.
</p>
</div>
<label for="report-type">
<span class="label__title">Reason</span>
</label>
<DropdownSelect
id="report-type"
v-model="reportType"
name="report-type"
:options="reportTypes"
:display-name="capitalizeString"
default-value="Choose report type"
class="multiselect"
/>
<label for="report-body">
<span class="label__title">Additional information</span>
<span class="label__description markdown-body">
Please provide additional context about your report. Include links and images if possible.
<strong>Empty reports will be closed.</strong> This editor supports
<a href="https://docs.modrinth.com/markdown" target="_blank">Markdown formatting</a>.
</span>
</label>
<Chips v-model="bodyViewType" class="separator" :items="['source', 'preview']" />
<div class="text-input textarea-wrapper">
<textarea v-if="bodyViewType === 'source'" id="body" v-model="body" spellcheck="true" />
<div v-else class="preview" v-html="renderString(body)" />
</div>
<div class="input-group push-right">
<Button @click="cancel">
<XIcon />
Cancel
</Button>
<Button color="primary" @click="submitReport">
<CheckIcon />
Report
</Button>
</div>
</div>
</Modal>
</template>
<script setup>
import { Chips, DropdownSelect, Modal, CheckIcon, XIcon, capitalizeString, renderString } from '@'
import { ref } from 'vue'
defineProps({
itemType: {
type: String,
default: '',
},
itemId: {
type: String,
default: '',
},
reportTypes: {
type: Array,
default: () => [],
},
submitReport: {
type: Function,
default: () => {},
},
noblur: {
type: Boolean,
default: false,
},
})
const reportType = ref('')
const body = ref('')
const bodyViewType = ref('source')
const modal = ref(null)
function cancel() {
reportType.value = ''
body.value = ''
bodyViewType.value = 'source'
modal.value.hide()
}
function show() {
modal.value.show()
}
defineExpose({
show,
})
</script>
<style scoped lang="scss">
.modal-report {
padding: var(--gap-lg);
.textarea-wrapper {
height: 10rem;
:first-child {
max-height: 8rem;
transform: translateY(1rem);
}
}
.preview {
overflow-y: auto;
}
}
</style>

View File

@@ -10,7 +10,7 @@ import {
TwitterIcon,
MastodonIcon,
RedditIcon,
} from '@/components'
} from '@'
import { computed, ref, nextTick } from 'vue'
import QrcodeVue from 'qrcode.vue'

View File

@@ -15,7 +15,7 @@
</template>
<script setup>
import { ChevronRightIcon } from '@/components'
import { ChevronRightIcon } from '@'
defineProps({
linkStack: {
type: Array,

View File

@@ -1,5 +1,5 @@
<script setup>
import { Button } from '@/components'
import { Button } from '@'
defineProps({
link: {

View File

@@ -1,7 +1,3 @@
<script setup>
defineProps({})
</script>
<template>
<div class="omorphia__navstack">
<slot />

View File

@@ -9,23 +9,16 @@
</div>
</template>
<script setup>
import { formatCategory } from '@/helpers/utils.js'
</script>
<script>
export default {
name: 'Categories',
props: {
categories: {
type: Array,
default() {
return []
},
import { formatCategory } from '@'
defineProps({
categories: {
type: Array,
default() {
return []
},
},
methods: {
formatCategory,
},
}
})
</script>
<style lang="scss" scoped>

View File

@@ -67,7 +67,7 @@
<script setup>
import { ref } from 'vue'
import { Avatar, Button, XIcon, SearchIcon } from '@/components'
import { Avatar, Button, XIcon, SearchIcon } from '@'
const props = defineProps({
options: {

View File

@@ -15,50 +15,48 @@
</Checkbox>
</template>
<script>
import { defineComponent } from 'vue'
import Checkbox from '@/components/base/Checkbox.vue'
export default defineComponent({
components: {
Checkbox,
<script setup>
import { Checkbox } from '@'
defineProps({
facetName: {
type: String,
default: '',
},
props: {
facetName: {
type: String,
default: '',
},
displayName: {
type: String,
default: '',
},
icon: {
type: String,
default: '',
},
activeFilters: {
type: Array,
default() {
return []
},
},
displayName: {
type: String,
default: '',
},
emits: ['toggle'],
methods: {
toggle() {
this.$emit('toggle', this.facetName)
icon: {
type: String,
default: '',
},
activeFilters: {
type: Array,
default() {
return []
},
},
})
const emit = defineEmits(['toggle'])
function toggle() {
emit('toggle', this.facetName)
}
</script>
<style lang="scss" scoped>
.filter {
margin-bottom: 0.5rem;
:deep(.filter-text) {
display: flex;
align-items: center;
.icon {
height: 1rem;
svg {
margin-right: 0.25rem;
width: 1rem;
@@ -66,6 +64,7 @@ export default defineComponent({
}
}
}
span {
user-select: none;
}