mirror of
https://github.com/modrinth/code.git
synced 2026-08-30 19:46:33 +00:00
fix moderation collapsibles & do a consistency pass (#7330)
* fix moderation collapsibles & do a consistency pass * extract tech review sources * improve buttons * buttn * improve markdown styling * fix nested details * save filters + fix reports reloading * prepr
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<AutoLink
|
||||
:to="to"
|
||||
class="mb-4 flex w-fit items-center gap-2 rounded-lg px-2 py-0.5 pl-0 text-link"
|
||||
>
|
||||
<ChevronLeftIcon aria-hidden="true" class="shrink-0" />
|
||||
<slot />
|
||||
</AutoLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ChevronLeftIcon } from '@modrinth/assets'
|
||||
|
||||
import AutoLink from './AutoLink.vue'
|
||||
|
||||
defineProps<{
|
||||
to: unknown
|
||||
}>()
|
||||
</script>
|
||||
@@ -2,28 +2,29 @@
|
||||
<div class="relative overflow-hidden">
|
||||
<div
|
||||
class="collapsible-region-content"
|
||||
:class="{ open: !collapsed }"
|
||||
:class="{ open: !collapsed || disabled }"
|
||||
:style="{ '--collapsed-height': collapsedHeight }"
|
||||
>
|
||||
<div :class="{ 'pointer-events-none select-none pb-16': collapsed }">
|
||||
<div :class="{ 'pointer-events-none select-none': collapsed }">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="collapsed"
|
||||
class="pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent"
|
||||
:class="gradientTo"
|
||||
v-if="!disabled"
|
||||
class="pointer-events-none absolute inset-0 bg-gradient-to-b from-transparent transition-opacity duration-250"
|
||||
:class="[gradientTo, { 'opacity-0': !collapsed }]"
|
||||
/>
|
||||
|
||||
<div class="absolute bottom-4 left-1/2 z-20 -translate-x-1/2">
|
||||
<Button
|
||||
type="quiet"
|
||||
class="flex items-center gap-1 text-xs !rounded-full"
|
||||
@click="collapsed = !collapsed"
|
||||
>
|
||||
<ExpandIcon v-if="collapsed" />
|
||||
<CollapseIcon v-else />
|
||||
<div v-if="!collapsed && !disabled" class="top-4 right-4 z-20 group absolute">
|
||||
<Button v-tooltip="collapseText" type="quiet" circular icon-only @click="collapsed = true">
|
||||
<DropdownIcon class="rotate-180" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-if="!disabled" class="absolute bottom-4 left-1/2 z-20 -translate-x-1/2">
|
||||
<Button type="quiet" class="text-xs" @click="collapsed = !collapsed">
|
||||
<DropdownIcon class="transition-transform" :class="{ 'rotate-180': !collapsed }" />
|
||||
{{ collapsed ? expandText : collapseText }}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -31,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CollapseIcon, ExpandIcon } from '@modrinth/assets'
|
||||
import { DropdownIcon } from '@modrinth/assets'
|
||||
|
||||
import { Button } from '#ui/components/base/buttons'
|
||||
|
||||
@@ -41,12 +42,14 @@ withDefaults(
|
||||
collapseText?: string
|
||||
collapsedHeight?: string
|
||||
gradientTo?: string
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{
|
||||
expandText: 'Expand',
|
||||
collapseText: 'Collapse',
|
||||
collapsedHeight: '8rem',
|
||||
gradientTo: 'to-surface-2',
|
||||
disabled: false,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -56,8 +59,12 @@ const collapsed = defineModel<boolean>('collapsed', { default: true })
|
||||
<style scoped>
|
||||
.collapsible-region-content {
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
transition: grid-template-rows 0.3s linear;
|
||||
grid-template-rows: minmax(var(--collapsed-height), 0fr);
|
||||
transition: grid-template-rows 500ms var(--ease-out-expo);
|
||||
|
||||
& > div {
|
||||
grid-row: 1 / span 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion) {
|
||||
@@ -67,16 +74,10 @@ const collapsed = defineModel<boolean>('collapsed', { default: true })
|
||||
}
|
||||
|
||||
.collapsible-region-content.open {
|
||||
grid-template-rows: 1fr;
|
||||
grid-template-rows: minmax(var(--collapsed-height), 1fr);
|
||||
}
|
||||
|
||||
.collapsible-region-content > div {
|
||||
overflow: hidden;
|
||||
min-height: var(--collapsed-height);
|
||||
transition: min-height 0.3s linear;
|
||||
}
|
||||
|
||||
.collapsible-region-content.open > div {
|
||||
min-height: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<button
|
||||
class="!m-0 inline-flex w-fit select-text items-center gap-2 rounded-[10px] bg-[var(--color-button-bg)] px-2 py-1 font-mono text-sm text-primary transition-[opacity,filter,transform,outline] duration-200 ease-in-out hover:brightness-[1.25] active:scale-95 active:brightness-[0.8] motion-reduce:transition-none [&>svg]:h-[1em] [&>svg]:w-[1em]"
|
||||
class="rounded-lg border border-solid border-surface-5 bg-surface-2 text-xs !m-0 inline-flex w-fit select-text items-center gap-2 px-2 py-1 font-mono text-primary transition-[opacity,filter,transform,outline] duration-200 ease-in-out hover:brightness-[1.25] active:scale-95 motion-reduce:transition-none [&>svg]:h-[1em] [&>svg]:w-[1em]"
|
||||
:title="formatMessage(copiedMessage)"
|
||||
@click="copyText"
|
||||
>
|
||||
<span>{{ displayText ?? text }}</span>
|
||||
<CheckIcon v-if="copied" />
|
||||
<ClipboardCopyIcon v-else />
|
||||
<CopyIcon v-else />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CheckIcon, ClipboardCopyIcon } from '@modrinth/assets'
|
||||
import { CheckIcon, CopyIcon } from '@modrinth/assets'
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
|
||||
import { defineMessage, useVIntl } from '../../composables/i18n'
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<IconButton
|
||||
v-tooltip="label"
|
||||
:label="label"
|
||||
:disabled="copied"
|
||||
class="relative grid place-items-center overflow-hidden"
|
||||
@click="copyToClipboard"
|
||||
>
|
||||
<CheckIcon
|
||||
class="absolute transition-all ease-in-out"
|
||||
:class="copied ? 'translate-y-0' : 'translate-y-7'"
|
||||
/>
|
||||
<LinkIcon
|
||||
class="absolute transition-all ease-in-out"
|
||||
:class="copied ? '-translate-y-7' : 'translate-y-0'"
|
||||
/>
|
||||
</IconButton>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CheckIcon, LinkIcon } from '@modrinth/assets'
|
||||
import { computed, onBeforeUnmount, ref } from 'vue'
|
||||
|
||||
import { defineMessage, useVIntl } from '../../composables/i18n'
|
||||
import { commonMessages } from '../../utils/common-messages'
|
||||
import IconButton from './buttons/IconButton.vue'
|
||||
|
||||
const copyLinkMessage = commonMessages.copyLinkButton
|
||||
const copiedToClipboardMessage = defineMessage({
|
||||
id: 'button.copied-to-clipboard',
|
||||
defaultMessage: 'Copied to clipboard',
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const props = defineProps<{
|
||||
url: string
|
||||
copyLabel?: string
|
||||
copiedLabel?: string
|
||||
}>()
|
||||
|
||||
const copied = ref(false)
|
||||
let copiedResetTimeout: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
const label = computed(() => {
|
||||
if (copied.value) {
|
||||
return props.copiedLabel ?? formatMessage(copiedToClipboardMessage)
|
||||
}
|
||||
|
||||
return props.copyLabel ?? formatMessage(copyLinkMessage)
|
||||
})
|
||||
|
||||
async function copyToClipboard() {
|
||||
await navigator.clipboard.writeText(props.url)
|
||||
copied.value = true
|
||||
clearTimeout(copiedResetTimeout)
|
||||
copiedResetTimeout = setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => clearTimeout(copiedResetTimeout))
|
||||
</script>
|
||||
@@ -67,7 +67,7 @@ defineExpose({ element })
|
||||
/>
|
||||
<div
|
||||
data-anchored-scroll-region
|
||||
class="flex min-w-48 flex-col p-2 overflow-y-auto"
|
||||
class="flex flex-col p-2 overflow-y-auto"
|
||||
:style="{ maxHeight: props.panelStyle.maxHeight }"
|
||||
>
|
||||
<slot />
|
||||
|
||||
@@ -5,6 +5,7 @@ export { default as AppearingProgressBar } from './AppearingProgressBar.vue'
|
||||
export { default as AutoBrandIcon } from './AutoBrandIcon.vue'
|
||||
export { default as AutoLink } from './AutoLink.vue'
|
||||
export { default as Avatar } from './Avatar.vue'
|
||||
export { default as BackToParentLink } from './BackToParentLink.vue'
|
||||
export { default as Badge } from './Badge.vue'
|
||||
export { default as BaseTerminal } from './BaseTerminal.vue'
|
||||
export { default as BasicMarkdownText } from './BasicMarkdownText.vue'
|
||||
@@ -46,6 +47,7 @@ export { default as CollapsibleRegion } from './CollapsibleRegion.vue'
|
||||
export type { ComboboxOption, ComboboxSearchInputVariant } from './Combobox.vue'
|
||||
export { default as Combobox } from './Combobox.vue'
|
||||
export { default as CopyCode } from './CopyCode.vue'
|
||||
export { default as CopyLinkButton } from './CopyLinkButton.vue'
|
||||
export { default as DoubleIcon } from './DoubleIcon.vue'
|
||||
export { default as DropArea } from './DropArea.vue'
|
||||
export type { DropdownFilterBarCategory, DropdownFilterBarOption } from './DropdownFilterBar.vue'
|
||||
|
||||
@@ -43,7 +43,9 @@ async function copyProjectLink() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-surface-3 p-4 rounded-2xl flex flex-col gap-3">
|
||||
<div
|
||||
class="bg-surface-3 p-4 rounded-2xl flex flex-col gap-3 border border-solid border-surface-4"
|
||||
>
|
||||
<div class="flex gap-4 justify-between">
|
||||
<div class="flex flex-col gap-2">
|
||||
<span class="text-contrast font-semibold">{{ title }}</span>
|
||||
@@ -88,7 +90,9 @@ async function copyProjectLink() {
|
||||
<div class="font-medium">Notes:</div>
|
||||
<div>{{ notes ?? 'N/A' }}</div>
|
||||
</div>
|
||||
<div class="bg-surface-2 p-4 rounded-2xl flex flex-col gap-3">
|
||||
<div
|
||||
class="bg-surface-2 p-4 rounded-2xl flex flex-col gap-3 border border-solid border-surface-4"
|
||||
>
|
||||
<span class="text-contrast font-semibold">Files</span>
|
||||
<span v-if="!(files?.length > 0)" class="text-secondary">
|
||||
No files available for external project.
|
||||
|
||||
@@ -176,6 +176,9 @@
|
||||
"button.continue": {
|
||||
"defaultMessage": "Continue"
|
||||
},
|
||||
"button.copied-to-clipboard": {
|
||||
"defaultMessage": "Copied to clipboard"
|
||||
},
|
||||
"button.copy-filename": {
|
||||
"defaultMessage": "Copy filename"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import BackToParentLink from '../../components/base/BackToParentLink.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/BackToParentLink',
|
||||
component: BackToParentLink,
|
||||
render: (args) => ({
|
||||
components: { BackToParentLink },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<BackToParentLink v-bind="args">All versions</BackToParentLink>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof BackToParentLink>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
to: '/versions',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import CopyLinkButton from '../../components/base/CopyLinkButton.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/CopyLinkButton',
|
||||
component: CopyLinkButton,
|
||||
} satisfies Meta<typeof CopyLinkButton>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
url: 'https://modrinth.com',
|
||||
},
|
||||
}
|
||||
|
||||
export const CustomLabels: Story = {
|
||||
args: {
|
||||
url: 'https://modrinth.com',
|
||||
copyLabel: 'Copy project link',
|
||||
copiedLabel: 'Project link copied',
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user