You've already forked AstralRinth
forked from didirus/AstralRinth
Fix a number of light mode issues and get rid of scrollbar jumping on menus (#4760)
* Fix DEV-466, Fixes #4692 as well as a bunch of other poor contrast and inconsistency issues in light mode. Adds shadows to buttons and makes scrollbar gutter stable. * lintttt & only do scrollbar gutter on website * try to fix following hydration issue * try another clientonly approach * fix home page link animation * lint * remove dropdown style from checkbox & improve shadow consistency * liiiint
This commit is contained in:
@@ -765,7 +765,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
>
|
||||
<LibraryIcon />
|
||||
</NavButton>
|
||||
<div class="h-px w-6 mx-auto my-2 bg-button-bg"></div>
|
||||
<div class="h-px w-6 mx-auto my-2 bg-surface-5"></div>
|
||||
<suspense>
|
||||
<QuickInstanceSwitcher />
|
||||
</suspense>
|
||||
|
||||
@@ -284,7 +284,7 @@ onUnmounted(() => {
|
||||
z-index: 11;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
width: max-content;
|
||||
user-select: none;
|
||||
-ms-user-select: none;
|
||||
@@ -380,7 +380,7 @@ onUnmounted(() => {
|
||||
text-align: left;
|
||||
|
||||
&.expanded {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ onBeforeUnmount(() => {
|
||||
background-color: var(--color-raised-bg);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-floating);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
z-index: 1000000;
|
||||
@@ -163,7 +163,7 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
|
||||
.divider {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
margin: var(--gap-sm);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</div>
|
||||
<div class="input-row">
|
||||
<p class="input-label">Game version</p>
|
||||
<div class="versions">
|
||||
<div class="flex gap-4 items-center">
|
||||
<multiselect
|
||||
v-model="game_version"
|
||||
class="selector"
|
||||
@@ -45,7 +45,7 @@
|
||||
open-direction="top"
|
||||
:show-labels="false"
|
||||
/>
|
||||
<Checkbox v-model="showSnapshots" class="filter-checkbox" label="Show all versions" />
|
||||
<Checkbox v-model="showSnapshots" class="shrink-0" label="Show all versions" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="loader !== 'vanilla'" class="input-row">
|
||||
@@ -546,12 +546,6 @@ const next = async () => {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.versions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
:deep(button.checkbox) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ onUnmounted(() => {
|
||||
<SpinnerIcon class="animate-spin w-4 h-4" />
|
||||
</div>
|
||||
</NavButton>
|
||||
<div v-if="recentInstances.length > 0" class="h-px w-6 mx-auto my-2 bg-button-bg"></div>
|
||||
<div v-if="recentInstances.length > 0" class="h-px w-6 mx-auto my-2 bg-divider"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -293,7 +293,7 @@ onBeforeUnmount(() => {
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
padding: var(--gap-sm) var(--gap-lg);
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ onBeforeUnmount(() => {
|
||||
gap: 1rem;
|
||||
overflow: auto;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
|
||||
&.hidden {
|
||||
transform: translateY(-100%);
|
||||
@@ -454,7 +454,7 @@ onBeforeUnmount(() => {
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
transition: all 0.2s ease-in-out;
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
padding: var(--gap-md);
|
||||
|
||||
&.hidden {
|
||||
|
||||
@@ -130,7 +130,7 @@ onUnmounted(() => {
|
||||
/>
|
||||
</template>
|
||||
<div
|
||||
class="grid grid-cols-[auto_minmax(0,3fr)_minmax(0,4fr)_auto] items-center gap-2 p-3 bg-bg-raised rounded-xl smart-clickable:highlight-on-hover"
|
||||
class="grid grid-cols-[auto_minmax(0,3fr)_minmax(0,4fr)_auto] items-center gap-2 p-3 bg-bg-raised card-shadow rounded-xl smart-clickable:highlight-on-hover"
|
||||
>
|
||||
<Avatar
|
||||
:src="instanceIcon ? convertFileSrc(instanceIcon) : undefined"
|
||||
|
||||
@@ -181,7 +181,7 @@ const messages = defineMessages({
|
||||
/>
|
||||
</template>
|
||||
<div
|
||||
class="grid grid-cols-[auto_minmax(0,3fr)_minmax(0,4fr)_auto] items-center gap-2 p-3 bg-bg-raised smart-clickable:highlight-on-hover rounded-xl"
|
||||
class="grid grid-cols-[auto_minmax(0,3fr)_minmax(0,4fr)_auto] items-center gap-2 p-3 bg-bg-raised card-shadow smart-clickable:highlight-on-hover rounded-xl"
|
||||
:class="{
|
||||
'world-item-highlighted': highlighted,
|
||||
}"
|
||||
|
||||
@@ -427,7 +427,7 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
|
||||
<div v-else class="flex items-center justify-center min-h-[50vh] pt-[25%]">
|
||||
<div
|
||||
class="bg-bg-raised rounded-lg p-7 flex flex-col gap-5 shadow-md relative max-w-xl w-full mx-auto"
|
||||
class="bg-bg-raised card-shadow rounded-lg p-7 flex flex-col gap-5 shadow-md relative max-w-xl w-full mx-auto"
|
||||
>
|
||||
<img
|
||||
:src="ExcitedRinthbot"
|
||||
|
||||
@@ -1290,3 +1290,7 @@ svg.inline-svg {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ html {
|
||||
--icon-32: 2rem;
|
||||
|
||||
interpolate-size: allow-keywords;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.light-mode {
|
||||
@@ -89,7 +90,7 @@ html {
|
||||
--color-hr: var(--color-text);
|
||||
|
||||
--color-table-border: #dfe2e5;
|
||||
--color-table-alternate-row: #f2f4f7;
|
||||
--color-table-alternate-row: #f0f1f2;
|
||||
|
||||
--landing-maze-bg: url('https://cdn.modrinth.com/landing-new/landing-light.webp');
|
||||
--landing-maze-gradient-bg: url('https://cdn.modrinth.com/landing-new/landing-lower-light.webp');
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="checkbox-outer button-within"
|
||||
:class="{ disabled, checked: modelValue }"
|
||||
role="presentation"
|
||||
@click="toggle"
|
||||
>
|
||||
<button
|
||||
class="checkbox"
|
||||
role="checkbox"
|
||||
:disabled="disabled"
|
||||
:class="{ checked: modelValue, collapsing: collapsingToggleStyle }"
|
||||
:aria-label="description ?? label"
|
||||
:aria-checked="modelValue"
|
||||
>
|
||||
<CheckIcon v-if="modelValue && !collapsingToggleStyle" aria-hidden="true" />
|
||||
<DropdownIcon v-else-if="collapsingToggleStyle" aria-hidden="true" />
|
||||
</button>
|
||||
<!-- aria-hidden is set so screenreaders only use the <button>'s aria-label -->
|
||||
<p v-if="label" aria-hidden="true">
|
||||
{{ label }}
|
||||
</p>
|
||||
<slot v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { CheckIcon, DropdownIcon } from '@modrinth/assets'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CheckIcon,
|
||||
DropdownIcon,
|
||||
},
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
modelValue: Boolean,
|
||||
clickEvent: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: () => {},
|
||||
},
|
||||
collapsingToggleStyle: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
methods: {
|
||||
toggle() {
|
||||
if (!this.disabled) {
|
||||
this.$emit('update:modelValue', !this.modelValue)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.checkbox-outer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
user-select: none;
|
||||
padding: 0.2rem 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.checked {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 4px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
min-width: 1rem;
|
||||
min-height: 1rem;
|
||||
|
||||
padding: 0;
|
||||
margin: 0 0.5rem 0 0;
|
||||
|
||||
color: var(--color-button-text);
|
||||
background-color: var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-control);
|
||||
box-shadow:
|
||||
var(--shadow-inset-sm),
|
||||
0 0 0 0 transparent;
|
||||
|
||||
&.checked {
|
||||
background-color: var(--color-brand);
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--color-accent-contrast, var(--color-brand-inverted));
|
||||
stroke-width: 0.2rem;
|
||||
height: 0.8rem;
|
||||
width: 0.8rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.collapsing {
|
||||
background-color: transparent !important;
|
||||
box-shadow: none;
|
||||
|
||||
svg {
|
||||
color: inherit;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
&.checked {
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<nav :aria-label="ariaLabel" class="w-full">
|
||||
<ul class="m-0 flex list-none flex-col items-start gap-1.5 rounded-2xl bg-bg-raised p-4">
|
||||
<ul
|
||||
class="card-shadow m-0 flex list-none flex-col items-start gap-1.5 rounded-2xl bg-bg-raised p-4"
|
||||
>
|
||||
<slot v-if="hasSlotContent" />
|
||||
|
||||
<template v-else>
|
||||
|
||||
@@ -152,7 +152,7 @@ const onSubmitHandler = () => {
|
||||
border-radius: var(--radius-md);
|
||||
overflow: hidden;
|
||||
margin-top: var(--gap-md);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
background-color: var(--color-raised-bg);
|
||||
|
||||
.table-row {
|
||||
|
||||
@@ -356,7 +356,7 @@ svg {
|
||||
:deep(.apexcharts-yaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
box-shadow: var(--shadow-floating) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
}
|
||||
@@ -371,7 +371,7 @@ svg {
|
||||
:deep(.apexcharts-xaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
color: var(--color-base) !important;
|
||||
|
||||
|
||||
@@ -891,7 +891,7 @@ const defaultRanges: RangeObject[] = [
|
||||
flex-direction: column;
|
||||
background-color: var(--color-bg);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
gap: var(--gap-md);
|
||||
padding: var(--gap-md);
|
||||
margin-top: var(--gap-md);
|
||||
@@ -920,7 +920,7 @@ const defaultRanges: RangeObject[] = [
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
background-color: var(--color-raised-bg);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
border-radius: var(--radius-sm);
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
@@ -159,10 +159,10 @@ defineExpose({
|
||||
flex-direction: column;
|
||||
|
||||
gap: var(--gap-xs);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-floating);
|
||||
box-shadow: var(--shadow-card);
|
||||
|
||||
color: var(--color-base);
|
||||
font-size: var(--font-size-nm);
|
||||
@@ -192,7 +192,7 @@ svg {
|
||||
:deep(.apexcharts-yaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
box-shadow: var(--shadow-floating) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<template>
|
||||
<Checkbox
|
||||
class="filter"
|
||||
:model-value="activeFilters.includes(facetName)"
|
||||
:description="displayName"
|
||||
@update:model-value="toggle()"
|
||||
>
|
||||
<div class="filter-text">
|
||||
<div v-if="icon" aria-hidden="true" class="icon" v-html="icon" />
|
||||
<div v-else class="icon">
|
||||
<slot />
|
||||
</div>
|
||||
<span aria-hidden="true"> {{ displayName }}</span>
|
||||
</div>
|
||||
</Checkbox>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Checkbox from '~/components/ui/Checkbox.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Checkbox,
|
||||
},
|
||||
props: {
|
||||
facetName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
displayName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
activeFilters: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
emits: ['toggle'],
|
||||
methods: {
|
||||
toggle() {
|
||||
this.$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;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -245,13 +245,20 @@ import {
|
||||
LockOpenIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { Admonition, Avatar, ButtonStyled, Combobox, CopyCode, NewModal } from '@modrinth/ui'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
Combobox,
|
||||
CopyCode,
|
||||
NewModal,
|
||||
} from '@modrinth/ui'
|
||||
import TagItem from '@modrinth/ui/src/components/base/TagItem.vue'
|
||||
import { formatCategory, formatVersionsForDisplay, type Mod, type Version } from '@modrinth/utils'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import Accordion from '~/components/ui/Accordion.vue'
|
||||
import Checkbox from '~/components/ui/Checkbox.vue'
|
||||
import ContentVersionFilter, {
|
||||
type ListedGameVersion,
|
||||
type ListedPlatform,
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
v-for="(option, index) in filteredOptions"
|
||||
:key="isDivider(option) ? `divider-${index}` : option.id"
|
||||
>
|
||||
<div v-if="isDivider(option)" class="h-px w-full bg-button-bg"></div>
|
||||
<div v-if="isDivider(option)" class="h-px w-full bg-surface-5"></div>
|
||||
<ButtonStyled v-else type="transparent" role="menuitem" :color="option.color">
|
||||
<button
|
||||
v-if="typeof option.action === 'function'"
|
||||
@@ -288,19 +288,10 @@ const handleMouseOver = (index: number) => {
|
||||
|
||||
// Scrolling is disabled for keyboard navigation
|
||||
const disableBodyScroll = () => {
|
||||
// Make opening not shift page when there's a vertical scrollbar
|
||||
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth
|
||||
if (scrollBarWidth > 0) {
|
||||
document.body.style.paddingRight = `${scrollBarWidth}px`
|
||||
} else {
|
||||
document.body.style.paddingRight = ''
|
||||
}
|
||||
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
const enableBodyScroll = () => {
|
||||
document.body.style.paddingRight = ''
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
|
||||
|
||||
@@ -261,9 +261,14 @@ import {
|
||||
SendIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { CopyCode, injectNotificationManager, MarkdownEditor, OverflowMenu } from '@modrinth/ui'
|
||||
import {
|
||||
Checkbox,
|
||||
CopyCode,
|
||||
injectNotificationManager,
|
||||
MarkdownEditor,
|
||||
OverflowMenu,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import Checkbox from '~/components/ui/Checkbox.vue'
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
import ThreadMessage from '~/components/ui/thread/ThreadMessage.vue'
|
||||
import { useImageUpload } from '~/composables/image-upload.ts'
|
||||
|
||||
@@ -220,8 +220,15 @@
|
||||
class="experimental-styles-within desktop-only relative z-[5] mx-auto grid max-w-[1280px] grid-cols-[1fr_auto] items-center gap-2 px-6 py-4 lg:grid-cols-[auto_1fr_auto]"
|
||||
>
|
||||
<div>
|
||||
<NuxtLink to="/" :aria-label="formatMessage(messages.modrinthHomePage)">
|
||||
<TextLogo aria-hidden="true" class="h-7 w-auto text-contrast" />
|
||||
<NuxtLink
|
||||
to="/"
|
||||
:aria-label="formatMessage(messages.modrinthHomePage)"
|
||||
class="group hover:brightness-[--hover-brightness] focus-visible:brightness-[--hover-brightness]"
|
||||
>
|
||||
<TextLogo
|
||||
aria-hidden="true"
|
||||
class="h-7 w-auto text-contrast transition-transform group-active:scale-[0.98]"
|
||||
/>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div
|
||||
@@ -369,7 +376,7 @@
|
||||
formatMessage(navMenuMessages.discoverContent)
|
||||
}}</span>
|
||||
<span class="contents md:hidden">{{ formatMessage(navMenuMessages.discover) }}</span>
|
||||
<DropdownIcon aria-hidden="true" class="h-5 w-5 text-secondary" />
|
||||
<DropdownIcon aria-hidden="true" class="h-5 w-5" />
|
||||
|
||||
<template #mods>
|
||||
<BoxIcon aria-hidden="true" />
|
||||
|
||||
@@ -547,14 +547,8 @@
|
||||
</div>
|
||||
</template>
|
||||
</Tooltip>
|
||||
<ClientOnly>
|
||||
<ButtonStyled
|
||||
size="large"
|
||||
circular
|
||||
:color="following ? 'red' : 'standard'"
|
||||
color-fill="none"
|
||||
hover-color-fill="background"
|
||||
>
|
||||
<ButtonStyled size="large" circular>
|
||||
<ClientOnly>
|
||||
<button
|
||||
v-if="auth.user"
|
||||
v-tooltip="
|
||||
@@ -579,94 +573,74 @@
|
||||
>
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu
|
||||
v-if="auth.user"
|
||||
:tooltip="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? formatMessage(commonMessages.savedLabel)
|
||||
: formatMessage(commonMessages.saveButton)
|
||||
"
|
||||
from="top-right"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
:dropdown-id="`${baseId}-save`"
|
||||
>
|
||||
<BookmarkIcon
|
||||
aria-hidden="true"
|
||||
:fill="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? 'currentColor'
|
||||
: 'none'
|
||||
"
|
||||
/>
|
||||
<template #menu>
|
||||
<input
|
||||
v-model="displayCollectionsSearch"
|
||||
type="text"
|
||||
:placeholder="formatMessage(commonMessages.searchPlaceholder)"
|
||||
class="search-input menu-search"
|
||||
/>
|
||||
<div v-if="collections.length > 0" class="collections-list text-primary">
|
||||
<Checkbox
|
||||
v-for="option in collections
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name))"
|
||||
:key="option.id"
|
||||
:model-value="option.projects.includes(project.id)"
|
||||
class="popout-checkbox"
|
||||
@update:model-value="() => onUserCollectProject(option, project.id)"
|
||||
>
|
||||
{{ option.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">{{ formatMessage(messages.noCollectionsFound) }}</p>
|
||||
</div>
|
||||
<button
|
||||
class="btn collection-button"
|
||||
@click="(event) => $refs.modal_collection.show(event)"
|
||||
>
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createNewCollection) }}
|
||||
</button>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link v-else v-tooltip="'Save'" to="/auth/sign-in" aria-label="Save">
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<template #fallback>
|
||||
<ButtonStyled size="large" circular>
|
||||
<button
|
||||
v-if="auth.user"
|
||||
:v-tooltip="formatMessage(commonMessages.followButton)"
|
||||
:aria-label="formatMessage(commonMessages.followButton)"
|
||||
@click="userFollowProject(project)"
|
||||
>
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</button>
|
||||
<template #fallback>
|
||||
<nuxt-link
|
||||
v-else
|
||||
v-tooltip="formatMessage(commonMessages.followButton)"
|
||||
to="/auth/sign-in"
|
||||
:aria-label="formatMessage(commonMessages.followButton)"
|
||||
>
|
||||
<HeartIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<nuxt-link
|
||||
v-tooltip="formatMessage(commonMessages.saveButton)"
|
||||
to="/auth/sign-in"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
</template>
|
||||
</ClientOnly>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu
|
||||
v-if="auth.user"
|
||||
:tooltip="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? formatMessage(commonMessages.savedLabel)
|
||||
: formatMessage(commonMessages.saveButton)
|
||||
"
|
||||
from="top-right"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
:dropdown-id="`${baseId}-save`"
|
||||
>
|
||||
<BookmarkIcon
|
||||
aria-hidden="true"
|
||||
:fill="
|
||||
collections.some((x) => x.projects.includes(project.id))
|
||||
? 'currentColor'
|
||||
: 'none'
|
||||
"
|
||||
/>
|
||||
<template #menu>
|
||||
<input
|
||||
v-model="displayCollectionsSearch"
|
||||
type="text"
|
||||
:placeholder="formatMessage(commonMessages.searchPlaceholder)"
|
||||
class="search-input menu-search"
|
||||
/>
|
||||
<div v-if="collections.length > 0" class="collections-list text-primary">
|
||||
<Checkbox
|
||||
v-for="option in collections
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name))"
|
||||
:key="option.id"
|
||||
:model-value="option.projects.includes(project.id)"
|
||||
class="popout-checkbox"
|
||||
@update:model-value="() => onUserCollectProject(option, project.id)"
|
||||
>
|
||||
{{ option.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">{{ formatMessage(messages.noCollectionsFound) }}</p>
|
||||
</div>
|
||||
<button
|
||||
class="btn collection-button"
|
||||
@click="(event) => $refs.modal_collection.show(event)"
|
||||
>
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</ClientOnly>
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createNewCollection) }}
|
||||
</button>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link v-else v-tooltip="'Save'" to="/auth/sign-in" aria-label="Save">
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="auth.user && currentMember" size="large" circular>
|
||||
<nuxt-link
|
||||
v-tooltip="formatMessage(commonMessages.settingsLabel)"
|
||||
@@ -1672,10 +1646,12 @@ const projectTypeDisplay = computed(() =>
|
||||
),
|
||||
)
|
||||
|
||||
const following = computed(
|
||||
() =>
|
||||
user.value && user.value.follows && user.value.follows.find((x) => x.id === project.value.id),
|
||||
)
|
||||
const following = computed(() => {
|
||||
if (!user.value?.follows) {
|
||||
return false
|
||||
}
|
||||
return !!user.value.follows.find((x) => x.id === project.value.id)
|
||||
})
|
||||
|
||||
const title = computed(() => `${project.value.title} - Minecraft ${projectTypeDisplay.value}`)
|
||||
const description = computed(
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</p>
|
||||
<template v-else>
|
||||
<template v-for="header in Object.keys(categoryLists)" :key="`categories-${header}`">
|
||||
<div class="label">
|
||||
<div class="label mb-3">
|
||||
<h4>
|
||||
<span class="label__title">{{ formatCategoryHeader(header) }}</span>
|
||||
</h4>
|
||||
@@ -134,6 +134,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SaveIcon, StarIcon, TriangleAlertIcon } from '@modrinth/assets'
|
||||
import { Checkbox } from '@modrinth/ui'
|
||||
import {
|
||||
formatCategory,
|
||||
formatCategoryHeader,
|
||||
@@ -143,8 +144,6 @@ import {
|
||||
} from '@modrinth/utils'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import Checkbox from '~/components/ui/Checkbox.vue'
|
||||
|
||||
interface Category {
|
||||
name: string
|
||||
header: string
|
||||
@@ -337,7 +336,7 @@ const saveChanges = () => {
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
|
||||
:deep(.category-selector) {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
|
||||
.category-selector__label {
|
||||
display: flex;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<Modal ref="editLinksModal" header="Edit links">
|
||||
<div class="universal-modal links-modal">
|
||||
<NewModal ref="editLinksModal" header="Edit links">
|
||||
<div class="universal-modal links-modal !p-0">
|
||||
<p>
|
||||
Any links you specify below will be overwritten on each of the selected projects. Any you
|
||||
leave blank will be ignored. You can clear a link from all selected projects using the
|
||||
@@ -139,10 +139,8 @@
|
||||
<Checkbox
|
||||
v-if="selectedProjects.length > 3"
|
||||
v-model="editLinks.showAffected"
|
||||
:label="editLinks.showAffected ? 'Less' : 'More'"
|
||||
description="Show all loaders"
|
||||
:border="false"
|
||||
:collapsing-toggle-style="true"
|
||||
label="Show all projects"
|
||||
description="Show all projects"
|
||||
/>
|
||||
<div class="push-right input-group">
|
||||
<button class="iconified-button" @click="$refs.editLinksModal.hide()">
|
||||
@@ -155,7 +153,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</NewModal>
|
||||
<ModalCreation ref="modal_creation" />
|
||||
<section class="universal-card">
|
||||
<div class="header__row">
|
||||
@@ -335,13 +333,13 @@ import {
|
||||
commonMessages,
|
||||
CopyCode,
|
||||
injectNotificationManager,
|
||||
NewModal,
|
||||
ProjectStatusBadge,
|
||||
} from '@modrinth/ui'
|
||||
import { formatProjectType } from '@modrinth/utils'
|
||||
import { Multiselect } from 'vue-multiselect'
|
||||
|
||||
import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
import Modal from '~/components/ui/Modal.vue'
|
||||
import { getProjectTypeForUrl } from '~/helpers/projects.js'
|
||||
|
||||
useHead({ title: 'Projects - Modrinth' })
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
class="flex flex-row justify-between border-0 !border-b-[2px] border-solid border-button-bg p-1.5 md:p-2"
|
||||
class="flex flex-row justify-between border-0 !border-b-[2px] border-solid border-divider p-1.5 md:p-2"
|
||||
>
|
||||
<span class="my-auto flex flex-row items-center gap-2 text-sm leading-none md:text-base"
|
||||
><span
|
||||
@@ -58,7 +58,7 @@
|
||||
<div
|
||||
v-for="(date, i) in dateSegments"
|
||||
:key="date.date"
|
||||
class="flex flex-row justify-between border-0 !border-b-[2px] border-solid border-button-bg p-1.5 md:p-2"
|
||||
class="flex flex-row justify-between border-0 !border-b-[2px] border-solid border-divider p-1.5 md:p-2"
|
||||
>
|
||||
<span class="my-auto flex flex-row items-center gap-2 text-sm leading-none md:text-base">
|
||||
<span
|
||||
@@ -123,7 +123,7 @@
|
||||
}}</span>
|
||||
<div class="grid grid-cols-1 gap-x-4 gap-y-2 md:grid-cols-2 lg:grid-cols-3">
|
||||
<button
|
||||
class="relative flex flex-col overflow-hidden rounded-2xl bg-gradient-to-r from-green to-green-700 p-4 text-inverted shadow-md transition-all duration-200 hover:brightness-105 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:brightness-100 md:p-5"
|
||||
class="relative flex flex-col overflow-hidden rounded-2xl bg-gradient-to-r from-green to-green-700 p-4 text-inverted shadow-md transition-all duration-200 hover:brightness-105 active:scale-[0.98] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:brightness-100 md:p-5"
|
||||
:disabled="hasTinMismatch"
|
||||
@click="openWithdrawModal"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="normal-page__content">
|
||||
<Modal ref="editLinksModal" header="Edit links">
|
||||
<div class="universal-modal links-modal">
|
||||
<div>
|
||||
<NewModal ref="editLinksModal" header="Edit links">
|
||||
<div class="universal-modal links-modal !p-0">
|
||||
<p>
|
||||
Any links you specify below will be overwritten on each of the selected projects. Any you
|
||||
leave blank will be ignored. You can clear a link from all selected projects using the
|
||||
@@ -25,16 +25,15 @@
|
||||
"
|
||||
maxlength="2048"
|
||||
/>
|
||||
<Button
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.issues.clear"
|
||||
icon-only
|
||||
@click="editLinks.issues.clear = !editLinks.issues.clear"
|
||||
>
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
<label
|
||||
for="source-code-input"
|
||||
@@ -53,15 +52,15 @@
|
||||
editLinks.source.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.source.clear"
|
||||
icon-only
|
||||
@click="editLinks.source.clear = !editLinks.source.clear"
|
||||
>
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
<label
|
||||
for="wiki-page-input"
|
||||
@@ -80,15 +79,15 @@
|
||||
editLinks.wiki.clear ? 'Existing link will be cleared' : 'Enter a valid URL'
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.wiki.clear"
|
||||
icon-only
|
||||
@click="editLinks.wiki.clear = !editLinks.wiki.clear"
|
||||
>
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
<label for="discord-invite-input" title="An invitation link to your Discord server.">
|
||||
<span class="label__title">Discord invite</span>
|
||||
@@ -106,15 +105,15 @@
|
||||
: 'Enter a valid Discord invite URL'
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
<button
|
||||
v-tooltip="'Clear link'"
|
||||
aria-label="Clear link"
|
||||
class="square-button label-button"
|
||||
:data-active="editLinks.discord.clear"
|
||||
icon-only
|
||||
@click="editLinks.discord.clear = !editLinks.discord.clear"
|
||||
>
|
||||
<TrashIcon />
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<p>
|
||||
@@ -140,35 +139,35 @@
|
||||
<Checkbox
|
||||
v-if="selectedProjects.length > 3"
|
||||
v-model="editLinks.showAffected"
|
||||
:label="editLinks.showAffected ? 'Less' : 'More'"
|
||||
description="Show all loaders"
|
||||
:border="false"
|
||||
:collapsing-toggle-style="true"
|
||||
label="Show all projects"
|
||||
description="Show all projects"
|
||||
/>
|
||||
<div class="push-right input-group">
|
||||
<Button @click="$refs.editLinksModal.hide()">
|
||||
<button class="iconified-button" @click="$refs.editLinksModal.hide()">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</Button>
|
||||
<Button color="primary" @click="onBulkEditLinks">
|
||||
</button>
|
||||
<button class="iconified-button brand-button" @click="onBulkEditLinks">
|
||||
<SaveIcon />
|
||||
Save changes
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</NewModal>
|
||||
<ModalCreation ref="modal_creation" :organization-id="organization.id" />
|
||||
<div class="universal-card">
|
||||
<h2>Projects</h2>
|
||||
<div class="input-group">
|
||||
<Button color="primary" @click="$refs.modal_creation.show()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(commonMessages.createAProjectButton) }}
|
||||
</Button>
|
||||
<OrganizationProjectTransferModal
|
||||
:projects="usersOwnedProjects || []"
|
||||
@submit="onProjectTransferSubmit"
|
||||
/>
|
||||
<section class="universal-card">
|
||||
<div class="header__row">
|
||||
<h2 class="header__title text-2xl">Projects</h2>
|
||||
<div class="input-group">
|
||||
<button class="iconified-button brand-button" @click="$refs.modal_creation.show()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(commonMessages.createAProjectButton) }}
|
||||
</button>
|
||||
<OrganizationProjectTransferModal
|
||||
:projects="usersOwnedProjects || []"
|
||||
@submit="onProjectTransferSubmit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="sortedProjects.length < 1">
|
||||
You don't have any projects yet. Click the green button above to begin.
|
||||
@@ -176,10 +175,14 @@
|
||||
<template v-else>
|
||||
<p>You can edit multiple projects at once by selecting them below.</p>
|
||||
<div class="input-group">
|
||||
<Button :disabled="selectedProjects.length === 0" @click="$refs.editLinksModal.show()">
|
||||
<button
|
||||
class="iconified-button"
|
||||
:disabled="selectedProjects.length === 0"
|
||||
@click="$refs.editLinksModal.show()"
|
||||
>
|
||||
<EditIcon />
|
||||
Edit links
|
||||
</Button>
|
||||
</button>
|
||||
<div class="push-right">
|
||||
<div class="labeled-control-row">
|
||||
Sort by
|
||||
@@ -195,21 +198,20 @@
|
||||
sortedProjects = updateSort(sortedProjects, sortBy, descending)
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
<button
|
||||
v-tooltip="descending ? 'Descending' : 'Ascending'"
|
||||
class="square-button"
|
||||
icon-only
|
||||
@click="updateDescending()"
|
||||
>
|
||||
<SortDescIcon v-if="descending" />
|
||||
<SortAscIcon v-else />
|
||||
</Button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="table-head table-row">
|
||||
<div class="check-cell table-cell">
|
||||
<div class="grid-table">
|
||||
<div class="grid-table__row grid-table__header">
|
||||
<div>
|
||||
<Checkbox
|
||||
:model-value="selectedProjects === sortedProjects"
|
||||
@update:model-value="
|
||||
@@ -219,15 +221,19 @@
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-cell">Icon</div>
|
||||
<div class="table-cell">Name</div>
|
||||
<div class="table-cell">ID</div>
|
||||
<div class="table-cell">Type</div>
|
||||
<div class="table-cell">Status</div>
|
||||
<div class="table-cell" />
|
||||
<div>Icon</div>
|
||||
<div>Name</div>
|
||||
<div>ID</div>
|
||||
<div>Type</div>
|
||||
<div>Status</div>
|
||||
<div />
|
||||
</div>
|
||||
<div v-for="project in sortedProjects" :key="`project-${project.id}`" class="table-row">
|
||||
<div class="check-cell table-cell">
|
||||
<div
|
||||
v-for="project in sortedProjects"
|
||||
:key="`project-${project.id}`"
|
||||
class="grid-table__row"
|
||||
>
|
||||
<div>
|
||||
<Checkbox
|
||||
:disabled="(project.permissions & EDIT_DETAILS) === EDIT_DETAILS"
|
||||
:model-value="selectedProjects.includes(project)"
|
||||
@@ -238,8 +244,13 @@
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-cell">
|
||||
<nuxt-link tabindex="-1" :to="`/project/${project.slug ? project.slug : project.id}`">
|
||||
<div>
|
||||
<nuxt-link
|
||||
tabindex="-1"
|
||||
:to="`/${getProjectTypeForUrl(project.project_types[0] ?? 'project', project.loaders)}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}`"
|
||||
>
|
||||
<Avatar
|
||||
:src="project.icon_url"
|
||||
aria-hidden="true"
|
||||
@@ -249,7 +260,7 @@
|
||||
</nuxt-link>
|
||||
</div>
|
||||
|
||||
<div class="table-cell">
|
||||
<div>
|
||||
<span class="project-title">
|
||||
<IssuesIcon
|
||||
v-if="project.moderator_message"
|
||||
@@ -258,48 +269,52 @@
|
||||
|
||||
<nuxt-link
|
||||
class="hover-link wrap-as-needed"
|
||||
:to="`/project/${project.slug ? project.slug : project.id}`"
|
||||
:to="`/${getProjectTypeForUrl(project.project_types[0] ?? 'project', project.loaders)}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}`"
|
||||
>
|
||||
{{ project.name }}
|
||||
</nuxt-link>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="table-cell">
|
||||
<div>
|
||||
<CopyCode :text="project.id" />
|
||||
</div>
|
||||
|
||||
<div class="table-cell">
|
||||
<BoxIcon />
|
||||
<span>{{
|
||||
<div>
|
||||
{{
|
||||
formatProjectType(
|
||||
$getProjectTypeForDisplay(project.project_types[0] ?? 'project', project.loaders),
|
||||
getProjectTypeForUrl(project.project_types[0] ?? 'project', project.loaders),
|
||||
)
|
||||
}}</span>
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div class="table-cell">
|
||||
<Badge v-if="project.status" :type="project.status" class="status" />
|
||||
<div>
|
||||
<ProjectStatusBadge v-if="project.status" :status="project.status" />
|
||||
</div>
|
||||
|
||||
<div class="table-cell">
|
||||
<nuxt-link
|
||||
class="btn icon-only"
|
||||
:to="`/project/${project.slug ? project.slug : project.id}/settings`"
|
||||
>
|
||||
<SettingsIcon />
|
||||
</nuxt-link>
|
||||
<div class="flex !flex-row items-center !justify-end gap-2">
|
||||
<ButtonStyled circular>
|
||||
<nuxt-link
|
||||
v-tooltip="formatMessage(commonMessages.settingsLabel)"
|
||||
:to="`/${getProjectTypeForUrl(project.project_types[0] ?? 'project', project.loaders)}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/settings`"
|
||||
>
|
||||
<SettingsIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
BoxIcon,
|
||||
EditIcon,
|
||||
IssuesIcon,
|
||||
PlusIcon,
|
||||
@@ -312,19 +327,20 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
CopyCode,
|
||||
injectNotificationManager,
|
||||
Modal,
|
||||
NewModal,
|
||||
ProjectStatusBadge,
|
||||
} from '@modrinth/ui'
|
||||
import { formatProjectType } from '@modrinth/utils'
|
||||
import { Multiselect } from 'vue-multiselect'
|
||||
|
||||
import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue'
|
||||
import OrganizationProjectTransferModal from '~/components/ui/OrganizationProjectTransferModal.vue'
|
||||
import { getProjectTypeForUrl } from '~/helpers/projects.js'
|
||||
import { injectOrganizationContext } from '~/providers/organization-context.ts'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
@@ -423,10 +439,12 @@ const updateSort = (inputProjects, sort, descending) => {
|
||||
break
|
||||
case 'Type':
|
||||
sortedArray = inputProjects.slice().sort((a, b) => {
|
||||
if (a.project_type < b.project_type) {
|
||||
const aType = a.project_types?.[0] ?? 'project'
|
||||
const bType = b.project_types?.[0] ?? 'project'
|
||||
if (aType < bType) {
|
||||
return -1
|
||||
}
|
||||
if (a.project_type > b.project_type) {
|
||||
if (aType > bType) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
@@ -456,109 +474,107 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
const emptyLinksData = {
|
||||
const editLinks = reactive({
|
||||
showAffected: false,
|
||||
source: {
|
||||
val: '',
|
||||
clear: false,
|
||||
},
|
||||
discord: {
|
||||
val: '',
|
||||
clear: false,
|
||||
},
|
||||
wiki: {
|
||||
val: '',
|
||||
clear: false,
|
||||
},
|
||||
issues: {
|
||||
val: '',
|
||||
clear: false,
|
||||
},
|
||||
}
|
||||
|
||||
const editLinks = ref(emptyLinksData)
|
||||
source: { val: '', clear: false },
|
||||
discord: { val: '', clear: false },
|
||||
wiki: { val: '', clear: false },
|
||||
issues: { val: '', clear: false },
|
||||
})
|
||||
|
||||
const updateDescending = () => {
|
||||
descending.value = !descending.value
|
||||
sortedProjects.value = updateSort(sortedProjects.value, sortBy.value, descending.value)
|
||||
}
|
||||
|
||||
const onBulkEditLinks = useClientTry(async () => {
|
||||
const linkData = editLinks.value
|
||||
const onBulkEditLinks = async () => {
|
||||
try {
|
||||
const baseData = {
|
||||
issues_url: editLinks.value.issues.clear ? null : editLinks.value.issues.val.trim(),
|
||||
source_url: editLinks.value.source.clear ? null : editLinks.value.source.val.trim(),
|
||||
wiki_url: editLinks.value.wiki.clear ? null : editLinks.value.wiki.val.trim(),
|
||||
discord_url: editLinks.value.discord.clear ? null : editLinks.value.discord.val.trim(),
|
||||
}
|
||||
const filteredData = Object.fromEntries(Object.entries(baseData).filter(([, v]) => v !== ''))
|
||||
|
||||
const baseData = {}
|
||||
await useBaseFetch(`projects?ids=${JSON.stringify(selectedProjects.value.map((x) => x.id))}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(filteredData),
|
||||
})
|
||||
|
||||
if (linkData.issues.clear) {
|
||||
baseData.issues_url = null
|
||||
} else if (linkData.issues.val.trim().length > 0) {
|
||||
baseData.issues_url = linkData.issues.val.trim()
|
||||
editLinksModal.value?.hide()
|
||||
addNotification({
|
||||
title: 'Success',
|
||||
text: "Bulk edited selected project's links.",
|
||||
type: 'success',
|
||||
})
|
||||
selectedProjects.value = []
|
||||
|
||||
editLinks.value.issues.val = ''
|
||||
editLinks.value.source.val = ''
|
||||
editLinks.value.wiki.val = ''
|
||||
editLinks.value.discord.val = ''
|
||||
editLinks.value.issues.clear = false
|
||||
editLinks.value.source.clear = false
|
||||
editLinks.value.wiki.clear = false
|
||||
editLinks.value.discord.clear = false
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
title: 'An error occurred',
|
||||
text: e?.data?.description || e?.message || e || 'Unknown error',
|
||||
type: 'error',
|
||||
})
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
if (linkData.source.clear) {
|
||||
baseData.source_url = null
|
||||
} else if (linkData.source.val.trim().length > 0) {
|
||||
baseData.source_url = linkData.source.val.trim()
|
||||
}
|
||||
|
||||
if (linkData.wiki.clear) {
|
||||
baseData.wiki_url = null
|
||||
} else if (linkData.wiki.val.trim().length > 0) {
|
||||
baseData.wiki_url = linkData.wiki.val.trim()
|
||||
}
|
||||
|
||||
if (linkData.discord.clear) {
|
||||
baseData.discord_url = null
|
||||
} else if (linkData.discord.val.trim().length > 0) {
|
||||
baseData.discord_url = linkData.discord.val.trim()
|
||||
}
|
||||
|
||||
await useBaseFetch(`projects?ids=${JSON.stringify(selectedProjects.value.map((x) => x.id))}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(baseData),
|
||||
})
|
||||
|
||||
editLinksModal.value.hide()
|
||||
|
||||
addNotification({
|
||||
title: 'Success',
|
||||
text: "Bulk edited selected project's links.",
|
||||
type: 'success',
|
||||
})
|
||||
|
||||
selectedProjects.value = []
|
||||
editLinks.value = emptyLinksData
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.table {
|
||||
.grid-table {
|
||||
display: grid;
|
||||
border-radius: var(--radius-md);
|
||||
grid-template-columns:
|
||||
min-content min-content minmax(min-content, 2fr)
|
||||
minmax(min-content, 1fr) minmax(min-content, 1fr) minmax(min-content, 1fr) min-content;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
overflow: hidden;
|
||||
margin-top: var(--gap-md);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
background-color: var(--color-raised-bg);
|
||||
margin-top: var(--spacing-card-md);
|
||||
outline: 1px solid transparent;
|
||||
|
||||
.table-row {
|
||||
grid-template-columns: 2.75rem 3.75rem 2fr 1fr 1fr 1fr 3.5rem;
|
||||
}
|
||||
.grid-table__row {
|
||||
display: contents;
|
||||
|
||||
.table-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--gap-xs);
|
||||
padding: var(--gap-md);
|
||||
padding-left: 0;
|
||||
}
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-card-sm);
|
||||
|
||||
.check-cell {
|
||||
padding-left: var(--gap-md);
|
||||
&:first-child {
|
||||
padding-left: var(--spacing-card-bg);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: var(--spacing-card-bg);
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n + 1) > div {
|
||||
background-color: var(--color-table-alternate-row);
|
||||
}
|
||||
|
||||
&.grid-table__header > div {
|
||||
background-color: var(--color-bg);
|
||||
font-weight: bold;
|
||||
color: var(--color-text-dark);
|
||||
padding-top: var(--spacing-card-bg);
|
||||
padding-bottom: var(--spacing-card-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.table-row {
|
||||
.grid-table__row {
|
||||
display: grid;
|
||||
grid-template: 'checkbox icon name type settings' 'checkbox icon id status settings';
|
||||
grid-template-columns:
|
||||
@@ -596,7 +612,7 @@ const onBulkEditLinks = useClientTry(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
.table-head {
|
||||
.grid-table__header {
|
||||
grid-template: 'checkbox settings';
|
||||
grid-template-columns: min-content minmax(min-content, 1fr);
|
||||
|
||||
@@ -611,7 +627,7 @@ const onBulkEditLinks = useClientTry(async () => {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.table-row {
|
||||
.grid-table__row {
|
||||
display: grid;
|
||||
grid-template: 'checkbox icon name settings' 'checkbox icon id settings' 'checkbox icon type settings' 'checkbox icon status settings';
|
||||
grid-template-columns: min-content min-content minmax(min-content, 1fr) min-content;
|
||||
@@ -621,7 +637,7 @@ const onBulkEditLinks = useClientTry(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
.table-head {
|
||||
.grid-table__header {
|
||||
grid-template: 'checkbox settings';
|
||||
grid-template-columns: min-content minmax(min-content, 1fr);
|
||||
}
|
||||
@@ -652,13 +668,13 @@ const onBulkEditLinks = useClientTry(async () => {
|
||||
flex-direction: row;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: var(--gap-sm);
|
||||
gap: var(--spacing-card-md);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.small-select {
|
||||
width: fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.label-button[data-active='true'] {
|
||||
@@ -688,16 +704,4 @@ const onBulkEditLinks = useClientTry(async () => {
|
||||
margin: 0 0 var(--spacing-card-sm) 0;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-block: var(--gap-sm) var(--gap-lg);
|
||||
font-size: 2em;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
:deep(.checkbox-outer) {
|
||||
button.checkbox {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4 rounded-xl bg-bg-raised p-6">
|
||||
<div class="card-shadow flex flex-col gap-4 rounded-xl bg-bg-raised p-6">
|
||||
<template v-if="!prefilled || !currentItemValid">
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-lg font-bold text-contrast">
|
||||
|
||||
@@ -88,7 +88,10 @@
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div v-if="server && projectType.id === 'modpack'" class="rounded-2xl bg-bg-raised">
|
||||
<div
|
||||
v-if="server && projectType.id === 'modpack'"
|
||||
class="card-shadow rounded-2xl bg-bg-raised"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-2 px-6 py-4 text-contrast">
|
||||
<h3 class="m-0 text-lg">Options</h3>
|
||||
</div>
|
||||
@@ -107,7 +110,10 @@
|
||||
the selected modpack.
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="server && projectType.id !== 'modpack'" class="rounded-2xl bg-bg-raised p-4">
|
||||
<div
|
||||
v-if="server && projectType.id !== 'modpack'"
|
||||
class="card-shadow rounded-2xl bg-bg-raised p-4"
|
||||
>
|
||||
<Checkbox
|
||||
v-model="serverHideInstalled"
|
||||
label="Hide installed content"
|
||||
@@ -126,7 +132,7 @@
|
||||
:class="
|
||||
filtersMenuOpen
|
||||
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
|
||||
: 'rounded-2xl bg-bg-raised'
|
||||
: 'card-shadow rounded-2xl bg-bg-raised'
|
||||
"
|
||||
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
|
||||
content-class="mb-4 mx-3"
|
||||
|
||||
@@ -825,7 +825,7 @@ a,
|
||||
.v-popper--theme-dropdown,
|
||||
.v-popper--theme-dropdown.v-popper--theme-ribbit-popout {
|
||||
.v-popper__inner {
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
padding: var(--gap-sm) !important;
|
||||
width: fit-content !important;
|
||||
border-radius: var(--radius-md) !important;
|
||||
@@ -834,7 +834,7 @@ a,
|
||||
}
|
||||
|
||||
.v-popper__arrow-outer {
|
||||
border-color: var(--color-button-bg) !important;
|
||||
border-color: var(--color-divider) !important;
|
||||
}
|
||||
|
||||
.v-popper__arrow-inner {
|
||||
|
||||
@@ -76,7 +76,7 @@ pre {
|
||||
padding: 1em 1em 1em 1em;
|
||||
border-width: 5px;
|
||||
border-radius: 2em;
|
||||
border-color: var(--color-button-bg);
|
||||
border-color: var(--color-divider);
|
||||
overflow-x: hidden;
|
||||
|
||||
code {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
--surface-2: #f5f5f5;
|
||||
--surface-3: #f8f8f8;
|
||||
--surface-4: #ffffff;
|
||||
--surface-5: #e6e6e6;
|
||||
--surface-5: #dddddd;
|
||||
|
||||
--color-red-50: #fef2f2;
|
||||
--color-red-100: #fee5e7;
|
||||
@@ -97,8 +97,8 @@
|
||||
--color-button-border: rgba(161, 161, 161, 0.35);
|
||||
--color-scrollbar: #96a2b0;
|
||||
|
||||
--color-divider: var(--surface-2);
|
||||
--color-divider-dark: #c8cdd3;
|
||||
--color-divider: var(--surface-5);
|
||||
--color-divider-dark: var(--surface-5);
|
||||
|
||||
--color-base: var(--color-text-default);
|
||||
--color-secondary: var(--color-text-tertiary);
|
||||
@@ -207,6 +207,8 @@ html {
|
||||
--color-link: var(--color-blue) !important;
|
||||
--color-link-hover: var(--color-blue) !important; // DEPRECATED, use filters in future
|
||||
--color-link-active: var(--color-blue) !important; // DEPRECATED, use filters in future
|
||||
|
||||
--shadow-button: 0 1px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 2px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.light-mode,
|
||||
|
||||
@@ -164,23 +164,31 @@ function setColorFill(
|
||||
}
|
||||
|
||||
const colorVariables = computed(() => {
|
||||
const defaultShadow =
|
||||
props.type === 'standard' || props.type === 'highlight' || props.highlighted
|
||||
? 'var(--shadow-button)'
|
||||
: 'none'
|
||||
|
||||
if (props.highlighted) {
|
||||
const colors = {
|
||||
bg:
|
||||
props.highlightedStyle === 'main-nav-primary'
|
||||
? 'var(--color-brand-highlight)'
|
||||
? 'var(--color-button-bg-selected)'
|
||||
: 'var(--color-button-bg)',
|
||||
text: 'var(--color-contrast)',
|
||||
text:
|
||||
props.highlightedStyle === 'main-nav-primary'
|
||||
? 'var(--color-button-text-selected)'
|
||||
: 'var(--color-contrast)',
|
||||
icon:
|
||||
props.type === 'chip'
|
||||
? 'var(--color-contrast)'
|
||||
: props.highlightedStyle === 'main-nav-primary'
|
||||
? 'var(--color-brand)'
|
||||
? 'var(--color-button-text-selected)'
|
||||
: 'var(--color-contrast)',
|
||||
}
|
||||
const hoverColors = JSON.parse(JSON.stringify(colors))
|
||||
const boxShadow =
|
||||
props.type === 'chip' && colorVar.value ? `0 0 0 2px ${colorVar.value}` : 'none'
|
||||
props.type === 'chip' && colorVar.value ? `0 0 0 2px ${colorVar.value}` : defaultShadow
|
||||
return `--_bg: ${colors.bg}; --_text: ${colors.text}; --_icon: ${colors.icon}; --_hover-bg: ${hoverColors.bg}; --_hover-text: ${hoverColors.text}; --_hover-icon: ${hoverColors.icon}; --_box-shadow: ${boxShadow};`
|
||||
}
|
||||
|
||||
@@ -217,7 +225,8 @@ const colorVariables = computed(() => {
|
||||
)
|
||||
}
|
||||
|
||||
const boxShadow = props.type === 'chip' && colorVar.value ? `0 0 0 2px ${colorVar.value}` : 'none'
|
||||
const boxShadow =
|
||||
props.type === 'chip' && colorVar.value ? `0 0 0 2px ${colorVar.value}` : defaultShadow
|
||||
return `--_bg: ${colors.bg}; --_text: ${colors.text}; --_hover-bg: ${hoverColors.bg}; --_hover-text: ${hoverColors.text}; --_box-shadow: ${boxShadow};`
|
||||
})
|
||||
|
||||
|
||||
@@ -1,31 +1,38 @@
|
||||
<template>
|
||||
<div
|
||||
class="checkbox-outer button-within"
|
||||
:class="{ disabled }"
|
||||
role="presentation"
|
||||
<button
|
||||
class="group bg-transparent border-none p-0 m-0 flex items-center gap-3 checkbox-outer outline-offset-4 text-contrast"
|
||||
:disabled="disabled"
|
||||
:class="
|
||||
disabled
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: 'cursor-pointer hover:brightness-[--hover-brightness] focus-visible:brightness-[--hover-brightness]'
|
||||
"
|
||||
:aria-label="description"
|
||||
:aria-checked="modelValue"
|
||||
role="checkbox"
|
||||
@click="toggle"
|
||||
>
|
||||
<button
|
||||
class="checkbox border-none"
|
||||
role="checkbox"
|
||||
:disabled="disabled"
|
||||
:class="{ checked: modelValue, collapsing: collapsingToggleStyle }"
|
||||
:aria-label="description"
|
||||
:aria-checked="modelValue"
|
||||
<span
|
||||
class="w-5 h-5 rounded-md flex items-center justify-center border-[1px] border-solid"
|
||||
:class="
|
||||
(modelValue
|
||||
? 'bg-brand border-button-border text-brand-inverted'
|
||||
: 'bg-surface-2 border-surface-5') +
|
||||
(disabled ? '' : ' checkbox-shadow group-active:scale-95')
|
||||
"
|
||||
>
|
||||
<MinusIcon v-if="indeterminate" aria-hidden="true" />
|
||||
<CheckIcon v-else-if="modelValue && !collapsingToggleStyle" aria-hidden="true" />
|
||||
<DropdownIcon v-else-if="collapsingToggleStyle" aria-hidden="true" />
|
||||
</button>
|
||||
<MinusIcon v-if="indeterminate" aria-hidden="true" stroke-width="3" />
|
||||
<CheckIcon v-else-if="modelValue" aria-hidden="true" stroke-width="3" />
|
||||
</span>
|
||||
<!-- aria-hidden is set so screenreaders only use the <button>'s aria-label -->
|
||||
<p v-if="label" aria-hidden="true" class="checkbox-label">
|
||||
<span v-if="label" aria-hidden="true">
|
||||
{{ label }}
|
||||
</p>
|
||||
</span>
|
||||
<slot v-else />
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CheckIcon, DropdownIcon, MinusIcon } from '@modrinth/assets'
|
||||
import { CheckIcon, MinusIcon } from '@modrinth/assets'
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [boolean]
|
||||
@@ -38,7 +45,6 @@ const props = withDefaults(
|
||||
description?: string
|
||||
modelValue: boolean
|
||||
clickEvent?: () => void
|
||||
collapsingToggleStyle?: boolean
|
||||
indeterminate?: boolean
|
||||
}>(),
|
||||
{
|
||||
@@ -47,7 +53,6 @@ const props = withDefaults(
|
||||
description: '',
|
||||
modelValue: false,
|
||||
clickEvent: () => {},
|
||||
collapsingToggleStyle: false,
|
||||
indeterminate: false,
|
||||
},
|
||||
)
|
||||
@@ -60,86 +65,7 @@ function toggle() {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.checkbox-outer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
user-select: none;
|
||||
padding: 0.2rem 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
min-width: 1rem;
|
||||
min-height: 1rem;
|
||||
|
||||
padding: 0;
|
||||
margin: 0 0.5rem 0 0;
|
||||
|
||||
color: var(--color-contrast);
|
||||
background-color: var(--color-button-bg);
|
||||
border-radius: var(--radius-xs);
|
||||
box-shadow:
|
||||
var(--shadow-inset-sm),
|
||||
0 0 0 0 transparent;
|
||||
|
||||
&.checked {
|
||||
background-color: var(--color-brand);
|
||||
|
||||
svg {
|
||||
color: var(--color-accent-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--color-secondary);
|
||||
stroke-width: 0.2rem;
|
||||
height: 0.8rem;
|
||||
width: 0.8rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.collapsing {
|
||||
background-color: transparent !important;
|
||||
box-shadow: none;
|
||||
|
||||
svg {
|
||||
color: inherit;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
transition: transform 0.25s ease-in-out;
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.checked {
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
color: var(--color-base);
|
||||
.checkbox-shadow {
|
||||
box-shadow: 1px 1px 2px 0 rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -891,7 +891,7 @@ function openVideoModal() {
|
||||
}
|
||||
|
||||
.markdown-body-wrapper {
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
border-radius: var(--radius-md);
|
||||
width: 100%;
|
||||
padding: var(--radius-md);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div
|
||||
v-if="isDivider(option)"
|
||||
:key="`divider-${index}`"
|
||||
class="h-px mx-3 my-2 bg-button-bg"
|
||||
class="h-px mx-3 my-2 bg-surface-5"
|
||||
></div>
|
||||
<Button
|
||||
v-else
|
||||
|
||||
@@ -286,7 +286,7 @@ svg {
|
||||
:deep(.apexcharts-yaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
box-shadow: var(--shadow-floating) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
}
|
||||
@@ -301,7 +301,7 @@ svg {
|
||||
:deep(.apexcharts-xaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
color: var(--color-base) !important;
|
||||
|
||||
|
||||
@@ -161,10 +161,10 @@ const chartOptions = ref({
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap-xs);
|
||||
border: 1px solid var(--color-button-bg);
|
||||
border: 1px solid var(--color-divider);
|
||||
border-radius: var(--radius-md);
|
||||
background-color: var(--color-raised-bg);
|
||||
box-shadow: var(--shadow-floating);
|
||||
box-shadow: var(--shadow-card);
|
||||
color: var(--color-base);
|
||||
font-size: var(--font-size-nm);
|
||||
width: 100%;
|
||||
@@ -190,7 +190,7 @@ svg {
|
||||
:deep(.apexcharts-yaxistooltip) {
|
||||
background: var(--color-raised-bg) !important;
|
||||
border-radius: var(--radius-sm) !important;
|
||||
border: 1px solid var(--color-button-bg) !important;
|
||||
border: 1px solid var(--color-divider) !important;
|
||||
box-shadow: var(--shadow-floating) !important;
|
||||
font-size: var(--font-size-nm) !important;
|
||||
}
|
||||
|
||||
@@ -151,21 +151,10 @@ const visible = ref(false)
|
||||
const scrollContainer = ref<HTMLElement | null>(null)
|
||||
const { showTopFade, showBottomFade, checkScrollState } = useScrollIndicator(scrollContainer)
|
||||
|
||||
// make modal opening not shift page when there's a vertical scrollbar
|
||||
function addBodyPadding() {
|
||||
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth
|
||||
if (scrollBarWidth > 0) {
|
||||
document.body.style.paddingRight = `${scrollBarWidth}px`
|
||||
} else {
|
||||
document.body.style.paddingRight = ''
|
||||
}
|
||||
}
|
||||
|
||||
function show(event?: MouseEvent) {
|
||||
props.onShow?.()
|
||||
open.value = true
|
||||
|
||||
addBodyPadding()
|
||||
document.body.style.overflow = 'hidden'
|
||||
window.addEventListener('mousedown', updateMousePosition)
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
@@ -184,7 +173,6 @@ function hide() {
|
||||
props.onHide?.()
|
||||
visible.value = false
|
||||
document.body.style.overflow = ''
|
||||
document.body.style.paddingRight = ''
|
||||
window.removeEventListener('mousedown', updateMousePosition)
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<template v-for="(version, index) in currentVersions" :key="index">
|
||||
<!-- Row divider -->
|
||||
<div
|
||||
class="versions-grid-row h-px w-full bg-button-bg"
|
||||
class="versions-grid-row h-px w-full bg-surface-5"
|
||||
:class="{
|
||||
'max-sm:!hidden': index === 0,
|
||||
}"
|
||||
|
||||
Reference in New Issue
Block a user