Compare commits

...
142 changed files with 939 additions and 1602 deletions
@@ -9,11 +9,10 @@ import {
WrenchIcon,
XIcon,
} from '@modrinth/assets'
import { ButtonStyled, Collapsible, injectNotificationManager } from '@modrinth/ui'
import { ButtonStyled, Collapsible, injectNotificationManager, Modal } from '@modrinth/ui'
import { computed, ref } from 'vue'
import { ChatIcon } from '@/assets/icons'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { install_existing_instance } from '@/helpers/install'
@@ -151,7 +150,7 @@ async function copyToClipboard(text) {
</script>
<template>
<ModalWrapper ref="errorModal" :header="title" :closable="closable">
<Modal ref="errorModal" :header="title" :closable="closable">
<div class="modal-body max-w-[550px]">
<div class="markdown-body">
<template v-if="errorType === 'minecraft_auth'">
@@ -323,7 +322,7 @@ async function copyToClipboard(text) {
</div>
</template>
</div>
</ModalWrapper>
</Modal>
</template>
<style>
@@ -6,7 +6,7 @@ import {
defineMessages,
FileTreeSelect,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
@@ -233,7 +233,7 @@ function isExportCandidateDisabled(path) {
</script>
<template>
<NewModal
<Modal
ref="exportModal"
:header="formatMessage(messages.header)"
scrollable
@@ -300,5 +300,5 @@ function isExportCandidateDisabled(path) {
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
@@ -1,9 +1,5 @@
<template>
<ModalWrapper
ref="detectJavaModal"
:header="formatMessage(messages.title)"
:show-ad-on-close="false"
>
<Modal ref="detectJavaModal" :header="formatMessage(messages.title)">
<div class="flex flex-col gap-4">
<Table :columns="javaInstallColumns" :data="chosenInstallOptions" row-key="path">
<template #cell-version="{ value }">
@@ -46,7 +42,7 @@
</ButtonStyled>
</div>
</div>
</ModalWrapper>
</Modal>
</template>
<script setup>
import { CheckIcon, PlusIcon, XIcon } from '@modrinth/assets'
@@ -54,12 +50,12 @@ import {
ButtonStyled,
defineMessages,
injectNotificationManager,
Modal,
Table,
useVIntl,
} from '@modrinth/ui'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { find_filtered_jres } from '@/helpers/jre.js'
@@ -1,10 +1,9 @@
<script setup>
import { CheckIcon } from '@modrinth/assets'
import { Badge, ButtonStyled } from '@modrinth/ui'
import { Badge, ButtonStyled, Modal } from '@modrinth/ui'
import { computed, ref } from 'vue'
import { SwapIcon } from '@/assets/icons/index.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { update_managed_modrinth_version } from '@/helpers/instance'
import { releaseColor } from '@/helpers/utils'
@@ -52,7 +51,7 @@ const onHide = () => {
</script>
<template>
<ModalWrapper
<Modal
ref="modpackVersionModal"
class="modpack-version-modal"
header="Change modpack version"
@@ -123,7 +122,7 @@ const onHide = () => {
</div>
</div>
</div>
</ModalWrapper>
</Modal>
</template>
<style scoped lang="scss">
@@ -1,8 +1,7 @@
<script setup>
import { ButtonStyled, injectNotificationManager, ProjectCard } from '@modrinth/ui'
import { ButtonStyled, injectNotificationManager, Modal, ProjectCard } from '@modrinth/ui'
import { ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_project_v3, get_version } from '@/helpers/cache.js'
import { injectContentInstall } from '@/providers/content-install'
@@ -45,7 +44,7 @@ async function install() {
</script>
<template>
<ModalWrapper ref="confirmModal" :header="`Install ${project?.name}`">
<Modal ref="confirmModal" :header="`Install ${project?.name}`">
<div class="modal-body">
<ProjectCard
:title="project.name"
@@ -75,7 +74,7 @@ async function install() {
</div>
</div>
</div>
</ModalWrapper>
</Modal>
</template>
<style scoped lang="scss">
@@ -6,6 +6,7 @@ import {
defineMessages,
injectNotificationManager,
IntlFormatted,
Modal,
StyledInput,
useRelativeTime,
useVIntl,
@@ -13,7 +14,6 @@ import {
import { computed, ref } from 'vue'
import FriendsSection from '@/components/ui/friends/FriendsSection.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { useFriends } from '@/composables/use-friends'
import type { FriendWithUserData } from '@/helpers/friends.ts'
import type { ModrinthCredentials } from '@/helpers/mr_auth'
@@ -176,7 +176,7 @@ const messages = defineMessages({
</script>
<template>
<ModalWrapper ref="friendInvitesModal" header="View friend requests">
<Modal ref="friendInvitesModal" header="View friend requests">
<p v-if="incomingRequests.length === 0">You have no pending friend requests :C</p>
<div v-else class="flex flex-col gap-4 min-w-[40rem]">
<div v-for="friend in incomingRequests" :key="friend.username" class="flex gap-2">
@@ -222,8 +222,8 @@ const messages = defineMessages({
</div>
</div>
</div>
</ModalWrapper>
<ModalWrapper ref="addFriendModal" :header="formatMessage(messages.addingAFriend)">
</Modal>
<Modal ref="addFriendModal" :header="formatMessage(messages.addingAFriend)">
<div class="min-w-[30rem]">
<h2 class="m-0 text-base font-medium text-primary">
{{ formatMessage(messages.usernameTitle) }}
@@ -248,7 +248,7 @@ const messages = defineMessages({
</ButtonStyled>
</div>
</div>
</ModalWrapper>
</Modal>
<div v-if="userCredentials && !loading" class="flex gap-1 items-center mb-3 -ml-1">
<template v-if="sortedFriends.length > 0">
<ButtonStyled circular type="transparent">
@@ -5,13 +5,13 @@ import {
Avatar,
ButtonStyled,
injectNotificationManager,
Modal,
StyledInput,
} from '@modrinth/ui'
import { useQueryClient } from '@tanstack/vue-query'
import { convertFileSrc } from '@tauri-apps/api/core'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { list } from '@/helpers/instance'
import { add_server_to_instance, get_instance_worlds } from '@/helpers/worlds.ts'
@@ -82,7 +82,7 @@ async function addServer(instance) {
</script>
<template>
<ModalWrapper ref="modal" header="Add server to instance">
<Modal ref="modal" header="Add server to instance">
<div class="flex flex-col gap-4 min-w-[350px]">
<Admonition type="warning" body="This server may not be compatible with all instances." />
<StyledInput
@@ -124,5 +124,5 @@ async function addServer(instance) {
</ButtonStyled>
</div>
</div>
</ModalWrapper>
</Modal>
</template>
@@ -7,7 +7,7 @@ import {
MessagesSquareIcon,
WrenchIcon,
} from '@modrinth/assets'
import { Admonition, ButtonStyled, Collapsible, NewModal } from '@modrinth/ui'
import { Admonition, ButtonStyled, Collapsible, Modal } from '@modrinth/ui'
import { computed, ref } from 'vue'
import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
@@ -16,7 +16,7 @@ import { handleSevereError } from '@/store/error.js'
import { findMinecraftAuthError, type MinecraftAuthError } from './minecraft-auth-errors'
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const rawError = ref<string>('')
const matchedError = ref<MinecraftAuthError | null>(null)
const debugCollapsed = ref(true)
@@ -74,7 +74,7 @@ async function copyToClipboard(text: string) {
</script>
<template>
<NewModal ref="modal" header="Sign in Failed" :max-width="'548px'" @hide="onModalHide">
<Modal ref="modal" header="Sign in Failed" :max-width="'548px'" @hide="onModalHide">
<div class="flex flex-col gap-6">
<Admonition
type="warning"
@@ -191,5 +191,5 @@ async function copyToClipboard(text: string) {
</div>
</div>
</div>
</NewModal>
</Modal>
</template>
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.header)" max-width="544px" no-padding>
<Modal ref="modal" :header="formatMessage(messages.header)" max-width="544px" no-padding>
<div class="grid grid-cols-[1fr_auto] gap-2.5 h-[154px] px-7 pt-4 pb-1 pr-9">
<div class="flex flex-col gap-2.5 items-start justify-center h-min mt-5">
<div class="font-semibold text-xl text-contrast">
@@ -64,12 +64,12 @@
</a>
</p>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
import { MessagesSquareIcon, SpinnerIcon } from '@modrinth/assets'
import { ButtonStyled, defineMessages, NewModal, useVIntl } from '@modrinth/ui'
import { ButtonStyled, defineMessages, Modal, useVIntl } from '@modrinth/ui'
import { inject, type Ref, ref } from 'vue'
import steveImage from '@/assets/steve-look-up-left.webp'
@@ -113,7 +113,7 @@ const messages = defineMessages({
},
})
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const loadingSignIn = ref(false)
function show() {
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.header)" fade="danger" max-width="500px">
<Modal ref="modal" :header="formatMessage(messages.header)" fade="danger" max-width="500px">
<Admonition type="critical" :header="formatMessage(messages.admonitionHeader)">
{{ formatMessage(messages.admonitionBody) }}
</Admonition>
@@ -20,7 +20,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -30,7 +30,7 @@ import {
ButtonStyled,
commonMessages,
defineMessages,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { ref } from 'vue'
@@ -61,7 +61,7 @@ const emit = defineEmits<{
(e: 'delete'): void
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
function show() {
modal.value?.show()
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:header="formatMessage(messages.installToPlay)"
:closable="true"
@@ -151,7 +151,7 @@
</div>
</div>
</div>
</NewModal>
</Modal>
<ModpackContentModal
ref="modpackContentModal"
@@ -171,8 +171,8 @@ import {
type ContentItem,
defineMessages,
formatLoader,
Modal,
ModpackContentModal,
NewModal,
Table,
type TableColumn,
useScrollIndicator,
@@ -191,7 +191,7 @@ type ExternalFileRow = {
name: string
}
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const modpackVersionId = ref<string | null>(null)
const modpackVersion = ref<Labrinth.Versions.v2.Version | null>(null)
const project = ref<Labrinth.Projects.v3.Project | null>(null)
@@ -1,56 +0,0 @@
<!-- @deprecated Use NewModal from @modrinth/ui directly. Ads/noblur now handled by injectModalBehavior. -->
<script setup lang="ts">
import { NewModal as Modal } from '@modrinth/ui'
import { useTemplateRef } from 'vue'
const props = defineProps({
header: {
type: String,
default: null,
},
hideHeader: {
type: Boolean,
default: false,
},
closable: {
type: Boolean,
default: true,
},
onHide: {
type: Function,
default() {
return () => {}
},
},
/** @deprecated No longer used — ads are handled by provideModalBehavior */
showAdOnClose: {
type: Boolean,
default: true,
},
})
const modal = useTemplateRef('modal')
defineExpose({
show: (e?: MouseEvent) => {
modal.value?.show(e)
},
hide: () => {
modal.value?.hide()
},
})
</script>
<template>
<Modal
ref="modal"
:header="header"
:closable="closable"
:hide-header="hideHeader"
:on-hide="() => props.onHide?.()"
>
<template #title>
<slot name="title" />
</template>
<slot />
</Modal>
</template>
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.header)" fade="warning" max-width="500px">
<Modal ref="modal" :header="formatMessage(messages.header)" fade="warning" max-width="500px">
<p class="m-0 text-secondary">
<IntlFormatted :message-id="messages.body" :values="{ instanceName }">
<template #bold="{ children }">
@@ -30,7 +30,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -40,7 +40,7 @@ import {
commonMessages,
defineMessages,
IntlFormatted,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { ref } from 'vue'
@@ -72,7 +72,7 @@ const emit = defineEmits<{
(e: 'create-anyway'): void
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const instanceName = ref('')
const instanceId = ref('')
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:header="formatMessage(authenticating ? messages.signingInHeader : messages.header)"
:on-hide="handleHide"
@@ -103,12 +103,12 @@
</p>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
import { LogInIcon, RefreshCwIcon, SpinnerIcon, UserPlusIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, defineMessages, IntlFormatted, NewModal, useVIntl } from '@modrinth/ui'
import { ButtonStyled, defineMessages, IntlFormatted, Modal, useVIntl } from '@modrinth/ui'
import { openUrl } from '@tauri-apps/plugin-opener'
import { ref } from 'vue'
@@ -119,7 +119,7 @@ const props = defineProps<{
}>()
const { formatMessage } = useVIntl()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const authenticating = ref<ModrinthAuthFlow | null>(null)
const reopeningBrowser = ref(false)
let resolveShow: ((signedIn: boolean) => void) | undefined
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.header)" fade="danger" max-width="500px">
<Modal ref="modal" :header="formatMessage(messages.header)" fade="danger" max-width="500px">
<Admonition type="critical" :header="formatMessage(messages.admonitionHeader)">
<IntlFormatted :message-id="messages.admonitionBody" :values="{ code: inviteCode }">
<template #monospace="{ children }">
@@ -24,7 +24,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -35,13 +35,13 @@ import {
commonMessages,
defineMessages,
IntlFormatted,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { ref } from 'vue'
const { formatMessage } = useVIntl()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const inviteCode = ref('')
const emit = defineEmits<{
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.header)" fade="warning" max-width="500px">
<Modal ref="modal" :header="formatMessage(messages.header)" fade="warning" max-width="500px">
<p class="m-0 text-secondary">
<IntlFormatted :message-id="messages.body" :values="{ instanceName }">
<template #bold="{ children }">
@@ -30,7 +30,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -40,7 +40,7 @@ import {
commonMessages,
defineMessages,
IntlFormatted,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { ref } from 'vue'
@@ -71,7 +71,7 @@ const emit = defineEmits<{
(e: 'cancel' | 'go-to-instance' | 'install-anyway'): void
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const instanceName = ref('')
function show(name: string) {
@@ -28,7 +28,7 @@
<span class="text-primary">{{ formatMessage(messages.unlinkDescription) }}</span>
</div>
<NewModal
<Modal
ref="unpublishModal"
:header="formatMessage(messages.unpublishModalHeader)"
fade="warning"
@@ -50,8 +50,8 @@
>
</div></template
>
</NewModal>
<NewModal
</Modal>
<Modal
ref="unlinkModal"
:header="formatMessage(messages.unlinkModalHeader)"
fade="warning"
@@ -81,7 +81,7 @@
>
</div></template
>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -92,7 +92,7 @@ import {
commonMessages,
defineMessages,
InlineBackupCreator,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { ref } from 'vue'
@@ -117,8 +117,8 @@ const props = withDefaults(
},
)
const { formatMessage } = useVIntl()
const unpublishModal = ref<InstanceType<typeof NewModal>>()
const unlinkModal = ref<InstanceType<typeof NewModal>>()
const unpublishModal = ref<InstanceType<typeof Modal>>()
const unlinkModal = ref<InstanceType<typeof Modal>>()
const backupCreator = ref<InstanceType<typeof InlineBackupCreator>>()
const backupBusy = ref(false)
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:header="
reportMode
@@ -242,7 +242,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
<ModpackContentModal
ref="contentModal"
:header="formatMessage(messages.sharedInstanceContent)"
@@ -270,8 +270,8 @@ import {
injectNotificationManager,
IntlFormatted,
MarkdownEditor,
Modal,
ModpackContentModal,
NewModal,
Table,
type TableColumn,
useScrollIndicator,
@@ -302,7 +302,7 @@ type SharedInstanceCreator = {
avatarUrl: string | null
}
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const contentModal = ref<InstanceType<typeof ModpackContentModal>>()
const externalFileTable = ref<HTMLElement | null>(null)
const preview = ref<SharedInstanceInstallPreview | null>(null)
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :on-hide="handleModalHide">
<Modal ref="modal" :on-hide="handleModalHide">
<template #title>
<span class="text-lg font-extrabold text-contrast">
{{ formatMessage(mode === 'edit' ? messages.editSkinTitle : messages.addSkinTitle) }}
@@ -127,7 +127,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -139,7 +139,7 @@ import {
commonMessages,
defineMessages,
injectNotificationManager,
NewModal,
Modal,
RadioButtons,
SkinPreviewRenderer,
useScrollIndicator,
@@ -5,7 +5,7 @@ import {
commonMessages,
defineMessages,
injectNotificationManager,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { ref } from 'vue'
@@ -29,7 +29,7 @@ const props = defineProps<{
instance: GameInstance
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const name = ref('')
const address = ref('')
@@ -88,7 +88,7 @@ const messages = defineMessages({
defineExpose({ show, hide })
</script>
<template>
<NewModal ref="modal" :header="formatMessage(messages.title)" width="500px" max-width="500px">
<Modal ref="modal" :header="formatMessage(messages.title)" width="500px" max-width="500px">
<ServerModalBody
v-model:name="name"
v-model:address="address"
@@ -116,5 +116,5 @@ defineExpose({ show, hide })
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
@@ -5,7 +5,7 @@ import {
ButtonStyled,
commonMessages,
defineMessages,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { computed, ref } from 'vue'
@@ -59,7 +59,7 @@ const messages = defineMessages({
},
})
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const isServer = computed(() => props.world?.type === 'server')
const isSingleplayer = computed(() => props.world?.type === 'singleplayer')
@@ -94,7 +94,7 @@ defineExpose({ show, hide })
</script>
<template>
<NewModal ref="modal" :header="formatMessage(titleMessage)" fade="danger" max-width="500px">
<Modal ref="modal" :header="formatMessage(titleMessage)" fade="danger" max-width="500px">
<div class="flex flex-col gap-4">
<Admonition
type="critical"
@@ -120,5 +120,5 @@ defineExpose({ show, hide })
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
@@ -5,7 +5,7 @@ import {
commonMessages,
defineMessage,
injectNotificationManager,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { computed, ref } from 'vue'
@@ -32,7 +32,7 @@ const props = defineProps<{
instance: GameInstance
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const name = ref<string>('')
const address = ref<string>('')
@@ -96,7 +96,7 @@ const titleMessage = defineMessage({
})
</script>
<template>
<NewModal ref="modal" :header="formatMessage(titleMessage)" width="500px" max-width="500px">
<Modal ref="modal" :header="formatMessage(titleMessage)" width="500px" max-width="500px">
<ServerModalBody
v-model:name="name"
v-model:address="address"
@@ -119,5 +119,5 @@ const titleMessage = defineMessage({
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
@@ -6,12 +6,12 @@ import {
commonMessages,
defineMessages,
injectNotificationManager,
Modal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
import type { GameInstance } from '@/helpers/types'
import type { DisplayStatus, SingleplayerWorld } from '@/helpers/worlds.ts'
@@ -94,7 +94,7 @@ const messages = defineMessages({
})
</script>
<template>
<ModalWrapper ref="modal">
<Modal ref="modal">
<template #title>
<Avatar :src="removeIcon || !icon ? undefined : icon" size="24px" />
{{ instance.name }} <ChevronRightIcon />
@@ -132,5 +132,5 @@ const messages = defineMessages({
</button>
</ButtonStyled>
</div>
</ModalWrapper>
</Modal>
</template>
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:header="formatMessage(messages.header)"
max-width="470px"
@@ -44,7 +44,7 @@
>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -55,7 +55,7 @@ import {
ButtonStyled,
commonMessages,
defineMessages,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { computed, ref } from 'vue'
@@ -64,7 +64,7 @@ import { methodLabels, type ShareRow } from './shared-instance-share-types'
const props = defineProps<{ row: ShareRow | null; memberCount: number }>()
const emit = defineEmits<{ confirm: [row: ShareRow]; clear: [] }>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const { formatMessage } = useVIntl()
const username = computed(() => props.row?.username ?? '')
const messages = defineMessages({
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:header="formatMessage(analyticsChartMessages.renderLimitHeader, { count: tableProjectCount })"
fade="warning"
@@ -24,11 +24,11 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
import { ButtonStyled, NewModal, useVIntl } from '@modrinth/ui'
import { ButtonStyled, Modal, useVIntl } from '@modrinth/ui'
import { analyticsChartMessages } from '../../analytics-messages'
@@ -41,7 +41,7 @@ const emit = defineEmits<{
}>()
const { formatMessage } = useVIntl()
const modal = ref<InstanceType<typeof NewModal> | null>(null)
const modal = ref<InstanceType<typeof Modal> | null>(null)
function show(event: MouseEvent) {
modal.value?.show(event)
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" fade="warning" width="550px">
<Modal ref="modal" fade="warning" width="550px">
<template #title>
<div class="flex items-center gap-2">
<span class="text-lg font-extrabold text-contrast">Transfer</span>
@@ -71,12 +71,12 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
import { RightArrowIcon, TransferIcon, XIcon } from '@modrinth/assets'
import { Admonition, Avatar, ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
import { Admonition, Avatar, ButtonStyled, Modal, StyledInput } from '@modrinth/ui'
import { computed, ref } from 'vue'
const props = defineProps<{
@@ -86,7 +86,7 @@ const props = defineProps<{
onConfirm: () => void
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const confirmationText = ref('')
const isConfirmEnabled = computed(
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" fade="warning" width="550px">
<Modal ref="modal" fade="warning" width="550px">
<template #title>
<div class="flex items-center gap-2">
<span class="text-lg font-extrabold text-contrast">Transfer</span>
@@ -78,12 +78,12 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
import { RightArrowIcon, TransferIcon, XIcon } from '@modrinth/assets'
import { Admonition, Avatar, ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
import { Admonition, Avatar, ButtonStyled, Modal, StyledInput } from '@modrinth/ui'
import { computed, ref } from 'vue'
const props = defineProps<{
@@ -98,7 +98,7 @@ const props = defineProps<{
onConfirm: () => void
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const confirmationText = ref('')
const isConfirmEnabled = computed(
-156
View File
@@ -1,156 +0,0 @@
<template>
<div v-if="shown">
<div
:class="{
shown: actuallyShown,
noblur: !$orElse(cosmetics.advancedRendering, true),
}"
class="modal-overlay"
@click="hide"
/>
<div class="modal-container" :class="{ shown: actuallyShown }">
<div class="modal-body">
<div v-if="header" class="header">
<strong>{{ header }}</strong>
<ButtonStyled circular type="transparent">
<button @click="hide">
<XIcon />
</button>
</ButtonStyled>
</div>
<div class="content">
<slot />
</div>
</div>
</div>
</div>
<div v-else />
</template>
<script>
import { XIcon } from '@modrinth/assets'
import { ButtonStyled } from '@modrinth/ui'
export default {
components: {
ButtonStyled,
XIcon,
},
props: {
header: {
type: String,
default: null,
},
},
setup() {
const cosmetics = useCosmetics()
return { cosmetics }
},
data() {
return {
shown: false,
actuallyShown: false,
}
},
methods: {
show() {
this.shown = true
setTimeout(() => {
this.actuallyShown = true
}, 50)
},
hide() {
this.actuallyShown = false
setTimeout(() => {
this.shown = false
}, 300)
},
},
}
</script>
<style lang="scss" scoped>
.modal-overlay {
visibility: hidden;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 20;
transition: all 0.3s ease-in-out;
&.shown {
opacity: 1;
visibility: visible;
background: hsla(0, 0%, 0%, 0.5);
backdrop-filter: blur(3px);
}
&.noblur {
backdrop-filter: none;
}
}
.modal-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 21;
visibility: hidden;
pointer-events: none;
&.shown {
visibility: visible;
.modal-body {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
}
.modal-body {
position: fixed;
box-shadow: var(--shadow-raised), var(--shadow-inset);
border-radius: var(--size-rounded-lg);
max-height: calc(100% - 2 * var(--spacing-card-bg));
overflow-y: auto;
width: 600px;
pointer-events: auto;
outline: 3px solid transparent;
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--color-bg);
padding: var(--spacing-card-md) var(--spacing-card-lg);
strong {
font-size: 1.25rem;
margin: 0.67em 0;
}
}
.content {
background-color: var(--color-raised-bg);
}
transform: translateY(50vh);
visibility: hidden;
opacity: 0;
transition: all 0.25s ease-in-out;
@media screen and (max-width: 650px) {
width: calc(100% - 2 * var(--spacing-card-bg));
}
}
}
</style>
@@ -1,6 +1,6 @@
<template>
<div>
<NewModal
<Modal
ref="modalOpen"
:header="formatMessage(messages.transferProjectsTitle)"
no-padding
@@ -83,7 +83,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
<ButtonStyled>
<button @click="show($event)">
<TransferIcon />
@@ -101,7 +101,7 @@ import {
commonMessages,
CopyCode,
defineMessages,
NewModal,
Modal,
Table,
useVIntl,
} from '@modrinth/ui'
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :on-show="onShow" :on-hide="onHide" width="544px" actions-divider>
<Modal ref="modal" :on-show="onShow" :on-hide="onHide" width="544px" actions-divider>
<template #title>
<template v-if="project">
<Avatar :src="project.icon_url" :alt="project.title" class="icon" size="32px" />
@@ -87,7 +87,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -102,7 +102,7 @@ import {
injectNotificationManager,
type JoinedButtonAction,
JoinedButtons,
NewModal,
Modal,
ServersPromo,
truncatedTooltip,
useDebugLogger,
@@ -146,7 +146,7 @@ type DownloadedFile = DownloadableFile & {
type DownloadActionType = 'zip' | 'dependencies' | 'recommended'
type NewModalRef = {
type ModalRef = {
show: (event?: MouseEvent) => void
hide: () => void
}
@@ -191,7 +191,7 @@ const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
const debug = useDebugLogger('DownloadModal')
const modal = ref<NewModalRef | null>(null)
const modal = ref<ModalRef | null>(null)
const downloadTitleRef = ref<HTMLElement | null>(null)
const modalOpening = ref(false)
const modalOpen = ref(false)
@@ -6,7 +6,7 @@ import {
ButtonStyled,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
ServerNotice,
StyledInput,
TagItem,
@@ -18,7 +18,7 @@ const client = injectModrinthClient()
type ServerNoticeType = Archon.Notices.v0.ListedNotice
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const emit = defineEmits<{
(e: 'close'): void
@@ -107,7 +107,7 @@ function hide() {
defineExpose({ show, hide })
</script>
<template>
<NewModal ref="modal" :on-hide="() => emit('close')">
<Modal ref="modal" :on-hide="() => emit('close')">
<template #title>
<span class="text-lg font-extrabold text-contrast">
Editing assignments of notice #{{ notice?.id }}
@@ -202,5 +202,5 @@ defineExpose({ show, hide })
</div>
</div>
</div>
</NewModal>
</Modal>
</template>
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal">
<Modal ref="modal">
<template #title>
<span class="text-lg font-extrabold text-contrast">Batch credit</span>
</template>
@@ -125,7 +125,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -135,7 +135,7 @@ import {
Combobox,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
TagItem,
Toggle,
@@ -148,7 +148,7 @@ import { useBaseFetch } from '#imports'
const { addNotification } = injectNotificationManager()
const client = injectModrinthClient()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const days = ref(1)
const sendEmail = ref(true)
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal">
<Modal ref="modal">
<template #title>
<span class="text-lg font-extrabold text-contrast">Schedule transfer</span>
</template>
@@ -189,7 +189,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -200,7 +200,7 @@ import {
Combobox,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
TagItem,
Toggle,
@@ -215,7 +215,7 @@ const emit = defineEmits<{
const { addNotification } = injectNotificationManager()
const client = injectModrinthClient()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const modeOptions = [
{ value: 'servers', label: 'Servers' },
@@ -10,7 +10,7 @@
@proceed="removePasskey()"
/>
<NewModal
<Modal
ref="managePasskeyModal"
width="600px"
:header="formatMessage(messages.managePasskeyTitle)"
@@ -99,9 +99,9 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<NewModal
<Modal
ref="addPasskeyModal"
width="500px"
:header="formatMessage(messages.passkeyAddModalTitle)"
@@ -138,9 +138,9 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<NewModal
<Modal
ref="renamePasskeyModal"
width="500px"
:header="formatMessage(messages.renamePasskeyModalHeader)"
@@ -177,7 +177,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<div class="adjacent-input mb-0">
<label for="manage-passkeys">
@@ -215,7 +215,7 @@ import {
defineMessages,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
useFormatDateTime,
useRelativeTime,
@@ -316,9 +316,9 @@ const messages = defineMessages({
})
const removePasskeyModal = useTemplateRef<InstanceType<typeof ConfirmModal>>('removePasskeyModal')
const managePasskeyModal = useTemplateRef<InstanceType<typeof NewModal>>('managePasskeyModal')
const addPasskeyModal = useTemplateRef<InstanceType<typeof NewModal>>('addPasskeyModal')
const renamePasskeyModal = useTemplateRef<InstanceType<typeof NewModal>>('renamePasskeyModal')
const managePasskeyModal = useTemplateRef<InstanceType<typeof Modal>>('managePasskeyModal')
const addPasskeyModal = useTemplateRef<InstanceType<typeof Modal>>('addPasskeyModal')
const renamePasskeyModal = useTemplateRef<InstanceType<typeof Modal>>('renamePasskeyModal')
const pendingPasskey = ref<Labrinth.Auth.v2.PasskeyRegisterFinishRequest | null>(null)
const passkeyToRemove = ref<Labrinth.Auth.v2.Passkey | null>(null)
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.title)">
<Modal ref="modal" :header="formatMessage(messages.title)">
<div class="min-w-md flex max-w-md flex-col gap-3">
<CreateLimitAlert v-model="hasHitLimit" type="collection" />
<div class="flex flex-col gap-2">
@@ -52,7 +52,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup>
import { PlusIcon, XIcon } from '@modrinth/assets'
@@ -61,7 +61,7 @@ import {
commonMessages,
defineMessages,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.title)">
<Modal ref="modal" :header="formatMessage(messages.title)">
<div class="min-w-md flex max-w-md flex-col gap-3">
<CreateLimitAlert v-model="hasHitLimit" type="org" />
<div class="flex flex-col gap-2">
@@ -73,7 +73,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -83,7 +83,7 @@ import {
commonMessages,
defineMessages,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
@@ -142,7 +142,7 @@ const slug = ref<string>('')
const description = ref<string>('')
const manualSlug = ref<boolean>(false)
const hasHitLimit = ref<boolean>(false)
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
async function createOrganization(): Promise<void> {
startLoading()
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:header="
projectType === 'server'
@@ -125,7 +125,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -140,7 +140,7 @@ import {
defineMessages,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
@@ -264,7 +264,7 @@ const props = defineProps<{
organizationId?: string | null
}>()
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
const hasHitLimit = ref(false)
const name = ref('')
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="taxFormModal"
:header="formatMessage(messages.taxFormHeader)"
:hide-header="currentStage === 'download-confirmation'"
@@ -151,7 +151,7 @@
</div>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -171,7 +171,7 @@ import {
defineMessages,
injectNotificationManager,
IntlFormatted,
NewModal,
Modal,
normalizeChildren,
useVIntl,
} from '@modrinth/ui'
@@ -191,7 +191,7 @@ const props = withDefaults(
const { addNotification } = injectNotificationManager()
const taxFormModal = ref<InstanceType<typeof NewModal> | null>(null)
const taxFormModal = ref<InstanceType<typeof Modal> | null>(null)
type ModalStage = 'form-selection' | 'download-confirmation'
const currentStage = ref<ModalStage>('form-selection')
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="withdrawModal"
:closable="currentStage !== 'completion'"
:hide-header="currentStage === 'completion'"
@@ -93,7 +93,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
<CreatorTaxFormModal
ref="taxFormModal"
:close-button-text="formatMessage(commonMessages.continueButton)"
@@ -118,7 +118,7 @@ import {
commonMessages,
defineMessages,
injectNotificationManager,
NewModal,
Modal,
useVIntl,
} from '@modrinth/ui'
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'
@@ -151,7 +151,7 @@ const emit = defineEmits<{
(e: 'refresh-data' | 'hide'): void
}>()
const withdrawModal = useTemplateRef<InstanceType<typeof NewModal>>('withdrawModal')
const withdrawModal = useTemplateRef<InstanceType<typeof Modal>>('withdrawModal')
const taxFormModal = ref<InstanceType<typeof CreatorTaxFormModal> | null>(null)
const isSubmitting = ref(false)
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { ClipboardCopyIcon, DownloadIcon, LoaderCircleIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, CopyCode, NewModal, useDebugLogger } from '@modrinth/ui'
import { ButtonStyled, CopyCode, Modal, useDebugLogger } from '@modrinth/ui'
import { ref, useTemplateRef } from 'vue'
export type UnsafeFile = {
@@ -18,7 +18,7 @@ const props = defineProps<{
const debug = useDebugLogger('MaliciousSummaryModal')
const modalRef = useTemplateRef<InstanceType<typeof NewModal>>('modalRef')
const modalRef = useTemplateRef<InstanceType<typeof Modal>>('modalRef')
const versionDataCache = ref<
Map<
@@ -96,7 +96,7 @@ defineExpose({ show, hide })
</script>
<template>
<NewModal
<Modal
ref="modalRef"
header="Malicious file(s) summary"
:close-on-click-outside="false"
@@ -182,5 +182,5 @@ defineExpose({ show, hide })
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
@@ -15,7 +15,7 @@ import {
defineMessages,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
Table,
type TableColumn,
useVIntl,
@@ -136,7 +136,7 @@ const props = defineProps<{
const client = injectModrinthClient()
const queryClient = useQueryClient()
const { addNotification } = injectNotificationManager()
const modalRef = useTemplateRef<InstanceType<typeof NewModal>>('modalRef')
const modalRef = useTemplateRef<InstanceType<typeof Modal>>('modalRef')
const clearModalRef = useTemplateRef<InstanceType<typeof ConfirmModal>>('clearModalRef')
const { formatMessage } = useVIntl()
@@ -373,7 +373,7 @@ defineExpose({ show, hide })
@proceed="clearAllGroups"
/>
<NewModal
<Modal
ref="modalRef"
width="60vw"
:close-on-click-outside="false"
@@ -517,5 +517,5 @@ defineExpose({ show, hide })
</template>
</Table>
</div>
</NewModal>
</Modal>
</template>
@@ -1,6 +1,6 @@
<template>
<div>
<NewModal
<Modal
ref="modalSubmit"
:header="
formatMessage(
@@ -56,8 +56,8 @@
</ButtonStyled>
</div>
</div>
</NewModal>
<NewModal ref="modalReply" :header="formatMessage(messages.replyModalHeader)">
</Modal>
<Modal ref="modalReply" :header="formatMessage(messages.replyModalHeader)">
<div class="flex max-w-[45rem] flex-col gap-3">
<p class="m-0">{{ formatMessage(messages.replyModalDescription) }}</p>
<p class="m-0">
@@ -98,7 +98,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<div v-if="flags.developerMode" class="mx-4 mb-3 font-semibold">
Thread ID:
<CopyCode :text="thread.id" />
@@ -430,7 +430,7 @@ import {
injectNotificationManager,
IntlFormatted,
MarkdownEditor,
NewModal,
Modal,
OverflowMenu,
useVIntl,
} from '@modrinth/ui'
@@ -2744,24 +2744,15 @@
"settings.applications.about": {
"message": "O"
},
"settings.applications.button.cancel": {
"message": "Zrušit"
},
"settings.applications.button.create": {
"message": "Vytvořit aplikaci"
},
"settings.applications.button.delete": {
"message": "Smazat"
},
"settings.applications.button.edit": {
"message": "Upravit"
},
"settings.applications.button.new": {
"message": "Nová aplikace"
},
"settings.applications.button.save-changes": {
"message": "Uložit změny"
},
"settings.applications.button.upload-icon": {
"message": "Nahrát ikonu"
},
@@ -2945,24 +2945,15 @@
"settings.applications.button.add-more": {
"message": "Tilføj mere"
},
"settings.applications.button.cancel": {
"message": "Annuller"
},
"settings.applications.button.create": {
"message": "Opret app"
},
"settings.applications.button.delete": {
"message": "Slet"
},
"settings.applications.button.edit": {
"message": "Rediger"
},
"settings.applications.button.new": {
"message": "Ny applikation"
},
"settings.applications.button.save-changes": {
"message": "Gem ændringer"
},
"settings.applications.button.upload-icon": {
"message": "Upload ikon"
},
@@ -4679,24 +4679,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Weiterleitungs-URI hinzufügen"
},
"settings.applications.button.cancel": {
"message": "Abbrechen"
},
"settings.applications.button.create": {
"message": "App erstellen"
},
"settings.applications.button.delete": {
"message": "Löschen"
},
"settings.applications.button.edit": {
"message": "Bearbeiten"
},
"settings.applications.button.new": {
"message": "Neue Anwendung"
},
"settings.applications.button.save-changes": {
"message": "Änderungen speichern"
},
"settings.applications.button.upload-icon": {
"message": "Icon hochladen"
},
@@ -4679,24 +4679,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Weiterleitungs-URI hinzufügen"
},
"settings.applications.button.cancel": {
"message": "Abbrechen"
},
"settings.applications.button.create": {
"message": "Anwendung erstellen"
},
"settings.applications.button.delete": {
"message": "Löschen"
},
"settings.applications.button.edit": {
"message": "Bearbeiten"
},
"settings.applications.button.new": {
"message": "Neue Anwendung"
},
"settings.applications.button.save-changes": {
"message": "Änderungen speichern"
},
"settings.applications.button.upload-icon": {
"message": "Icon hochladen"
},
@@ -4709,24 +4709,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Add a redirect URI"
},
"settings.applications.button.cancel": {
"message": "Cancel"
},
"settings.applications.button.create": {
"message": "Create app"
},
"settings.applications.button.delete": {
"message": "Delete"
},
"settings.applications.button.edit": {
"message": "Edit"
},
"settings.applications.button.new": {
"message": "New application"
},
"settings.applications.button.save-changes": {
"message": "Save changes"
},
"settings.applications.button.upload-icon": {
"message": "Upload icon"
},
@@ -4187,24 +4187,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Agregar un URI de redirección"
},
"settings.applications.button.cancel": {
"message": "Cancelar"
},
"settings.applications.button.create": {
"message": "Crear aplicación"
},
"settings.applications.button.delete": {
"message": "Borrar"
},
"settings.applications.button.edit": {
"message": "Editar"
},
"settings.applications.button.new": {
"message": "Nueva aplicación"
},
"settings.applications.button.save-changes": {
"message": "Guardar cambios"
},
"settings.applications.button.upload-icon": {
"message": "Cargar icono"
},
@@ -3860,24 +3860,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Añadir una URI de redirección"
},
"settings.applications.button.cancel": {
"message": "Cancelar"
},
"settings.applications.button.create": {
"message": "Crear aplicación"
},
"settings.applications.button.delete": {
"message": "Eliminar"
},
"settings.applications.button.edit": {
"message": "Editar"
},
"settings.applications.button.new": {
"message": "Nueva aplicación"
},
"settings.applications.button.save-changes": {
"message": "Guardar cambios"
},
"settings.applications.button.upload-icon": {
"message": "Subir icono"
},
@@ -2840,24 +2840,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Magdagdag ng redirect URI"
},
"settings.applications.button.cancel": {
"message": "Kanselahin"
},
"settings.applications.button.create": {
"message": "Lumikha ng app"
},
"settings.applications.button.delete": {
"message": "Tanggalin"
},
"settings.applications.button.edit": {
"message": "Baguhin"
},
"settings.applications.button.new": {
"message": "Bagong aplikasyon"
},
"settings.applications.button.save-changes": {
"message": "I-save ang mga pagbabago"
},
"settings.applications.button.upload-icon": {
"message": "I-upload ang ikono"
},
@@ -4667,24 +4667,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Ajouter une URI de redirection"
},
"settings.applications.button.cancel": {
"message": "Annuler"
},
"settings.applications.button.create": {
"message": "Créer une appli"
},
"settings.applications.button.delete": {
"message": "Supprimer"
},
"settings.applications.button.edit": {
"message": "Modifier"
},
"settings.applications.button.new": {
"message": "Nouvelle application"
},
"settings.applications.button.save-changes": {
"message": "Sauvegarder les changements"
},
"settings.applications.button.upload-icon": {
"message": "Charger une icône"
},
@@ -2282,9 +2282,6 @@
"settings.account.data-export.description": {
"message": "תבקש עותק של כל הדאטה האישי שהעלית ל-Modrinth. זה יכול לקחת כמה דקות."
},
"settings.applications.button.edit": {
"message": "ערוך"
},
"settings.applications.field.name": {
"message": "שם"
},
@@ -4130,24 +4130,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Átírányítás URI hozzáadása"
},
"settings.applications.button.cancel": {
"message": "Mégse"
},
"settings.applications.button.create": {
"message": "Alkalmazás létrehozása"
},
"settings.applications.button.delete": {
"message": "Törlés"
},
"settings.applications.button.edit": {
"message": "Szerkesztés"
},
"settings.applications.button.new": {
"message": "Új alkalmazás"
},
"settings.applications.button.save-changes": {
"message": "Módosítások mentése"
},
"settings.applications.button.upload-icon": {
"message": "Ikon feltöltése"
},
@@ -2918,24 +2918,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Tambah URI pengarahan ulang"
},
"settings.applications.button.cancel": {
"message": "Batal"
},
"settings.applications.button.create": {
"message": "Buat aplikasi"
},
"settings.applications.button.delete": {
"message": "Hapus"
},
"settings.applications.button.edit": {
"message": "Sunting"
},
"settings.applications.button.new": {
"message": "Aplikasi baru"
},
"settings.applications.button.save-changes": {
"message": "Simpan perubahan"
},
"settings.applications.button.upload-icon": {
"message": "Unggah ikon"
},
@@ -4667,24 +4667,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Aggiungi reindirizzamento"
},
"settings.applications.button.cancel": {
"message": "Annulla"
},
"settings.applications.button.create": {
"message": "Crea app"
},
"settings.applications.button.delete": {
"message": "Elimina"
},
"settings.applications.button.edit": {
"message": "Modifica"
},
"settings.applications.button.new": {
"message": "Nuova applicazione"
},
"settings.applications.button.save-changes": {
"message": "Salva modifiche"
},
"settings.applications.button.upload-icon": {
"message": "Carica icona"
},
@@ -3620,24 +3620,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "リダイレクトURIを追加"
},
"settings.applications.button.cancel": {
"message": "キャンセル"
},
"settings.applications.button.create": {
"message": "アプリを作成する"
},
"settings.applications.button.delete": {
"message": "削除"
},
"settings.applications.button.edit": {
"message": "編集"
},
"settings.applications.button.new": {
"message": "新規アプリケーション"
},
"settings.applications.button.save-changes": {
"message": "変更を保存する"
},
"settings.applications.button.upload-icon": {
"message": "アイコンをアップロード"
},
@@ -4631,24 +4631,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "리디렉션 URI 를 추가하세요"
},
"settings.applications.button.cancel": {
"message": "취소"
},
"settings.applications.button.create": {
"message": "앱 만들기"
},
"settings.applications.button.delete": {
"message": "지우기"
},
"settings.applications.button.edit": {
"message": "편집"
},
"settings.applications.button.new": {
"message": "새 애플리케이션"
},
"settings.applications.button.save-changes": {
"message": "변경사항 저장"
},
"settings.applications.button.upload-icon": {
"message": "아이콘 업로드"
},
@@ -3653,24 +3653,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Tambah URI pengubah hala"
},
"settings.applications.button.cancel": {
"message": "Batal"
},
"settings.applications.button.create": {
"message": "Cipta aplikasi"
},
"settings.applications.button.delete": {
"message": "Padam"
},
"settings.applications.button.edit": {
"message": "Sunting"
},
"settings.applications.button.new": {
"message": "Aplikasi baharu"
},
"settings.applications.button.save-changes": {
"message": "Simpan perubahan"
},
"settings.applications.button.upload-icon": {
"message": "Muat naik ikon"
},
@@ -4706,24 +4706,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Voeg een URI toe"
},
"settings.applications.button.cancel": {
"message": "Annuleren"
},
"settings.applications.button.create": {
"message": "Maak nieuwe"
},
"settings.applications.button.delete": {
"message": "Verwijder"
},
"settings.applications.button.edit": {
"message": "Bewerk"
},
"settings.applications.button.new": {
"message": "Nieuw project"
},
"settings.applications.button.save-changes": {
"message": "Wijzigingen opslaan"
},
"settings.applications.button.upload-icon": {
"message": "Pictogram uploaden"
},
@@ -3539,24 +3539,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Legg til en omdirings-URI"
},
"settings.applications.button.cancel": {
"message": "Avbryt"
},
"settings.applications.button.create": {
"message": "Lag app"
},
"settings.applications.button.delete": {
"message": "Slett"
},
"settings.applications.button.edit": {
"message": "Regiser"
},
"settings.applications.button.new": {
"message": "Ny søknad"
},
"settings.applications.button.save-changes": {
"message": "Lagre endringer"
},
"settings.applications.button.upload-icon": {
"message": "Last opp ikon"
},
@@ -4622,24 +4622,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Dodaj URI przekierowywania"
},
"settings.applications.button.cancel": {
"message": "Anuluj"
},
"settings.applications.button.create": {
"message": "Stwórz aplikację"
},
"settings.applications.button.delete": {
"message": "Usuń"
},
"settings.applications.button.edit": {
"message": "Edytuj"
},
"settings.applications.button.new": {
"message": "Nowa aplikacja"
},
"settings.applications.button.save-changes": {
"message": "Zapisz zmiany"
},
"settings.applications.button.upload-icon": {
"message": "Prześlij ikonę"
},
@@ -4709,24 +4709,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Adicionar URI de redirecionamento"
},
"settings.applications.button.cancel": {
"message": "Cancelar"
},
"settings.applications.button.create": {
"message": "Criar app"
},
"settings.applications.button.delete": {
"message": "Excluir"
},
"settings.applications.button.edit": {
"message": "Editar"
},
"settings.applications.button.new": {
"message": "Novo aplicativo"
},
"settings.applications.button.save-changes": {
"message": "Salvar alterações"
},
"settings.applications.button.upload-icon": {
"message": "Enviar ícone"
},
@@ -3350,24 +3350,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Adicionar um endereço de redirecionamento"
},
"settings.applications.button.cancel": {
"message": "Cancelar"
},
"settings.applications.button.create": {
"message": "Criar app"
},
"settings.applications.button.delete": {
"message": "Apagar"
},
"settings.applications.button.edit": {
"message": "Editar"
},
"settings.applications.button.new": {
"message": "Nova aplicação"
},
"settings.applications.button.save-changes": {
"message": "Guardar alterações"
},
"settings.applications.button.upload-icon": {
"message": "Carregar ícone"
},
@@ -1814,21 +1814,12 @@
"settings.applications.about": {
"message": "Despre"
},
"settings.applications.button.cancel": {
"message": "Anuleaza"
},
"settings.applications.button.create": {
"message": "Creaza aplicația"
},
"settings.applications.button.delete": {
"message": "Sterge"
},
"settings.applications.button.edit": {
"message": "Editeaza"
},
"settings.applications.button.save-changes": {
"message": "Salvează schimbările"
},
"settings.applications.client-id": {
"message": "ID Client"
},
@@ -4655,24 +4655,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Добавить адрес перенаправления"
},
"settings.applications.button.cancel": {
"message": "Отмена"
},
"settings.applications.button.create": {
"message": "Создать приложение"
},
"settings.applications.button.delete": {
"message": "Удалить"
},
"settings.applications.button.edit": {
"message": "Изменить"
},
"settings.applications.button.new": {
"message": "Новое приложение"
},
"settings.applications.button.save-changes": {
"message": "Сохранить изменения"
},
"settings.applications.button.upload-icon": {
"message": "Загрузить иконку"
},
@@ -3713,24 +3713,15 @@
"settings.applications.button.add-more": {
"message": "Lägg till mer"
},
"settings.applications.button.cancel": {
"message": "Avbryt"
},
"settings.applications.button.create": {
"message": "Skapa app"
},
"settings.applications.button.delete": {
"message": "Ta bort"
},
"settings.applications.button.edit": {
"message": "Redigera"
},
"settings.applications.button.new": {
"message": "Ny applikation"
},
"settings.applications.button.save-changes": {
"message": "Spara ändringar"
},
"settings.applications.button.upload-icon": {
"message": "Ladda upp ikon"
},
@@ -4118,24 +4118,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Yönlendirme URI'ları ekle"
},
"settings.applications.button.cancel": {
"message": "İptal"
},
"settings.applications.button.create": {
"message": "Uygulama oluştur"
},
"settings.applications.button.delete": {
"message": "Sil"
},
"settings.applications.button.edit": {
"message": "Düzenle"
},
"settings.applications.button.new": {
"message": "Yeni uygulama"
},
"settings.applications.button.save-changes": {
"message": "Değişiklikleri kaydet"
},
"settings.applications.button.upload-icon": {
"message": "Simge yükle"
},
@@ -4631,24 +4631,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Додати переадресацію URI"
},
"settings.applications.button.cancel": {
"message": "Скасувати"
},
"settings.applications.button.create": {
"message": "Створити застосунок"
},
"settings.applications.button.delete": {
"message": "Видалити"
},
"settings.applications.button.edit": {
"message": "Редагувати"
},
"settings.applications.button.new": {
"message": "Новий застосунок"
},
"settings.applications.button.save-changes": {
"message": "Зберегти зміни"
},
"settings.applications.button.upload-icon": {
"message": "Завантажити значок"
},
@@ -3818,24 +3818,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "Thêm URI chuyển hướng"
},
"settings.applications.button.cancel": {
"message": "Huỷ"
},
"settings.applications.button.create": {
"message": "Tạo ứng dụng"
},
"settings.applications.button.delete": {
"message": "Xoá"
},
"settings.applications.button.edit": {
"message": "Chỉnh sửa"
},
"settings.applications.button.new": {
"message": "Ứng dụng mới"
},
"settings.applications.button.save-changes": {
"message": "Lưu thay đổi"
},
"settings.applications.button.upload-icon": {
"message": "Tải lên biểu tượng"
},
@@ -4709,24 +4709,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "添加重定向 URI"
},
"settings.applications.button.cancel": {
"message": "取消"
},
"settings.applications.button.create": {
"message": "新建 App"
},
"settings.applications.button.delete": {
"message": "删除"
},
"settings.applications.button.edit": {
"message": "编辑"
},
"settings.applications.button.new": {
"message": "新建应用程序"
},
"settings.applications.button.save-changes": {
"message": "保存更改"
},
"settings.applications.button.upload-icon": {
"message": "上传图标"
},
@@ -4709,24 +4709,15 @@
"settings.applications.button.add-redirect-uri": {
"message": "新增重新導向 URI"
},
"settings.applications.button.cancel": {
"message": "取消"
},
"settings.applications.button.create": {
"message": "建立應用程式"
},
"settings.applications.button.delete": {
"message": "刪除"
},
"settings.applications.button.edit": {
"message": "編輯"
},
"settings.applications.button.new": {
"message": "新的應用程式"
},
"settings.applications.button.save-changes": {
"message": "儲存變更"
},
"settings.applications.button.upload-icon": {
"message": "上傳圖示"
},
@@ -305,7 +305,7 @@ import {
DropArea,
FileInput,
injectProjectPageContext,
NewModal as Modal,
Modal,
StyledInput,
useFormatDateTime,
} from '@modrinth/ui'
@@ -295,7 +295,7 @@ import {
DropArea,
FileInput,
injectProjectPageContext,
NewModal as Modal,
Modal,
StyledInput,
useFormatDateTime,
} from '@modrinth/ui'
@@ -10,7 +10,7 @@
:proceed-label="formatMessage(messages.proceedDeletion)"
@proceed="deleteVersion()"
/>
<NewModal
<Modal
v-if="auth.user && currentMember"
ref="packageModal"
:header="formatMessage(messages.packageDataPackHeader)"
@@ -79,7 +79,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<ProjectDownloadModal
ref="dependencyDownloadModal"
download-reason="dependency"
@@ -513,8 +513,8 @@ import {
injectNotificationManager,
injectProjectPageContext,
IntlFormatted,
Modal,
MultiSelect,
NewModal,
OverflowMenu,
useFormatBytes,
useFormatDateTime,
@@ -7,7 +7,7 @@
@proceed="confirmDeleteEvent"
/>
<NewModal
<Modal
ref="eventModal"
:header="modalMode === 'create' ? 'New event' : 'Edit event'"
width="480px"
@@ -121,7 +121,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
<div class="normal-page no-sidebar">
<div class="normal-page__content flex flex-col gap-4">
@@ -245,9 +245,9 @@ import {
DatePicker,
injectModrinthClient,
injectNotificationManager,
Modal,
MultiSelect,
type MultiSelectOption,
NewModal,
type SortDirection,
StyledInput,
Table,
@@ -318,7 +318,7 @@ const metricKindOptions: MultiSelectOption<AnalyticsEventMetricKind>[] = [
const allMetricKinds = metricKindOptions.map((option) => option.value)
const deleteEventModal = ref<InstanceType<typeof ConfirmModal> | null>(null)
const eventModal = ref<InstanceType<typeof NewModal> | null>(null)
const eventModal = ref<InstanceType<typeof Modal> | null>(null)
const titleInput = ref<InstanceType<typeof StyledInput> | null>(null)
const searchQuery = ref('')
const sortColumn = ref<EventColumnKey | undefined>('date')
@@ -1,5 +1,5 @@
<template>
<NewModal ref="refundModal">
<Modal ref="refundModal">
<template #title>
<span class="text-lg font-extrabold text-contrast">Refund charge</span>
</template>
@@ -57,8 +57,8 @@
</ButtonStyled>
</div>
</div>
</NewModal>
<NewModal ref="modifyModal">
</Modal>
<Modal ref="modifyModal">
<template #title>
<span class="text-lg font-extrabold text-contrast">Modify charge</span>
</template>
@@ -96,8 +96,8 @@
</ButtonStyled>
</div>
</div>
</NewModal>
<NewModal ref="creditModal">
</Modal>
<Modal ref="creditModal">
<template #title>
<span class="text-lg font-extrabold text-contrast">Credit subscription</span>
</template>
@@ -131,7 +131,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<div class="page">
<div
class="mb-4 flex items-center justify-between border-0 border-b border-solid border-divider pb-4"
@@ -232,7 +232,7 @@ import {
DropdownSelect,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
StyledInput,
Toggle,
useFormatDateTime,
+3 -3
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { CopyIcon, LibraryIcon, PlayIcon, SearchIcon } from '@modrinth/assets'
import { ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
import { ButtonStyled, Modal, StyledInput } from '@modrinth/ui'
import { computed, onMounted, ref } from 'vue'
import emails from '~/templates/emails'
@@ -126,7 +126,7 @@ onMounted(() => {
<template>
<div class="normal-page no-sidebar">
<h1 class="mb-4 text-3xl font-extrabold text-heading">Email templates</h1>
<NewModal
<Modal
ref="previewModal"
header="Preview email"
width="min(92vw, 1000px)"
@@ -188,7 +188,7 @@ onMounted(() => {
</div>
</div>
</div>
</NewModal>
</Modal>
<div class="normal-page__content">
<div class="flex flex-wrap items-center gap-3">
<StyledInput
@@ -1,5 +1,5 @@
<template>
<NewModal ref="createNoticeModal">
<Modal ref="createNoticeModal">
<template #title>
<span class="text-lg font-extrabold text-contrast">{{
editingNotice ? `Editing notice #${editingNotice?.id}` : 'Creating a notice'
@@ -118,7 +118,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<AssignNoticeModal ref="assignNoticeModal" @close="refreshNotices" />
<div class="page">
<div
@@ -270,7 +270,7 @@ import {
defineMessages,
injectModrinthClient,
injectNotificationManager,
NewModal,
Modal,
ServerNotice,
StyledInput,
TagItem,
@@ -301,7 +301,7 @@ const formatDateTimeShortMonth = useFormatDateTime({
type ServerNoticeType = Archon.Notices.v0.ListedNotice
const notices = ref<ServerNoticeType[]>([])
const createNoticeModal = ref<InstanceType<typeof NewModal>>()
const createNoticeModal = ref<InstanceType<typeof Modal>>()
const assignNoticeModal = ref<InstanceType<typeof AssignNoticeModal>>()
await refreshNotices()
@@ -12,7 +12,7 @@
:proceed-label="formatMessage(commonMessages.deleteLabel)"
@proceed="deleteCollection()"
/>
<NewModal v-if="canEdit" ref="editModal" :header="formatMessage(messages.editingCollection)">
<Modal v-if="canEdit" ref="editModal" :header="formatMessage(messages.editingCollection)">
<div class="flex w-[30rem] flex-col gap-3">
<div class="flow-root">
<div class="group relative float-end ml-4">
@@ -136,7 +136,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<NormalPage :sidebar="cosmetics.leftContentLayout ? 'left' : 'right'">
<template #header>
<div class="flex flex-col gap-4">
@@ -413,8 +413,8 @@ import {
injectModrinthClient,
injectNotificationManager,
IntlFormatted,
Modal,
NavTabs,
NewModal,
normalizeChildren,
NormalPage,
OverflowMenu,
@@ -1,6 +1,6 @@
<template>
<div>
<NewModal ref="editLinksModal" :header="formatMessage(messages.editLinksHeader)">
<Modal ref="editLinksModal" :header="formatMessage(messages.editLinksHeader)">
<div class="universal-modal links-modal !p-0">
<p>{{ formatMessage(messages.editLinksDescription) }}</p>
<section class="links">
@@ -155,7 +155,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<ModalCreation ref="modal_creation" />
<section class="relative overflow-hidden rounded-2xl">
<Table
@@ -365,7 +365,7 @@ import {
FloatingActionBar,
injectNotificationManager,
IntlFormatted,
NewModal,
Modal,
ProjectStatusBadge,
StyledInput,
Table,
@@ -1,5 +1,5 @@
<template>
<NewModal ref="editModal" header="Edit external project">
<Modal ref="editModal" header="Edit external project">
<form class="flex flex-col gap-2" @submit.prevent="saveExternalProjectEdit">
<label class="font-semibold text-contrast" for="edit-form-title">Title</label>
<StyledInput id="edit-form-title" v-model="editForm.title" type="text" />
@@ -47,7 +47,7 @@
</ButtonStyled>
</div>
</form>
</NewModal>
</Modal>
<div>
<form class="flex gap-2" @submit.prevent="executeSearch">
<StyledInput
@@ -139,7 +139,7 @@ import {
externalProjectLicenseStatusMessages,
ExternalProjectLookupCard,
injectModrinthClient,
NewModal,
Modal,
StyledInput,
useVIntl,
} from '@modrinth/ui'
@@ -150,7 +150,7 @@ const query = ref('')
const isLoading = ref(false)
const isSavingEdit = ref(false)
const client = injectModrinthClient()
const editModal = useTemplateRef<InstanceType<typeof NewModal>>('editModal')
const editModal = useTemplateRef<InstanceType<typeof Modal>>('editModal')
useHead({ title: 'External projects - Modrinth' })
@@ -1,6 +1,6 @@
<template>
<div>
<NewModal ref="editLinksModal" :header="formatMessage(messages.editLinksHeader)">
<Modal ref="editLinksModal" :header="formatMessage(messages.editLinksHeader)">
<div class="universal-modal links-modal !p-0">
<p>{{ formatMessage(messages.editLinksDescription) }}</p>
<section class="links">
@@ -155,7 +155,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<ModalCreation ref="modal_creation" :organization-id="organization.id" />
<section class="relative overflow-hidden rounded-2xl">
<Table
@@ -366,7 +366,7 @@ import {
injectModrinthClient,
injectNotificationManager,
IntlFormatted,
NewModal,
Modal,
ProjectStatusBadge,
StyledInput,
Table,
+9 -9
View File
@@ -9,7 +9,7 @@
:has-to-type="true"
@proceed="deleteAccount"
/>
<NewModal
<Modal
ref="changeEmailModal"
width="500px"
:header="`${auth.user.email ? formatMessage(messages.changeEmailHeaderChange) : formatMessage(messages.changeEmailHeaderAdd)}`"
@@ -48,8 +48,8 @@
</ButtonStyled>
</div>
</div>
</NewModal>
<NewModal
</Modal>
<Modal
ref="managePasswordModal"
width="600px"
:header="`${
@@ -169,8 +169,8 @@
</template>
</div>
</div>
</NewModal>
<NewModal
</Modal>
<Modal
ref="manageTwoFactorModal"
width="500px"
:header="
@@ -307,8 +307,8 @@
</div>
</template>
</div>
</NewModal>
<NewModal
</Modal>
<Modal
ref="manageProvidersModal"
width="500px"
:header="formatMessage(messages.manageProvidersModalHeader)"
@@ -343,7 +343,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<section class="universal-card">
<h2 class="text-2xl">{{ formatMessage(messages.accountSecurityTitle) }}</h2>
@@ -500,7 +500,7 @@ import {
defineMessages,
injectNotificationManager,
IntlFormatted,
NewModal,
Modal,
StyledInput,
Table,
useVIntl,
@@ -7,10 +7,10 @@
:proceed-label="formatMessage(messages.deleteConfirmButton)"
@proceed="removeApp(editingId)"
/>
<Modal ref="appModal" :header="formatMessage(messages.modalHeader)">
<div class="universal-modal">
<label for="app-name"
><span class="label__title">{{ formatMessage(messages.nameLabel) }}</span>
<Modal ref="appModal" :header="formatMessage(messages.modalHeader)" width="40rem" scrollable>
<div class="flex flex-col">
<label for="app-name" class="m-0 mb-2 text-lg font-semibold text-contrast">
{{ formatMessage(messages.nameLabel) }}
</label>
<StyledInput
id="app-name"
@@ -19,8 +19,12 @@
autocomplete="off"
:placeholder="formatMessage(messages.namePlaceholder)"
/>
<label v-if="editingId" for="app-icon"
><span class="label__title">{{ formatMessage(messages.iconLabel) }}</span>
<label
v-if="editingId"
for="app-icon"
class="mb-2 mt-4 text-lg font-semibold text-contrast"
>
{{ formatMessage(messages.iconLabel) }}
</label>
<div v-if="editingId" class="icon-submission">
<Avatar size="md" :src="icon" />
@@ -36,8 +40,8 @@
</FileInput>
</ButtonStyled>
</div>
<label v-if="editingId" for="app-url">
<span class="label__title">{{ formatMessage(messages.urlLabel) }}</span>
<label v-if="editingId" for="app-url" class="mb-2 mt-4 text-lg font-semibold text-contrast">
{{ formatMessage(messages.urlLabel) }}
</label>
<StyledInput
v-if="editingId"
@@ -48,8 +52,12 @@
autocomplete="off"
:placeholder="formatMessage(messages.urlPlaceholder)"
/>
<label v-if="editingId" for="app-description">
<span class="label__title">{{ formatMessage(messages.descriptionLabel) }}</span>
<label
v-if="editingId"
for="app-description"
class="mb-2 mt-4 text-lg font-semibold text-contrast"
>
{{ formatMessage(messages.descriptionLabel) }}
</label>
<StyledInput
v-if="editingId"
@@ -61,15 +69,12 @@
:placeholder="formatMessage(messages.descriptionPlaceholder)"
input-class="h-24 resize-y"
/>
<label for="app-scopes"
><span class="label__title">{{ formatMessage(messages.scopesLabel) }}</span>
<label for="app-scopes" class="mb-2 mt-4 text-lg font-semibold text-contrast">
{{ formatMessage(messages.scopesLabel) }}
</label>
<div
id="app-scopes"
class="scope-items mt-2 grid grid-cols-1 gap-x-6 gap-y-4 min-[600px]:grid-cols-2"
>
<div id="app-scopes" class="scope-items grid grid-cols-1 gap-6 min-[600px]:grid-cols-2">
<div v-for="category in scopeCategories" :key="category.name" class="flex flex-col gap-2">
<h4 class="m-0 border-b border-divider pb-1 text-base font-bold text-contrast">
<h4 class="m-0 text-base font-medium text-primary">
{{ category.name }}
</h4>
<div class="flex flex-col gap-2">
@@ -83,8 +88,8 @@
</div>
</div>
</div>
<label for="app-redirect-uris" class="mt-4"
><span class="label__title">{{ formatMessage(messages.redirectUrisLabel) }}</span>
<label for="app-redirect-uris" class="mb-2 mt-4 text-lg font-semibold text-contrast">
{{ formatMessage(messages.redirectUrisLabel) }}
</label>
<div class="uri-input-list">
<div v-for="(_, index) in redirectUris" :key="index">
@@ -93,6 +98,7 @@
v-model="redirectUris[index]"
:maxlength="2048"
type="url"
class="w-80"
autocomplete="off"
:placeholder="formatMessage(messages.redirectUriPlaceholder)"
/>
@@ -116,18 +122,20 @@
</ButtonStyled>
</div>
</div>
</div>
<div class="submit-row input-group push-right">
<template #actions>
<div class="flex justify-end gap-2 p-2">
<ButtonStyled>
<button @click="$refs.appModal.hide()">
<XIcon />
{{ formatMessage(messages.cancel) }}
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-if="editingId" color="brand">
<button :disabled="!canSubmit" @click="editApp">
<SaveIcon />
{{ formatMessage(messages.saveChanges) }}
{{ formatMessage(commonMessages.saveChangesButton) }}
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand">
@@ -137,7 +145,7 @@
</button>
</ButtonStyled>
</div>
</div>
</template>
</Modal>
<div class="header__row">
@@ -220,7 +228,7 @@
"
>
<EditIcon />
{{ formatMessage(messages.edit) }}
{{ formatMessage(commonMessages.editButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="red">
@@ -255,6 +263,7 @@ import {
injectModrinthClient,
injectNotificationManager,
IntlFormatted,
Modal,
normalizeChildren,
StyledInput,
useFormatDateTime,
@@ -262,7 +271,6 @@ import {
} from '@modrinth/ui'
import { useQuery } from '@tanstack/vue-query'
import Modal from '~/components/ui/Modal.vue'
import {
getScopeValue,
hasScope,
@@ -355,14 +363,6 @@ const messages = defineMessages({
id: 'settings.applications.button.add-redirect-uri',
defaultMessage: 'Add a redirect URI',
},
cancel: {
id: 'settings.applications.button.cancel',
defaultMessage: 'Cancel',
},
saveChanges: {
id: 'settings.applications.button.save-changes',
defaultMessage: 'Save changes',
},
createApp: {
id: 'settings.applications.button.create',
defaultMessage: 'Create app',
@@ -396,10 +396,6 @@ const messages = defineMessages({
id: 'settings.applications.created-on',
defaultMessage: 'Created on {date}',
},
edit: {
id: 'settings.applications.button.edit',
defaultMessage: 'Edit',
},
delete: {
id: 'settings.applications.button.delete',
defaultMessage: 'Delete',
+3 -3
View File
@@ -7,7 +7,7 @@
:proceed-label="formatMessage(deleteModalMessages.action)"
@proceed="removePat(deletePatIndex)"
/>
<NewModal
<Modal
ref="patModal"
:header="
editPatId !== null
@@ -86,7 +86,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<div class="header__row">
<div class="header__title">
@@ -211,7 +211,7 @@ import {
injectModrinthClient,
injectNotificationManager,
IntlFormatted,
NewModal,
Modal,
StyledInput,
useFormatDateTime,
useRelativeTime,
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="formatMessage(messages.createHeader)">
<Modal ref="modal" :header="formatMessage(messages.createHeader)">
<div class="flex flex-col">
<label v-if="showUserField" class="contents" for="create-affiliate-user-input">
<span class="text-lg font-semibold text-contrast mb-1">
@@ -55,7 +55,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script lang="ts"></script>
<script setup lang="ts">
@@ -64,7 +64,7 @@ import { computed, ref, useTemplateRef } from 'vue'
import { defineMessages, useVIntl } from '../../composables/i18n'
import { commonMessages } from '../../utils/common-messages'
import { AutoBrandIcon, ButtonStyled, NewModal, StyledInput } from '../index'
import { AutoBrandIcon, ButtonStyled, Modal, StyledInput } from '../index'
export type CreateAffiliateProps = { sourceName: string; username?: string }
const props = withDefaults(
@@ -82,7 +82,7 @@ const emit = defineEmits<{
(e: 'create', data: CreateAffiliateProps): void
}>()
const modal = useTemplateRef<typeof NewModal>('modal')
const modal = useTemplateRef<typeof Modal>('modal')
const { formatMessage } = useVIntl()
const affiliateLinkTitle = ref('')
@@ -1,5 +1,5 @@
<template>
<NewModal ref="linkModal" :header="formatMessage(messages.linkModalHeader)" class="!w-[40rem]">
<Modal ref="linkModal" :header="formatMessage(messages.linkModalHeader)" class="!w-[40rem]">
<div class="modal-insert">
<label class="label" for="insert-link-label">
<span class="label__title">{{ formatMessage(messages.linkModalLabelFieldTitle) }}</span>
@@ -66,8 +66,8 @@
</ButtonStyled>
</div>
</div>
</NewModal>
<NewModal ref="imageModal" :header="formatMessage(messages.imageModalHeader)" class="!w-[40rem]">
</Modal>
<Modal ref="imageModal" :header="formatMessage(messages.imageModalHeader)" class="!w-[40rem]">
<div class="modal-insert">
<label class="label" for="insert-image-alt">
<span class="label__title">
@@ -164,8 +164,8 @@
</ButtonStyled>
</div>
</div>
</NewModal>
<NewModal ref="videoModal" :header="formatMessage(messages.videoModalHeader)" class="!w-[40rem]">
</Modal>
<Modal ref="videoModal" :header="formatMessage(messages.videoModalHeader)" class="!w-[40rem]">
<div class="modal-insert">
<label class="label" for="insert-video-url">
<span class="label__title">
@@ -224,7 +224,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
<div class="block grow w-full">
<div class="editor-action-row w-full">
<div class="w-full flex justify-between items-center flex-wrap gap-2">
@@ -335,7 +335,7 @@ import { type Component, computed, onBeforeUnmount, onMounted, ref, toRef, watch
import { defineMessages, type MessageDescriptor, useVIntl } from '../../composables/i18n'
import { commonMessages } from '../../utils/common-messages.ts'
import NewModal from '../modal/NewModal.vue'
import Modal from '../modal/Modal.vue'
import ButtonStyled from './ButtonStyled.vue'
import Chips from './Chips.vue'
import FileInput from './FileInput.vue'
@@ -1042,9 +1042,9 @@ const videoMarkdown = computed(() => {
return ''
})
const linkModal = ref<InstanceType<typeof NewModal> | null>(null)
const imageModal = ref<InstanceType<typeof NewModal> | null>(null)
const videoModal = ref<InstanceType<typeof NewModal> | null>(null)
const linkModal = ref<InstanceType<typeof Modal> | null>(null)
const imageModal = ref<InstanceType<typeof Modal> | null>(null)
const videoModal = ref<InstanceType<typeof Modal> | null>(null)
function resetModalStates() {
linkText.value = ''
@@ -1,5 +1,5 @@
<template>
<NewModal
<Modal
ref="modal"
:scrollable="true"
max-content-height="72vh"
@@ -114,12 +114,12 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script lang="ts">
import { ChevronRightIcon, SpinnerIcon } from '@modrinth/assets'
import { ButtonStyled, NewModal } from '@modrinth/ui'
import { ButtonStyled, Modal } from '@modrinth/ui'
import type { Component } from 'vue'
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
@@ -175,7 +175,7 @@ const props = withDefaults(
},
)
const modal = useTemplateRef<InstanceType<typeof NewModal>>('modal')
const modal = useTemplateRef<InstanceType<typeof Modal>>('modal')
const currentStageIndex = ref<number>(0)
function show() {
@@ -5,13 +5,13 @@ import { nextTick, ref, useTemplateRef } from 'vue'
import { defineMessages, useVIntl } from '../../composables/i18n'
import { commonMessages } from '../../utils'
import { ButtonStyled, NewModal } from '../index'
import { ButtonStyled, Modal } from '../index'
import type { AddPaymentMethodProps } from './AddPaymentMethod.vue'
import AddPaymentMethod from './AddPaymentMethod.vue'
const { formatMessage } = useVIntl()
const modal = useTemplateRef<InstanceType<typeof NewModal>>('modal')
const modal = useTemplateRef<InstanceType<typeof Modal>>('modal')
const addPaymentMethod = useTemplateRef<InstanceType<typeof AddPaymentMethod>>('addPaymentMethod')
const props = defineProps<AddPaymentMethodProps>()
@@ -40,7 +40,7 @@ defineExpose({
</script>
<template>
<NewModal ref="modal">
<Modal ref="modal">
<template #title>
<span class="text-lg font-extrabold text-contrast">
{{ formatMessage(messages.addingPaymentMethod) }}
@@ -71,5 +71,5 @@ defineExpose({
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
@@ -18,8 +18,8 @@ import { defineMessage, type MessageDescriptor, useVIntl } from '../../composabl
import { useStripe } from '../../composables/stripe'
import { commonMessages } from '../../utils'
import { ButtonStyled } from '../index'
import Modal from '../modal/Modal.vue'
import ModalLoadingIndicator from '../modal/ModalLoadingIndicator.vue'
import NewModal from '../modal/NewModal.vue'
import PlanSelector from './ServersPurchase0Plan.vue'
import RegionSelector from './ServersPurchase1Region.vue'
import PaymentMethodSelector from './ServersPurchase2PaymentMethod.vue'
@@ -65,7 +65,7 @@ const props = defineProps<{
affiliateCode?: string | null
}>()
const modal = useTemplateRef<InstanceType<typeof NewModal>>('modal')
const modal = useTemplateRef<InstanceType<typeof Modal>>('modal')
const selectedPlan = ref<Labrinth.Billing.Internal.Product>()
const selectedInterval = ref<ServerBillingInterval>('quarterly')
const loading = ref(false)
@@ -448,7 +448,7 @@ function goToBreadcrumbStep(id: string) {
}
</script>
<template>
<NewModal ref="modal" @hide="handleHide">
<Modal ref="modal" @hide="handleHide">
<template #title>
<div class="flex items-center gap-1 font-bold text-secondary">
<template v-for="(step, index) in visibleSteps" :key="step">
@@ -601,5 +601,5 @@ function goToBreadcrumbStep(id: string) {
</button>
</ButtonStyled>
</div>
</NewModal>
</Modal>
</template>
@@ -1,5 +1,5 @@
<template>
<NewModal ref="purchaseModal">
<Modal ref="purchaseModal">
<template #title>
<span class="text-contrast text-xl font-extrabold">
<template v-if="productType === 'midas'">Subscribe to Modrinth+!</template>
@@ -446,7 +446,7 @@
</button>
</template>
</div>
</NewModal>
</Modal>
</template>
<script setup>
@@ -479,7 +479,7 @@ import Combobox from '../base/Combobox.vue'
import Slider from '../base/Slider.vue'
import StyledInput from '../base/StyledInput.vue'
import AnimatedLogo from '../brand/AnimatedLogo.vue'
import NewModal from '../modal/NewModal.vue'
import Modal from '../modal/Modal.vue'
import LoaderIcon from '../servers/icons/LoaderIcon.vue'
const { formatMessage } = useVIntl()
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" max-width="550px">
<Modal ref="modal" max-width="550px">
<template #title>
<div class="text-2xl font-semibold text-contrast">{{ formatMessage(messages.title) }}</div>
</template>
@@ -92,7 +92,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -105,7 +105,7 @@ import { injectNotificationManager } from '#ui/providers/web-notifications.ts'
import { useFormatDateTime, useFormatPrice } from '../../composables'
import { defineMessages, useVIntl } from '../../composables/i18n'
import IntlFormatted from '../base/IntlFormatted.vue'
import { ButtonStyled, NewModal } from '../index'
import { ButtonStyled, Modal } from '../index'
const { addNotification } = injectNotificationManager()
const { formatMessage } = useVIntl()
@@ -148,7 +148,7 @@ const emit = defineEmits<{
const formatDate = useFormatDateTime({ dateStyle: 'long' })
const formatPrice = useFormatPrice()
const modal = useTemplateRef<InstanceType<typeof NewModal>>('modal')
const modal = useTemplateRef<InstanceType<typeof Modal>>('modal')
const modalData = ref<ResubscribeModalState | null>(null)
@@ -4,7 +4,7 @@ import { ChevronRightIcon, ExternalIcon, XIcon } from '@modrinth/assets'
import { computed, ref, useTemplateRef } from 'vue'
import ButtonStyled from '../base/ButtonStyled.vue'
import NewModal from '../modal/NewModal.vue'
import Modal from '../modal/Modal.vue'
import type { ServerBillingInterval } from './ModrinthServersPurchaseModal.vue'
import PlanSelector from './ServersPurchase0Plan.vue'
@@ -23,7 +23,7 @@ const emit = defineEmits<{
(e: 'continue', payload: { interval: ServerBillingInterval; planId: string | null }): void
}>()
const modal = useTemplateRef<InstanceType<typeof NewModal>>('modal')
const modal = useTemplateRef<InstanceType<typeof Modal>>('modal')
const selectedPlan = ref<Labrinth.Billing.Internal.Product>()
const selectedInterval = ref<ServerBillingInterval>('quarterly')
@@ -91,7 +91,7 @@ defineExpose({
</script>
<template>
<NewModal ref="modal" :on-hide="handleModalHide" no-padding>
<Modal ref="modal" :on-hide="handleModalHide" no-padding>
<template #title>
<div class="flex items-center gap-1 font-bold text-secondary">
<span class="text-contrast">Plan</span>
@@ -165,5 +165,5 @@ defineExpose({
</div>
</Transition>
</div>
</NewModal>
</Modal>
</template>
@@ -3,7 +3,7 @@ import type { Labrinth } from '@modrinth/api-client'
import { CheckIcon, PlusIcon, SearchIcon, SpinnerIcon, XIcon } from '@modrinth/assets'
import { computed, nextTick, ref } from 'vue'
import { ButtonStyled, NewModal, StyledInput } from '#ui/components'
import { ButtonStyled, Modal, StyledInput } from '#ui/components'
import { commonMessages } from '#ui/utils'
import { defineMessages, useVIntl } from '../../composables/i18n'
@@ -60,7 +60,7 @@ type AssignableFileEntry = {
sourceLabel: string
}
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
const modalRef = ref<InstanceType<typeof Modal> | null>(null)
const assignableEntries = ref<AssignableFileEntry[]>([])
const searchQuery = ref('')
const searchInputRef = ref<{ focus: () => void } | null>(null)
@@ -176,7 +176,7 @@ defineExpose({ show, hide })
</script>
<template>
<NewModal
<Modal
ref="modalRef"
:header="formatMessage(messages.addFilesModalTitle)"
max-width="560px"
@@ -283,5 +283,5 @@ defineExpose({ show, hide })
</div>
</div>
</template>
</NewModal>
</Modal>
</template>
@@ -4,7 +4,7 @@ import { PlusIcon, SearchIcon, SpinnerIcon, XIcon } from '@modrinth/assets'
import { useMutation } from '@tanstack/vue-query'
import { computed, ref, useTemplateRef } from 'vue'
import { Accordion, ButtonStyled, NewModal, StyledInput } from '#ui/components'
import { Accordion, ButtonStyled, Modal, StyledInput } from '#ui/components'
import { injectModrinthClient, injectNotificationManager } from '../../providers'
import AttributionGroupFilePicker from './AttributionGroupFilePicker.vue'
@@ -41,7 +41,7 @@ type ExternalProject = {
const client = injectModrinthClient()
const { addNotification } = injectNotificationManager()
const modalRef = useTemplateRef<InstanceType<typeof NewModal>>('modalRef')
const modalRef = useTemplateRef<InstanceType<typeof Modal>>('modalRef')
const searchAccordionRef = useTemplateRef<InstanceType<typeof Accordion>>('searchAccordionRef')
const filesAccordionRef = useTemplateRef<InstanceType<typeof Accordion>>('filesAccordionRef')
const searchInputRef = useTemplateRef<InstanceType<typeof StyledInput>>('searchInputRef')
@@ -185,7 +185,7 @@ defineExpose({ show, hide })
</script>
<template>
<NewModal
<Modal
ref="modalRef"
header="Add to existing entry"
max-width="720px"
@@ -324,5 +324,5 @@ defineExpose({ show, hide })
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
@@ -9,7 +9,7 @@ import {
ButtonStyled,
Combobox,
type ComboboxOption,
NewModal,
Modal,
StyledInput,
} from '#ui/components'
@@ -35,7 +35,7 @@ const emit = defineEmits<{
const client = injectModrinthClient()
const { addNotification } = injectNotificationManager()
const modalRef = useTemplateRef<InstanceType<typeof NewModal>>('modalRef')
const modalRef = useTemplateRef<InstanceType<typeof Modal>>('modalRef')
const statusOptions: ComboboxOption<ExternalLicenseStatus>[] = [
{ value: 'yes', label: 'Yes' },
@@ -140,7 +140,7 @@ defineExpose({ show, hide })
</script>
<template>
<NewModal
<Modal
ref="modalRef"
header="Adding to global permissions database"
max-width="640px"
@@ -241,5 +241,5 @@ defineExpose({ show, hide })
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :header="localizeIfPossible(title)" fade="warning" max-width="500px">
<Modal ref="modal" :header="localizeIfPossible(title)" fade="warning" max-width="500px">
<div class="flex flex-col gap-6">
<Admonition :type="admonitionType" :header="localizeIfPossible(header)">
{{ localizeIfPossible(body) }}
@@ -22,7 +22,7 @@
</ButtonStyled>
</div>
</template>
</NewModal>
</Modal>
</template>
<script setup lang="ts">
@@ -33,7 +33,7 @@ import Admonition from '#ui/components/base/Admonition.vue'
import ButtonStyled from '#ui/components/base/ButtonStyled.vue'
import { defineMessage, type MessageDescriptor, useVIntl } from '#ui/composables/i18n'
import NewModal from './NewModal.vue'
import Modal from './Modal.vue'
const { formatMessage } = useVIntl()
@@ -80,7 +80,7 @@ function localizeIfPossible(message: MessageDescriptor | string) {
return typeof message === 'string' ? message : formatMessage(message)
}
const modal = ref<InstanceType<typeof NewModal>>()
const modal = ref<InstanceType<typeof Modal>>()
let resolvePromise: ((value: boolean) => void) | null = null
function prompt(): Promise<boolean> {
@@ -1,5 +1,5 @@
<template>
<NewModal ref="modal" :noblur="noblur" :danger="danger" :on-hide="onHide" max-width="550px">
<Modal ref="modal" :noblur="noblur" :danger="danger" :on-hide="onHide" max-width="550px">
<template #title>
<slot name="title">
<span class="font-extrabold text-contrast text-lg">{{ title }}</span>
@@ -45,7 +45,7 @@
</ButtonStyled>
</div>
</div>
</NewModal>
</Modal>
</template>
<script setup>
@@ -55,7 +55,7 @@ import { computed, ref } from 'vue'
import ButtonStyled from '../base/ButtonStyled.vue'
import StyledInput from '../base/StyledInput.vue'
import NewModal from './NewModal.vue'
import Modal from './Modal.vue'
const props = defineProps({
confirmationText: {
+430 -91
View File
@@ -1,82 +1,403 @@
<template>
<div v-if="shown">
<div
:class="{ shown: actuallyShown }"
class="tauri-overlay"
data-tauri-drag-region
@click="() => (closable ? hide() : {})"
/>
<div
:class="{
shown: actuallyShown,
noblur: props.noblur,
}"
class="modal-overlay"
@click="() => (closable ? hide() : {})"
/>
<div class="modal-container" :class="{ shown: actuallyShown }">
<div class="modal-body">
<div v-if="props.header" class="header">
<h1>{{ props.header }}</h1>
<button v-if="closable" class="btn icon-only transparent" @click="hide">
<XIcon />
</button>
</div>
<div class="content">
<slot />
<Teleport to="body">
<div v-if="open" class="modal-root">
<div
:class="{ shown: visible }"
class="tauri-overlay"
data-tauri-drag-region
@click="() => (closeOnClickOutside && closable ? hide() : {})"
/>
<div
:class="[
'modal-overlay',
{
shown: visible,
noblur: effectiveNoblur,
},
computedFade,
]"
@click="() => (closeOnClickOutside && closable ? hide() : {})"
/>
<div class="modal-container" :class="{ shown: visible }">
<div
ref="modalBodyRef"
role="dialog"
aria-modal="true"
:aria-labelledby="headerId"
class="modal-body flex flex-col bg-bg-raised rounded-2xl border border-solid border-surface-5"
v-bind="$attrs"
@keydown="handleKeyDown"
>
<div
v-if="!hideHeader"
data-tauri-drag-region
class="grid grid-cols-[1fr_auto] items-center gap-4 p-6 border-solid border-0 border-b-[1px] border-surface-5 max-w-full"
>
<div class="flex text-wrap break-words items-center gap-3 min-w-0">
<slot name="title">
<span v-if="header" :id="headerId" class="text-2xl font-semibold text-contrast">
{{ header }}
</span>
</slot>
</div>
<div class="flex items-center gap-2">
<slot name="header-actions" />
<ButtonStyled v-if="closable" circular>
<button
v-tooltip="closeLabel"
:aria-label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
</div>
</div>
<ButtonStyled
v-if="props.mergeHeader && closable"
class="absolute top-4 right-4 z-10"
circular
>
<button
v-tooltip="closeLabel"
:aria-label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
<div v-if="scrollable" class="relative flex-1 min-h-0 flex flex-col">
<Transition
enter-active-class="transition-all duration-200 ease-out"
enter-from-class="opacity-0 max-h-0"
enter-to-class="opacity-100 max-h-6"
leave-active-class="transition-all duration-200 ease-in"
leave-from-class="opacity-100 max-h-6"
leave-to-class="opacity-0 max-h-0"
>
<div
v-if="showTopFade"
class="pointer-events-none absolute left-0 right-0 top-0 z-10 h-6 bg-gradient-to-b from-bg-raised to-transparent"
/>
</Transition>
<div
ref="scrollContainer"
data-modal-content
:class="[
'flex-1 min-h-0 overflow-y-auto',
props.noPadding ? '' : 'p-6 !pb-1 sm:pb-6',
{ 'pt-12': props.mergeHeader && closable && !props.noPadding },
]"
:style="{ maxHeight: maxContentHeight }"
@scroll="checkScrollState"
>
<slot> You just lost the game.</slot>
</div>
<Transition
enter-active-class="transition-all duration-200 ease-out"
enter-from-class="opacity-0 max-h-0"
enter-to-class="opacity-100 max-h-6"
leave-active-class="transition-all duration-200 ease-in"
leave-from-class="opacity-100 max-h-6"
leave-to-class="opacity-0 max-h-0"
>
<div
v-if="showBottomFade"
class="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-6 bg-gradient-to-t from-bg-raised to-transparent"
/>
</Transition>
</div>
<div
v-else
data-modal-content
:class="[
'min-h-0',
props.noPadding ? '' : 'overflow-y-auto p-6',
{ 'pt-12': props.mergeHeader && closable && !props.noPadding },
]"
>
<slot> You just lost the game.</slot>
</div>
<div
v-if="$slots.actions"
:class="{ 'pt-4 border-0 border-t border-solid border-surface-5': actionsDivider }"
class="p-4"
>
<slot name="actions" />
</div>
</div>
</div>
</div>
</div>
<div v-else></div>
</Teleport>
</template>
<script setup>
<script setup lang="ts">
import { XIcon } from '@modrinth/assets'
import { ref } from 'vue'
import { computed, nextTick, onUnmounted, ref } from 'vue'
const props = defineProps({
header: {
type: String,
default: null,
},
noblur: {
type: Boolean,
default: false,
},
closable: {
type: Boolean,
default: true,
},
onHide: {
type: Function,
default() {
return () => {}
},
import { useVIntl } from '../../composables/i18n'
import { useModalStack } from '../../composables/modal-stack'
import { useScrollIndicator } from '../../composables/scroll-indicator'
import { injectModalBehavior } from '../../providers'
import { commonMessages } from '../../utils/common-messages'
import ButtonStyled from '../base/ButtonStyled.vue'
const { formatMessage } = useVIntl()
const modalBehavior = injectModalBehavior(null)
const {
push: pushModal,
pop: popModal,
isTopmost: isTopmostModal,
stackSize: modalStackSize,
} = useModalStack()
const props = withDefaults(
defineProps<{
noblur?: boolean
closable?: boolean
/** @deprecated Use `fade="danger"` instead */
danger?: boolean
fade?: 'standard' | 'warning' | 'danger'
closeOnEsc?: boolean
closeOnClickOutside?: boolean
warnOnClose?: boolean
header?: string
hideHeader?: boolean
onHide?: () => void
onAfterHide?: () => void
onShow?: () => void
beforeHide?: () => boolean
mergeHeader?: boolean
scrollable?: boolean
maxContentHeight?: string
/** Removes padding from the content area. Useful for edge-to-edge layouts. */
noPadding?: boolean
/** Max width for the modal (e.g., '460px', '600px'). Defaults to '60rem'. */
maxWidth?: string
/** Width for the modal body (e.g., '460px', '600px'). */
width?: string
/** Disables all close actions (close button, ESC key, click outside). */
disableClose?: boolean
actionsDivider?: boolean
}>(),
{
type: true,
noblur: undefined,
closable: true,
danger: false,
fade: undefined,
closeOnClickOutside: true,
closeOnEsc: true,
warnOnClose: false,
header: undefined,
hideHeader: false,
onHide: () => {},
onAfterHide: () => {},
onShow: () => {},
beforeHide: undefined,
mergeHeader: false,
// TODO: migrate all modals to use scrollable and remove this prop
scrollable: false,
maxContentHeight: '70vh',
noPadding: false,
maxWidth: undefined,
width: undefined,
disableClose: false,
actionsDivider: false,
},
)
const effectiveNoblur = computed(() => props.noblur ?? modalBehavior?.noblur.value ?? false)
const computedFade = computed(() => {
if (props.fade) return props.fade
if (props.danger) return 'danger'
return 'standard'
})
const shown = ref(false)
const actuallyShown = ref(false)
const modalId = `modal-${Math.random().toString(36).slice(2, 9)}`
const headerId = `${modalId}-header`
const closeLabel = computed(() => formatMessage(commonMessages.closeButton))
function show() {
shown.value = true
const open = ref(false)
const visible = ref(false)
const stackDepth = ref(0)
const modalBodyRef = ref<HTMLElement | null>(null)
let previousFocusEl: Element | null = null
let hideTimeout: ReturnType<typeof setTimeout> | null = null
const scrollContainer = ref<HTMLElement | null>(null)
const { showTopFade, showBottomFade, checkScrollState } = useScrollIndicator(scrollContainer)
const FOCUSABLE_SELECTOR =
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
function getFocusableElements(): HTMLElement[] {
if (!modalBodyRef.value) return []
return Array.from(modalBodyRef.value.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))
}
function show(event?: MouseEvent) {
if (hideTimeout) {
clearTimeout(hideTimeout)
hideTimeout = null
}
props.onShow?.()
const wasEmpty = modalStackSize() === 0
stackDepth.value = modalStackSize()
open.value = true
previousFocusEl = document.activeElement
pushModal()
if (wasEmpty) modalBehavior?.onShow?.()
document.body.style.overflow = 'hidden'
window.addEventListener('keydown', handleWindowKeyDown)
window.addEventListener('mousedown', updateMousePosition)
if (event) {
updateMousePosition(event)
} else {
mouseX.value = Math.round(window.innerWidth / 2)
mouseY.value = Math.round(window.innerHeight / 2)
}
setTimeout(() => {
actuallyShown.value = true
visible.value = true
nextTick(() => {
const focusable = getFocusableElements()
if (focusable.length > 0) {
focusable[0].focus()
} else {
modalBodyRef.value?.focus()
}
})
}, 50)
}
function hide() {
function hide(): boolean {
if (props.disableClose) {
return false
}
if (props.beforeHide?.() === false) {
return false
}
props.onHide?.()
actuallyShown.value = false
setTimeout(() => {
shown.value = false
resetMousePosition()
visible.value = false
popModal()
if (modalStackSize() === 0) {
modalBehavior?.onHide?.()
document.body.style.overflow = ''
}
window.removeEventListener('keydown', handleWindowKeyDown)
window.removeEventListener('mousedown', updateMousePosition)
if (previousFocusEl instanceof HTMLElement) {
previousFocusEl.focus()
}
previousFocusEl = null
hideTimeout = setTimeout(() => {
open.value = false
hideTimeout = null
nextTick(() => props.onAfterHide?.())
}, 300)
return true
}
async function scrollToBottom(behavior: ScrollBehavior = 'smooth') {
await nextTick()
if (!scrollContainer.value) return
scrollContainer.value.scrollTo({
top: scrollContainer.value.scrollHeight,
behavior,
})
requestAnimationFrame(checkScrollState)
}
defineExpose({
show,
hide,
checkScrollState,
scrollToBottom,
})
const mouseX = ref(0)
const mouseY = ref(0)
const MODAL_STACK_BASE_Z = 100
const stackZBase = computed(() => MODAL_STACK_BASE_Z + stackDepth.value * 10)
const stackOverlayZ = computed(() => stackZBase.value + 19)
const stackTauriZ = computed(() => stackZBase.value + 20)
const stackContainerZ = computed(() => stackZBase.value + 21)
const resolvedMaxWidth = computed(() => props.maxWidth ?? '60rem')
const resolvedWidth = computed(() => props.width ?? 'fit-content')
const mouseXOffset = computed(() => `calc((-50vw + ${mouseX.value}px) / 16)`)
const mouseYOffset = computed(() => `calc((-50vh + ${mouseY.value}px) / 16)`)
function updateMousePosition(event: { clientX: number; clientY: number }) {
mouseX.value = event.clientX
mouseY.value = event.clientY
}
function resetMousePosition() {
mouseX.value = Math.round(window.innerWidth / 2)
mouseY.value = Math.round(window.innerHeight / 2)
}
onUnmounted(() => {
if (hideTimeout) {
clearTimeout(hideTimeout)
hideTimeout = null
}
if (open.value) {
popModal()
window.removeEventListener('keydown', handleWindowKeyDown)
window.removeEventListener('mousedown', updateMousePosition)
if (modalStackSize() === 0) {
document.body.style.overflow = ''
modalBehavior?.onHide?.()
}
}
})
function handleWindowKeyDown(event: KeyboardEvent) {
if (props.closeOnEsc && event.key === 'Escape' && props.closable) {
if (!isTopmostModal()) return
hide()
}
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Tab') {
const focusable = getFocusableElements()
if (focusable.length === 0) return
const first = focusable[0]
const last = focusable[focusable.length - 1]
if (event.shiftKey) {
if (document.activeElement === first) {
event.preventDefault()
last.focus()
}
} else {
if (document.activeElement === last) {
event.preventDefault()
first.focus()
}
}
}
}
defineOptions({
inheritAttrs: false,
})
</script>
@@ -88,7 +409,7 @@ defineExpose({
left: 0;
width: 100%;
height: 100px;
z-index: 20;
z-index: v-bind(stackTauriZ);
&.shown {
opacity: 1;
@@ -97,14 +418,33 @@ defineExpose({
}
.modal-overlay {
visibility: hidden;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 19;
transition: all 0.3s ease-in-out;
inset: -5rem;
z-index: v-bind(stackOverlayZ);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: all 0.2s ease-out;
//transform: translate(
// calc((-50vw + var(--_mouse-x, 50vw) * 1px) / 2),
// calc((-50vh + var(--_mouse-y, 50vh) * 1px) / 2)
// )
// scaleX(0.8) scaleY(0.5);
border-radius: 180px;
//filter: blur(5px);
// Fade variants
&.standard {
background: linear-gradient(to bottom, rgba(29, 48, 43, 0.52) 0%, rgba(14, 21, 26, 0.95) 100%);
}
&.warning {
background: linear-gradient(to bottom, rgba(48, 38, 29, 0.52) 0%, rgba(26, 20, 14, 0.95) 100%);
}
&.danger {
background: linear-gradient(to bottom, rgba(43, 18, 26, 0.52) 0%, rgba(49, 10, 15, 0.95) 100%);
}
@media (prefers-reduced-motion) {
transition: none !important;
@@ -113,12 +453,19 @@ defineExpose({
&.shown {
opacity: 1;
visibility: visible;
background: hsla(0, 0%, 0%, 0.5);
backdrop-filter: blur(3px);
pointer-events: auto;
backdrop-filter: blur(5px);
}
&.noblur {
backdrop-filter: none;
filter: none;
}
}
.modrinth-parent__no-modal-blurs {
.modal-overlay {
backdrop-filter: none;
}
}
@@ -131,52 +478,44 @@ defineExpose({
display: flex;
justify-content: center;
align-items: center;
z-index: 21;
z-index: v-bind(stackContainerZ);
visibility: hidden;
pointer-events: none;
transform: translate(v-bind(mouseXOffset), v-bind(mouseYOffset));
transition: none;
&.shown {
visibility: visible;
.modal-body {
transform: translate(0, 0);
transition: all 0.2s ease-out;
> .modal-body {
opacity: 1;
visibility: visible;
transform: translateY(0);
scale: 1;
}
}
.modal-body {
> .modal-body {
position: fixed;
box-shadow: var(--shadow-raised), var(--shadow-inset);
border-radius: var(--radius-lg);
background-color: var(--color-raised-bg);
box-shadow: 4px 4px 26px 10px rgba(0, 0, 0, 0.08);
max-height: calc(100% - 2 * var(--gap-lg));
overflow-y: visible;
width: 600px;
max-width: min(v-bind(resolvedMaxWidth), calc(100% - 2 * var(--gap-lg)));
overflow-y: hidden;
overflow-x: hidden;
width: v-bind(resolvedWidth);
pointer-events: auto;
scale: 0.97;
.header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--color-bg);
padding: var(--gap-md) var(--gap-lg);
h1 {
font-weight: bold;
font-size: 1.25rem;
}
}
transform: translateY(50vh);
visibility: hidden;
opacity: 0;
transition: all 0.25s ease-in-out;
transition: all 0.2s ease-in-out;
@media (prefers-reduced-motion) {
transition: none !important;
}
@media screen and (max-width: 650px) {
@media screen and (max-width: 640px) {
width: calc(100% - 2 * var(--gap-lg));
}
}
@@ -1,523 +0,0 @@
<template>
<Teleport to="body">
<div v-if="open" class="modal-root">
<div
:class="{ shown: visible }"
class="tauri-overlay"
data-tauri-drag-region
@click="() => (closeOnClickOutside && closable ? hide() : {})"
/>
<div
:class="[
'modal-overlay',
{
shown: visible,
noblur: effectiveNoblur,
},
computedFade,
]"
@click="() => (closeOnClickOutside && closable ? hide() : {})"
/>
<div class="modal-container" :class="{ shown: visible }">
<div
ref="modalBodyRef"
role="dialog"
aria-modal="true"
:aria-labelledby="headerId"
class="modal-body flex flex-col bg-bg-raised rounded-2xl border border-solid border-surface-5"
v-bind="$attrs"
@keydown="handleKeyDown"
>
<div
v-if="!hideHeader"
data-tauri-drag-region
class="grid grid-cols-[1fr_auto] items-center gap-4 p-6 border-solid border-0 border-b-[1px] border-surface-5 max-w-full"
>
<div class="flex text-wrap break-words items-center gap-3 min-w-0">
<slot name="title">
<span v-if="header" :id="headerId" class="text-2xl font-semibold text-contrast">
{{ header }}
</span>
</slot>
</div>
<div class="flex items-center gap-2">
<slot name="header-actions" />
<ButtonStyled v-if="closable" circular>
<button
v-tooltip="closeLabel"
:aria-label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
</div>
</div>
<ButtonStyled
v-if="props.mergeHeader && closable"
class="absolute top-4 right-4 z-10"
circular
>
<button
v-tooltip="closeLabel"
:aria-label="closeLabel"
:disabled="disableClose"
@click="hide"
>
<XIcon aria-hidden="true" />
</button>
</ButtonStyled>
<div v-if="scrollable" class="relative flex-1 min-h-0 flex flex-col">
<Transition
enter-active-class="transition-all duration-200 ease-out"
enter-from-class="opacity-0 max-h-0"
enter-to-class="opacity-100 max-h-6"
leave-active-class="transition-all duration-200 ease-in"
leave-from-class="opacity-100 max-h-6"
leave-to-class="opacity-0 max-h-0"
>
<div
v-if="showTopFade"
class="pointer-events-none absolute left-0 right-0 top-0 z-10 h-6 bg-gradient-to-b from-bg-raised to-transparent"
/>
</Transition>
<div
ref="scrollContainer"
data-modal-content
:class="[
'flex-1 min-h-0 overflow-y-auto',
props.noPadding ? '' : 'p-6 !pb-1 sm:pb-6',
{ 'pt-12': props.mergeHeader && closable && !props.noPadding },
]"
:style="{ maxHeight: maxContentHeight }"
@scroll="checkScrollState"
>
<slot> You just lost the game.</slot>
</div>
<Transition
enter-active-class="transition-all duration-200 ease-out"
enter-from-class="opacity-0 max-h-0"
enter-to-class="opacity-100 max-h-6"
leave-active-class="transition-all duration-200 ease-in"
leave-from-class="opacity-100 max-h-6"
leave-to-class="opacity-0 max-h-0"
>
<div
v-if="showBottomFade"
class="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-6 bg-gradient-to-t from-bg-raised to-transparent"
/>
</Transition>
</div>
<div
v-else
data-modal-content
:class="[
'min-h-0',
props.noPadding ? '' : 'overflow-y-auto p-6',
{ 'pt-12': props.mergeHeader && closable && !props.noPadding },
]"
>
<slot> You just lost the game.</slot>
</div>
<div
v-if="$slots.actions"
:class="{ 'pt-4 border-0 border-t border-solid border-surface-5': actionsDivider }"
class="p-4"
>
<slot name="actions" />
</div>
</div>
</div>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { XIcon } from '@modrinth/assets'
import { computed, nextTick, onUnmounted, ref } from 'vue'
import { useVIntl } from '../../composables/i18n'
import { useModalStack } from '../../composables/modal-stack'
import { useScrollIndicator } from '../../composables/scroll-indicator'
import { injectModalBehavior } from '../../providers'
import { commonMessages } from '../../utils/common-messages'
import ButtonStyled from '../base/ButtonStyled.vue'
const { formatMessage } = useVIntl()
const modalBehavior = injectModalBehavior(null)
const {
push: pushModal,
pop: popModal,
isTopmost: isTopmostModal,
stackSize: modalStackSize,
} = useModalStack()
const props = withDefaults(
defineProps<{
noblur?: boolean
closable?: boolean
/** @deprecated Use `fade="danger"` instead */
danger?: boolean
fade?: 'standard' | 'warning' | 'danger'
closeOnEsc?: boolean
closeOnClickOutside?: boolean
warnOnClose?: boolean
header?: string
hideHeader?: boolean
onHide?: () => void
onAfterHide?: () => void
onShow?: () => void
beforeHide?: () => boolean
mergeHeader?: boolean
scrollable?: boolean
maxContentHeight?: string
/** Removes padding from the content area. Useful for edge-to-edge layouts. */
noPadding?: boolean
/** Max width for the modal (e.g., '460px', '600px'). Defaults to '60rem'. */
maxWidth?: string
/** Width for the modal body (e.g., '460px', '600px'). */
width?: string
/** Disables all close actions (close button, ESC key, click outside). */
disableClose?: boolean
actionsDivider?: boolean
}>(),
{
type: true,
noblur: undefined,
closable: true,
danger: false,
fade: undefined,
closeOnClickOutside: true,
closeOnEsc: true,
warnOnClose: false,
header: undefined,
hideHeader: false,
onHide: () => {},
onAfterHide: () => {},
onShow: () => {},
beforeHide: undefined,
mergeHeader: false,
// TODO: migrate all modals to use scrollable and remove this prop
scrollable: false,
maxContentHeight: '70vh',
noPadding: false,
maxWidth: undefined,
width: undefined,
disableClose: false,
actionsDivider: false,
},
)
const effectiveNoblur = computed(() => props.noblur ?? modalBehavior?.noblur.value ?? false)
const computedFade = computed(() => {
if (props.fade) return props.fade
if (props.danger) return 'danger'
return 'standard'
})
const modalId = `modal-${Math.random().toString(36).slice(2, 9)}`
const headerId = `${modalId}-header`
const closeLabel = computed(() => formatMessage(commonMessages.closeButton))
const open = ref(false)
const visible = ref(false)
const stackDepth = ref(0)
const modalBodyRef = ref<HTMLElement | null>(null)
let previousFocusEl: Element | null = null
let hideTimeout: ReturnType<typeof setTimeout> | null = null
const scrollContainer = ref<HTMLElement | null>(null)
const { showTopFade, showBottomFade, checkScrollState } = useScrollIndicator(scrollContainer)
const FOCUSABLE_SELECTOR =
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
function getFocusableElements(): HTMLElement[] {
if (!modalBodyRef.value) return []
return Array.from(modalBodyRef.value.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))
}
function show(event?: MouseEvent) {
if (hideTimeout) {
clearTimeout(hideTimeout)
hideTimeout = null
}
props.onShow?.()
const wasEmpty = modalStackSize() === 0
stackDepth.value = modalStackSize()
open.value = true
previousFocusEl = document.activeElement
pushModal()
if (wasEmpty) modalBehavior?.onShow?.()
document.body.style.overflow = 'hidden'
window.addEventListener('keydown', handleWindowKeyDown)
window.addEventListener('mousedown', updateMousePosition)
if (event) {
updateMousePosition(event)
} else {
mouseX.value = Math.round(window.innerWidth / 2)
mouseY.value = Math.round(window.innerHeight / 2)
}
setTimeout(() => {
visible.value = true
nextTick(() => {
const focusable = getFocusableElements()
if (focusable.length > 0) {
focusable[0].focus()
} else {
modalBodyRef.value?.focus()
}
})
}, 50)
}
function hide(): boolean {
if (props.disableClose) {
return false
}
if (props.beforeHide?.() === false) {
return false
}
props.onHide?.()
resetMousePosition()
visible.value = false
popModal()
if (modalStackSize() === 0) {
modalBehavior?.onHide?.()
document.body.style.overflow = ''
}
window.removeEventListener('keydown', handleWindowKeyDown)
window.removeEventListener('mousedown', updateMousePosition)
if (previousFocusEl instanceof HTMLElement) {
previousFocusEl.focus()
}
previousFocusEl = null
hideTimeout = setTimeout(() => {
open.value = false
hideTimeout = null
nextTick(() => props.onAfterHide?.())
}, 300)
return true
}
async function scrollToBottom(behavior: ScrollBehavior = 'smooth') {
await nextTick()
if (!scrollContainer.value) return
scrollContainer.value.scrollTo({
top: scrollContainer.value.scrollHeight,
behavior,
})
requestAnimationFrame(checkScrollState)
}
defineExpose({
show,
hide,
checkScrollState,
scrollToBottom,
})
const mouseX = ref(0)
const mouseY = ref(0)
const MODAL_STACK_BASE_Z = 100
const stackZBase = computed(() => MODAL_STACK_BASE_Z + stackDepth.value * 10)
const stackOverlayZ = computed(() => stackZBase.value + 19)
const stackTauriZ = computed(() => stackZBase.value + 20)
const stackContainerZ = computed(() => stackZBase.value + 21)
const resolvedMaxWidth = computed(() => props.maxWidth ?? '60rem')
const resolvedWidth = computed(() => props.width ?? 'fit-content')
const mouseXOffset = computed(() => `calc((-50vw + ${mouseX.value}px) / 16)`)
const mouseYOffset = computed(() => `calc((-50vh + ${mouseY.value}px) / 16)`)
function updateMousePosition(event: { clientX: number; clientY: number }) {
mouseX.value = event.clientX
mouseY.value = event.clientY
}
function resetMousePosition() {
mouseX.value = Math.round(window.innerWidth / 2)
mouseY.value = Math.round(window.innerHeight / 2)
}
onUnmounted(() => {
if (hideTimeout) {
clearTimeout(hideTimeout)
hideTimeout = null
}
if (open.value) {
popModal()
window.removeEventListener('keydown', handleWindowKeyDown)
window.removeEventListener('mousedown', updateMousePosition)
if (modalStackSize() === 0) {
document.body.style.overflow = ''
modalBehavior?.onHide?.()
}
}
})
function handleWindowKeyDown(event: KeyboardEvent) {
if (props.closeOnEsc && event.key === 'Escape' && props.closable) {
if (!isTopmostModal()) return
hide()
}
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Tab') {
const focusable = getFocusableElements()
if (focusable.length === 0) return
const first = focusable[0]
const last = focusable[focusable.length - 1]
if (event.shiftKey) {
if (document.activeElement === first) {
event.preventDefault()
last.focus()
}
} else {
if (document.activeElement === last) {
event.preventDefault()
first.focus()
}
}
}
}
defineOptions({
inheritAttrs: false,
})
</script>
<style lang="scss" scoped>
.tauri-overlay {
position: fixed;
visibility: hidden;
top: 0;
left: 0;
width: 100%;
height: 100px;
z-index: v-bind(stackTauriZ);
&.shown {
opacity: 1;
visibility: visible;
}
}
.modal-overlay {
position: fixed;
inset: -5rem;
z-index: v-bind(stackOverlayZ);
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: all 0.2s ease-out;
//transform: translate(
// calc((-50vw + var(--_mouse-x, 50vw) * 1px) / 2),
// calc((-50vh + var(--_mouse-y, 50vh) * 1px) / 2)
// )
// scaleX(0.8) scaleY(0.5);
border-radius: 180px;
//filter: blur(5px);
// Fade variants
&.standard {
background: linear-gradient(to bottom, rgba(29, 48, 43, 0.52) 0%, rgba(14, 21, 26, 0.95) 100%);
}
&.warning {
background: linear-gradient(to bottom, rgba(48, 38, 29, 0.52) 0%, rgba(26, 20, 14, 0.95) 100%);
}
&.danger {
background: linear-gradient(to bottom, rgba(43, 18, 26, 0.52) 0%, rgba(49, 10, 15, 0.95) 100%);
}
@media (prefers-reduced-motion) {
transition: none !important;
}
&.shown {
opacity: 1;
visibility: visible;
pointer-events: auto;
backdrop-filter: blur(5px);
}
&.noblur {
backdrop-filter: none;
filter: none;
}
}
.modrinth-parent__no-modal-blurs {
.modal-overlay {
backdrop-filter: none;
}
}
.modal-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: v-bind(stackContainerZ);
visibility: hidden;
pointer-events: none;
transform: translate(v-bind(mouseXOffset), v-bind(mouseYOffset));
transition: none;
&.shown {
visibility: visible;
transform: translate(0, 0);
transition: all 0.2s ease-out;
> .modal-body {
opacity: 1;
visibility: visible;
scale: 1;
}
}
> .modal-body {
position: fixed;
box-shadow: 4px 4px 26px 10px rgba(0, 0, 0, 0.08);
max-height: calc(100% - 2 * var(--gap-lg));
max-width: min(v-bind(resolvedMaxWidth), calc(100% - 2 * var(--gap-lg)));
overflow-y: hidden;
overflow-x: hidden;
width: v-bind(resolvedWidth);
pointer-events: auto;
scale: 0.97;
visibility: hidden;
opacity: 0;
transition: all 0.2s ease-in-out;
@media (prefers-reduced-motion) {
transition: none !important;
}
@media screen and (max-width: 640px) {
width: calc(100% - 2 * var(--gap-lg));
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More