mirror of
https://github.com/modrinth/code.git
synced 2026-08-28 10:34:53 +00:00
refactor: Button components (#6929)
* feat: refactor button components
* fix: qa
* fix: storybook
* a11y: pass
* fix: build
* fix: rename default ->md
* fix: lint
* docs: buttons.md
* refactor part 1
* refactor part 2
* fix: undo refactor for fresh restart
* refactor
* Revert "refactor"
This reverts commit 96d65902d7.
* refactor: part 1
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: qa
* fix: remove text-contrast
* fix: qa
* fix: v-tooltip
* fix: lint
* fix: split broken
* fix: passkey qa
* fix: qa
* fix: qa
* fix: prepr
* fix: splitbutton
* improve button group seam
---------
Signed-off-by: Calum H. <calum@modrinth.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
@@ -153,16 +153,14 @@ For components that need user interaction to show:
|
||||
```typescript
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { Button, NewModal },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
return { modalRef }
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled @click="modalRef?.show()">
|
||||
<button>Open Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="modalRef?.show()">Open Modal</Button>
|
||||
<NewModal ref="modalRef" header="Example Modal">
|
||||
<p>Modal content</p>
|
||||
</NewModal>
|
||||
@@ -199,10 +197,10 @@ Components should use relative imports, not the package alias:
|
||||
|
||||
```typescript
|
||||
// ❌ BAD - Causes circular dependency in Storybook
|
||||
import { ButtonStyled } from '@modrinth/ui'
|
||||
import { Button } from '@modrinth/ui'
|
||||
|
||||
// ✅ GOOD - Use relative imports
|
||||
import ButtonStyled from '../base/ButtonStyled.vue'
|
||||
import Button from '../components/base/buttons/Button.vue'
|
||||
```
|
||||
|
||||
### 2. Object/Array Prop Defaults Must Be Factory Functions
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Admonition from '../../components/base/Admonition.vue'
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Admonition',
|
||||
@@ -78,7 +78,7 @@ export const HeaderWithTimestamp: Story = {
|
||||
|
||||
export const WithTopRightActions: Story = {
|
||||
render: () => ({
|
||||
components: { Admonition, ButtonStyled },
|
||||
components: { Admonition, Button },
|
||||
template: /*html*/ `
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<Admonition
|
||||
@@ -88,9 +88,7 @@ export const WithTopRightActions: Story = {
|
||||
>
|
||||
Uploading server files...
|
||||
<template #top-right-actions>
|
||||
<ButtonStyled type="outlined" color="blue">
|
||||
<button class="!border" type="button">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" class="!border !text-blue [&>svg]:!text-blue !shadow-[inset_0_0_0_1px_var(--color-blue)]" native-type="button">Cancel</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
<Admonition
|
||||
@@ -100,9 +98,7 @@ export const WithTopRightActions: Story = {
|
||||
>
|
||||
Something went wrong while extracting the archive.
|
||||
<template #top-right-actions>
|
||||
<ButtonStyled color="red">
|
||||
<button type="button">Retry</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" native-type="button">Retry</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
<Admonition type="success" header="Extraction complete" :dismissible="true">
|
||||
@@ -115,7 +111,7 @@ export const WithTopRightActions: Story = {
|
||||
|
||||
export const WithProgressBar: Story = {
|
||||
render: () => ({
|
||||
components: { Admonition, ButtonStyled },
|
||||
components: { Admonition, Button },
|
||||
template: /*html*/ `
|
||||
<div style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<Admonition
|
||||
@@ -127,9 +123,7 @@ export const WithProgressBar: Story = {
|
||||
>
|
||||
128 KB / 1.2 MB (45%)
|
||||
<template #top-right-actions>
|
||||
<ButtonStyled type="outlined" color="blue">
|
||||
<button class="!border" type="button">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" class="!border !text-blue [&>svg]:!text-blue !shadow-[inset_0_0_0_1px_var(--color-blue)]" native-type="button">Cancel</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
<Admonition
|
||||
@@ -141,9 +135,7 @@ export const WithProgressBar: Story = {
|
||||
>
|
||||
24 MB extracted — config/settings.yml
|
||||
<template #top-right-actions>
|
||||
<ButtonStyled type="outlined" color="blue">
|
||||
<button class="!border" type="button">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" class="!border !text-blue [&>svg]:!text-blue !shadow-[inset_0_0_0_1px_var(--color-blue)]" native-type="button">Cancel</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
<Admonition
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from '../../components/base/Button.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Button',
|
||||
component: Button,
|
||||
render: (args) => ({
|
||||
components: { Button },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<Button v-bind="args">Click me</Button>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof Button>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
color: 'primary',
|
||||
},
|
||||
}
|
||||
|
||||
export const Danger: Story = {
|
||||
args: {
|
||||
color: 'danger',
|
||||
},
|
||||
}
|
||||
|
||||
export const AllColors: Story = {
|
||||
render: () => ({
|
||||
components: { Button },
|
||||
template: /*html*/ `
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 1rem;">
|
||||
<Button color="default">Default</Button>
|
||||
<Button color="primary">Primary</Button>
|
||||
<Button color="danger">Danger</Button>
|
||||
<Button color="red">Red</Button>
|
||||
<Button color="orange">Orange</Button>
|
||||
<Button color="green">Green</Button>
|
||||
<Button color="blue">Blue</Button>
|
||||
<Button color="purple">Purple</Button>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Large: Story = {
|
||||
args: {
|
||||
large: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const Outline: Story = {
|
||||
args: {
|
||||
outline: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const Transparent: Story = {
|
||||
args: {
|
||||
transparent: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const AsLink: Story = {
|
||||
args: {
|
||||
link: 'https://modrinth.com',
|
||||
external: true,
|
||||
},
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
import { DownloadIcon, HeartIcon, SettingsIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
|
||||
const colors = ['standard', 'brand', 'red', 'orange', 'green', 'blue', 'purple'] as const
|
||||
const types = [
|
||||
'standard',
|
||||
'outlined',
|
||||
'transparent',
|
||||
'highlight',
|
||||
'highlight-colored-text',
|
||||
'chip',
|
||||
] as const
|
||||
const sizes = ['small', 'standard', 'large'] as const
|
||||
|
||||
const meta = {
|
||||
title: 'Base/ButtonStyled',
|
||||
component: ButtonStyled,
|
||||
argTypes: {
|
||||
color: {
|
||||
control: 'select',
|
||||
options: [...colors, 'medal-promo'],
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: [...sizes],
|
||||
},
|
||||
type: {
|
||||
control: 'select',
|
||||
options: [...types],
|
||||
},
|
||||
circular: { control: 'boolean' },
|
||||
colorFill: {
|
||||
control: 'select',
|
||||
options: ['auto', 'background', 'text', 'none'],
|
||||
},
|
||||
hoverColorFill: {
|
||||
control: 'select',
|
||||
options: ['auto', 'background', 'text', 'none'],
|
||||
},
|
||||
highlighted: { control: 'boolean' },
|
||||
highlightedStyle: {
|
||||
control: 'select',
|
||||
options: ['main-nav-primary', 'main-nav-secondary'],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
color: 'standard',
|
||||
size: 'standard',
|
||||
type: 'standard',
|
||||
circular: false,
|
||||
colorFill: 'auto',
|
||||
hoverColorFill: 'auto',
|
||||
highlighted: false,
|
||||
highlightedStyle: 'main-nav-primary',
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { ButtonStyled, DownloadIcon },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<ButtonStyled v-bind="args">
|
||||
<button><DownloadIcon /> Button</button>
|
||||
</ButtonStyled>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof ButtonStyled>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
type: 'standard',
|
||||
},
|
||||
}
|
||||
|
||||
export const AllVariants: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled },
|
||||
setup() {
|
||||
return { colors, types }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div style="overflow-x: auto;">
|
||||
<table style="border-collapse: collapse; width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding: 0.5rem 1rem; text-align: left; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">Color / Type</th>
|
||||
<th v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">{{ type }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="color in colors" :key="color" style="border-top: 1px solid var(--color-surface-5);">
|
||||
<td style="padding: 0.5rem 1rem; font-weight: 600; font-size: 0.875rem;">{{ color }}</td>
|
||||
<td v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center;">
|
||||
<ButtonStyled :color="color" :type="type">
|
||||
<button>Button</button>
|
||||
</ButtonStyled>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const AllVariantsHighlighted: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled },
|
||||
setup() {
|
||||
return { colors, types }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div style="overflow-x: auto;">
|
||||
<table style="border-collapse: collapse; width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding: 0.5rem 1rem; text-align: left; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">Color / Type</th>
|
||||
<th v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">{{ type }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="color in colors" :key="color" style="border-top: 1px solid var(--color-surface-5);">
|
||||
<td style="padding: 0.5rem 1rem; font-weight: 600; font-size: 0.875rem;">{{ color }}</td>
|
||||
<td v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center;">
|
||||
<ButtonStyled :color="color" :type="type" highlighted>
|
||||
<button>Button</button>
|
||||
</ButtonStyled>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Sizes: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled },
|
||||
setup() {
|
||||
return { sizes, types }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div style="overflow-x: auto;">
|
||||
<table style="border-collapse: collapse; width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding: 0.5rem 1rem; text-align: left; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">Size / Type</th>
|
||||
<th v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">{{ type }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="size in sizes" :key="size" style="border-top: 1px solid var(--color-surface-5);">
|
||||
<td style="padding: 0.5rem 1rem; font-weight: 600; font-size: 0.875rem;">{{ size }}</td>
|
||||
<td v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center;">
|
||||
<ButtonStyled :size="size" :type="type" color="brand">
|
||||
<button>Button</button>
|
||||
</ButtonStyled>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const WithIcons: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, DownloadIcon, HeartIcon, SettingsIcon },
|
||||
setup() {
|
||||
return { types }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div style="overflow-x: auto;">
|
||||
<table style="border-collapse: collapse; width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding: 0.5rem 1rem; text-align: left; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">Variant</th>
|
||||
<th v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center; font-size: 0.75rem; text-transform: uppercase; color: var(--color-secondary);">{{ type }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr style="border-top: 1px solid var(--color-surface-5);">
|
||||
<td style="padding: 0.5rem 1rem; font-weight: 600; font-size: 0.875rem;">Icon + text</td>
|
||||
<td v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center;">
|
||||
<ButtonStyled :type="type" color="brand">
|
||||
<button><DownloadIcon /> Download</button>
|
||||
</ButtonStyled>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="border-top: 1px solid var(--color-surface-5);">
|
||||
<td style="padding: 0.5rem 1rem; font-weight: 600; font-size: 0.875rem;">Icon only</td>
|
||||
<td v-for="type in types" :key="type" style="padding: 0.5rem 1rem; text-align: center;">
|
||||
<ButtonStyled :type="type" color="brand" circular>
|
||||
<button><HeartIcon /></button>
|
||||
</ButtonStyled>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Disabled: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled },
|
||||
setup() {
|
||||
return { types }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 1rem; align-items: center;">
|
||||
<ButtonStyled v-for="type in types" :key="type" :type="type" color="brand">
|
||||
<button disabled>{{ type }}</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import EmptyState from '../../components/base/EmptyState.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -42,7 +42,7 @@ export const Default: Story = {
|
||||
|
||||
export const WithActions: StoryObj = {
|
||||
render: () => ({
|
||||
components: { EmptyState, ButtonStyled },
|
||||
components: { EmptyState, Button },
|
||||
template: /*html*/ `
|
||||
<EmptyState
|
||||
type="empty-inbox"
|
||||
@@ -50,9 +50,7 @@ export const WithActions: StoryObj = {
|
||||
description="Create your first backup"
|
||||
>
|
||||
<template #actions>
|
||||
<ButtonStyled color="brand">
|
||||
<button>Create backup</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand">Create backup</Button>
|
||||
</template>
|
||||
</EmptyState>
|
||||
`,
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
import { PlayIcon, SlashIcon, StopCircleIcon, UpdatedIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import JoinedButtons from '../../components/base/JoinedButtons.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/JoinedButtons',
|
||||
component: JoinedButtons,
|
||||
argTypes: {
|
||||
color: {
|
||||
control: 'select',
|
||||
options: ['standard', 'brand', 'red', 'orange', 'green', 'blue', 'purple'],
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['small', 'standard', 'large'],
|
||||
},
|
||||
disabled: { control: 'boolean' },
|
||||
primaryDisabled: { control: 'boolean' },
|
||||
dropdownDisabled: { control: 'boolean' },
|
||||
primaryMuted: { control: 'boolean' },
|
||||
},
|
||||
} satisfies Meta<typeof JoinedButtons>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Start: Story = {
|
||||
args: {
|
||||
color: 'brand',
|
||||
size: 'large',
|
||||
actions: [
|
||||
{
|
||||
id: 'start',
|
||||
label: 'Start',
|
||||
icon: PlayIcon,
|
||||
action: () => console.log('Start'),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const StopWithKill: Story = {
|
||||
args: {
|
||||
color: 'red',
|
||||
size: 'large',
|
||||
actions: [
|
||||
{
|
||||
id: 'stop',
|
||||
label: 'Stop',
|
||||
icon: StopCircleIcon,
|
||||
action: () => console.log('Stop'),
|
||||
},
|
||||
{
|
||||
id: 'kill_server',
|
||||
label: 'Kill server',
|
||||
icon: SlashIcon,
|
||||
action: () => console.log('Kill'),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const Stopping: Story = {
|
||||
args: {
|
||||
color: 'red',
|
||||
size: 'large',
|
||||
primaryDisabled: true,
|
||||
primaryMuted: true,
|
||||
actions: [
|
||||
{
|
||||
id: 'stop',
|
||||
label: 'Stopping',
|
||||
icon: StopCircleIcon,
|
||||
action: () => console.log('Stop'),
|
||||
},
|
||||
{
|
||||
id: 'kill_server',
|
||||
label: 'Kill server',
|
||||
icon: SlashIcon,
|
||||
action: () => console.log('Kill'),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const Restart: Story = {
|
||||
args: {
|
||||
color: 'orange',
|
||||
size: 'large',
|
||||
actions: [
|
||||
{
|
||||
id: 'restart',
|
||||
label: 'Restart',
|
||||
icon: UpdatedIcon,
|
||||
action: () => console.log('Restart'),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
color: 'red',
|
||||
size: 'large',
|
||||
disabled: true,
|
||||
actions: [
|
||||
{
|
||||
id: 'stop',
|
||||
label: 'Stop',
|
||||
icon: StopCircleIcon,
|
||||
action: () => console.log('Stop'),
|
||||
},
|
||||
{
|
||||
id: 'kill_server',
|
||||
label: 'Kill server',
|
||||
icon: SlashIcon,
|
||||
action: () => console.log('Kill'),
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import { MoreHorizontalIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import OverflowMenu from '../../components/base/OverflowMenu.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/OverflowMenu',
|
||||
component: OverflowMenu,
|
||||
render: (args) => ({
|
||||
components: { OverflowMenu, MoreHorizontalIcon, ButtonStyled },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu v-bind="args">
|
||||
<MoreHorizontalIcon class="h-5 w-5" />
|
||||
<template #edit>Edit</template>
|
||||
<template #delete>Delete</template>
|
||||
<template #share>Share</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof OverflowMenu>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
options: [
|
||||
{ id: 'edit', action: () => console.log('Edit clicked') },
|
||||
{ id: 'share', action: () => console.log('Share clicked') },
|
||||
{ divider: true },
|
||||
{ id: 'delete', action: () => console.log('Delete clicked'), color: 'danger' },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const WithDifferentPlacements: StoryObj = {
|
||||
render: () => ({
|
||||
components: { OverflowMenu, MoreHorizontalIcon, ButtonStyled },
|
||||
template: /*html*/ `
|
||||
<div class="flex gap-8 items-start">
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<span class="text-sm text-secondary">bottom-end (default)</span>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
:options="[
|
||||
{ id: 'edit', action: () => {} },
|
||||
{ id: 'delete', action: () => {}, color: 'danger' },
|
||||
]"
|
||||
>
|
||||
<MoreHorizontalIcon class="h-5 w-5" />
|
||||
<template #edit>Edit</template>
|
||||
<template #delete>Delete</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<span class="text-sm text-secondary">bottom-start</span>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
direction="left"
|
||||
:options="[
|
||||
{ id: 'edit', action: () => {} },
|
||||
{ id: 'delete', action: () => {}, color: 'danger' },
|
||||
]"
|
||||
>
|
||||
<MoreHorizontalIcon class="h-5 w-5" />
|
||||
<template #edit>Edit</template>
|
||||
<template #delete>Delete</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -20,9 +20,13 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import AutoLink from '../../components/base/AutoLink.vue'
|
||||
import Avatar from '../../components/base/Avatar.vue'
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
SplitButton,
|
||||
TeleportOverflowMenu,
|
||||
} from '../../components/base/buttons'
|
||||
import FormattedTag from '../../components/base/FormattedTag.vue'
|
||||
import JoinedButtons from '../../components/base/JoinedButtons.vue'
|
||||
import PageHeader from '../../components/base/page-header/index.vue'
|
||||
import PageHeaderMetadata from '../../components/base/page-header/metadata/index.vue'
|
||||
import PageHeaderMetadataItem from '../../components/base/page-header/metadata/page-header-metadata-item.vue'
|
||||
@@ -32,7 +36,6 @@ import PageHeaderMetadataTimeItem from '../../components/base/page-header/metada
|
||||
import PageHeaderActions from '../../components/base/page-header/page-header-actions.vue'
|
||||
import PageHeaderBadgeItem from '../../components/base/page-header/page-header-badge-item.vue'
|
||||
import TagItem from '../../components/base/TagItem.vue'
|
||||
import TeleportOverflowMenu from '../../components/base/TeleportOverflowMenu.vue'
|
||||
import LoaderIcon from '../../components/servers/icons/LoaderIcon.vue'
|
||||
import ServerIcon from '../../components/servers/icons/ServerIcon.vue'
|
||||
|
||||
@@ -87,9 +90,10 @@ const pageHeaderIcons = {
|
||||
const pageHeaderComponents = {
|
||||
AutoLink,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
Button,
|
||||
FormattedTag,
|
||||
JoinedButtons,
|
||||
IconButton,
|
||||
SplitButton,
|
||||
PageHeader,
|
||||
PageHeaderActions,
|
||||
PageHeaderBadgeItem,
|
||||
@@ -111,7 +115,7 @@ const meta = {
|
||||
},
|
||||
decorators: [
|
||||
(story) => ({
|
||||
components: { story },
|
||||
components: { story, TeleportOverflowMenu },
|
||||
template: '<div class="w-full"><story /></div>',
|
||||
}),
|
||||
],
|
||||
@@ -157,17 +161,13 @@ export const ProjectHeader: Story = {
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled color="brand" size="large">
|
||||
<button type="button" @click="noop">
|
||||
<DownloadIcon />
|
||||
Download
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu :options="menuActions" aria-label="More actions">
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" size="xl" native-type="button" @click="noop">
|
||||
<DownloadIcon />
|
||||
Download
|
||||
</Button>
|
||||
<TeleportOverflowMenu type="quiet" size="xl" label="More actions" :options="menuActions">
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
@@ -207,12 +207,10 @@ export const CreatorHeader: Story = {
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled color="brand" size="large">
|
||||
<button type="button" @click="noop">
|
||||
<HeartIcon />
|
||||
Follow
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" size="xl" native-type="button" @click="noop">
|
||||
<HeartIcon />
|
||||
Follow
|
||||
</Button>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
@@ -222,10 +220,7 @@ export const CreatorHeader: Story = {
|
||||
|
||||
export const AppInstanceHeader: Story = {
|
||||
render: () => ({
|
||||
components: {
|
||||
...pageHeaderComponents,
|
||||
LoaderIcon,
|
||||
},
|
||||
components: { ...pageHeaderComponents, LoaderIcon, TeleportOverflowMenu },
|
||||
setup() {
|
||||
return {
|
||||
...pageHeaderIcons,
|
||||
@@ -252,22 +247,16 @@ export const AppInstanceHeader: Story = {
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled color="brand" size="large">
|
||||
<button type="button" @click="noop">
|
||||
<PlayIcon />
|
||||
Play
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large">
|
||||
<button type="button" aria-label="Instance settings" @click="noop">
|
||||
<SettingsIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu :options="menuActions" aria-label="More actions">
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" size="xl" native-type="button" @click="noop">
|
||||
<PlayIcon />
|
||||
Play
|
||||
</Button>
|
||||
<IconButton size="xl" label="Instance settings" native-type="button" @click="noop">
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
<TeleportOverflowMenu type="quiet" size="xl" label="More actions" :options="menuActions">
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
@@ -277,10 +266,7 @@ export const AppInstanceHeader: Story = {
|
||||
|
||||
export const BrowseHeader: Story = {
|
||||
render: () => ({
|
||||
components: {
|
||||
...pageHeaderComponents,
|
||||
LoaderIcon,
|
||||
},
|
||||
components: { ...pageHeaderComponents, LoaderIcon, TeleportOverflowMenu },
|
||||
setup() {
|
||||
return {
|
||||
...pageHeaderIcons,
|
||||
@@ -291,11 +277,9 @@ export const BrowseHeader: Story = {
|
||||
template: `
|
||||
<PageHeader title="Survival SMP" :divider="false" :bottom-padding="false" main-class="items-center" title-class="leading-8" truncate-title>
|
||||
<template #leading>
|
||||
<ButtonStyled circular size="large">
|
||||
<button type="button" aria-label="Back to instance" @click="noop">
|
||||
<LeftArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton size="xl" label="Back to instance" native-type="button" @click="noop">
|
||||
<LeftArrowIcon />
|
||||
</IconButton>
|
||||
<Avatar src="" alt="Survival SMP" size="48px" tint-by="survival-smp" />
|
||||
</template>
|
||||
|
||||
@@ -317,10 +301,7 @@ export const BrowseHeader: Story = {
|
||||
|
||||
export const ServerPanelRootHeader: Story = {
|
||||
render: () => ({
|
||||
components: {
|
||||
...pageHeaderComponents,
|
||||
ServerIcon,
|
||||
},
|
||||
components: { ...pageHeaderComponents, ServerIcon, TeleportOverflowMenu },
|
||||
setup() {
|
||||
return {
|
||||
...pageHeaderIcons,
|
||||
@@ -346,17 +327,13 @@ export const ServerPanelRootHeader: Story = {
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled color="brand" size="large">
|
||||
<button type="button" @click="noop">
|
||||
<PlayIcon />
|
||||
Start server
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large">
|
||||
<button type="button" aria-label="Server settings" @click="noop">
|
||||
<SettingsIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" size="xl" native-type="button" @click="noop">
|
||||
<PlayIcon />
|
||||
Start server
|
||||
</Button>
|
||||
<IconButton size="xl" label="Server settings" native-type="button" @click="noop">
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
@@ -366,10 +343,7 @@ export const ServerPanelRootHeader: Story = {
|
||||
|
||||
export const ServerPanelInstanceHeader: Story = {
|
||||
render: () => ({
|
||||
components: {
|
||||
...pageHeaderComponents,
|
||||
LoaderIcon,
|
||||
},
|
||||
components: { ...pageHeaderComponents, LoaderIcon, TeleportOverflowMenu },
|
||||
setup() {
|
||||
return {
|
||||
...pageHeaderIcons,
|
||||
@@ -381,11 +355,9 @@ export const ServerPanelInstanceHeader: Story = {
|
||||
template: `
|
||||
<PageHeader title="My World">
|
||||
<template #leading>
|
||||
<ButtonStyled circular size="large">
|
||||
<button type="button" aria-label="All instances" @click="noop">
|
||||
<LeftArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton size="xl" label="All instances" native-type="button" @click="noop">
|
||||
<LeftArrowIcon />
|
||||
</IconButton>
|
||||
</template>
|
||||
|
||||
<template #metadata>
|
||||
@@ -400,18 +372,24 @@ export const ServerPanelInstanceHeader: Story = {
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled color="brand" size="large">
|
||||
<button type="button" @click="noop">
|
||||
<PlayIcon />
|
||||
Start instance
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<JoinedButtons :actions="joinedActions" color="red" size="large" />
|
||||
<ButtonStyled circular size="large">
|
||||
<button type="button" aria-label="Instance settings" @click="noop">
|
||||
<SettingsIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" size="xl" native-type="button" @click="noop">
|
||||
<PlayIcon />
|
||||
Start instance
|
||||
</Button>
|
||||
<SplitButton
|
||||
type="colored"
|
||||
color="red"
|
||||
size="xl"
|
||||
:options="joinedActions.slice(1)"
|
||||
menu-label="Force stop options"
|
||||
@click="noop"
|
||||
>
|
||||
<StopCircleIcon />
|
||||
Stop
|
||||
</SplitButton>
|
||||
<IconButton size="xl" label="Instance settings" native-type="button" @click="noop">
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import { SettingsIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from '../../components/base/Button.vue'
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import PopoutMenu from '../../components/base/PopoutMenu.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/PopoutMenu',
|
||||
component: PopoutMenu,
|
||||
render: (args) => ({
|
||||
components: { PopoutMenu, Button, ButtonStyled, SettingsIcon },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<ButtonStyled circular type="transparent">
|
||||
<PopoutMenu v-bind="args">
|
||||
<SettingsIcon class="h-5 w-5" />
|
||||
<template #menu>
|
||||
<div class="flex flex-col gap-1 p-1">
|
||||
<Button transparent>Option 1</Button>
|
||||
<Button transparent>Option 2</Button>
|
||||
<Button transparent>Option 3</Button>
|
||||
</div>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
</ButtonStyled>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof PopoutMenu>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const WithTooltip: Story = {
|
||||
args: {
|
||||
tooltip: 'Click for more options',
|
||||
},
|
||||
}
|
||||
|
||||
export const DifferentPlacements: StoryObj = {
|
||||
render: () => ({
|
||||
components: { PopoutMenu, Button, ButtonStyled, SettingsIcon },
|
||||
template: /*html*/ `
|
||||
<div class="flex gap-8 items-start p-8">
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<span class="text-sm text-secondary">bottom-end (default)</span>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<PopoutMenu placement="bottom-end">
|
||||
<SettingsIcon class="h-5 w-5" />
|
||||
<template #menu>
|
||||
<div class="flex flex-col gap-1 p-1">
|
||||
<Button transparent>Option 1</Button>
|
||||
<Button transparent>Option 2</Button>
|
||||
</div>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<span class="text-sm text-secondary">bottom-start</span>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<PopoutMenu placement="bottom-start">
|
||||
<SettingsIcon class="h-5 w-5" />
|
||||
<template #menu>
|
||||
<div class="flex flex-col gap-1 p-1">
|
||||
<Button transparent>Option 1</Button>
|
||||
<Button transparent>Option 2</Button>
|
||||
</div>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-2">
|
||||
<span class="text-sm text-secondary">top-end</span>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<PopoutMenu placement="top-end">
|
||||
<SettingsIcon class="h-5 w-5" />
|
||||
<template #menu>
|
||||
<div class="flex flex-col gap-1 p-1">
|
||||
<Button transparent>Option 1</Button>
|
||||
<Button transparent>Option 2</Button>
|
||||
</div>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import Admonition from '../../components/base/Admonition.vue'
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import StackedAdmonitionsRaw, {
|
||||
type StackedAdmonitionItem,
|
||||
} from '../../components/base/StackedAdmonitions.vue'
|
||||
@@ -383,7 +383,7 @@ interface RichItem extends StackedAdmonitionItem {
|
||||
|
||||
export const RichContent: Story = {
|
||||
render: () => ({
|
||||
components: { StackedAdmonitions, Admonition, ButtonStyled },
|
||||
components: { StackedAdmonitions, Admonition, Button },
|
||||
setup() {
|
||||
const items = ref<RichItem[]>([
|
||||
{
|
||||
@@ -429,12 +429,8 @@ export const RichContent: Story = {
|
||||
>
|
||||
{{ item.body }}
|
||||
<template #top-right-actions>
|
||||
<ButtonStyled v-if="item.canCancel" type="outlined" color="blue">
|
||||
<button class="!border" type="button" @click="dismiss(item.id)">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="item.canRetry" color="red">
|
||||
<button type="button" @click="dismiss(item.id)">Retry</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" v-if="item.canCancel" class="!border !text-blue [&>svg]:!text-blue !shadow-[inset_0_0_0_1px_var(--color-blue)]" native-type="button" @click="dismiss(item.id)">Cancel</Button>
|
||||
<Button type="colored" color="red" v-if="item.canRetry" native-type="button" @click="dismiss(item.id)">Retry</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
</template>
|
||||
|
||||
@@ -3,8 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import Badge from '../../components/base/Badge.vue'
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import OverflowMenu from '../../components/base/OverflowMenu.vue'
|
||||
import { Button, TeleportOverflowMenu } from '../../components/base/buttons'
|
||||
import Table from '../../components/base/Table.vue'
|
||||
|
||||
interface User {
|
||||
@@ -54,7 +53,7 @@ export default meta
|
||||
export const Default: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -74,7 +73,7 @@ export const Default: StoryObj = {
|
||||
export const HorizontalOverflow: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -103,7 +102,7 @@ export const HorizontalOverflow: StoryObj = {
|
||||
export const CustomClasses: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name', cellClass: '!overflow-visible py-3' },
|
||||
@@ -129,7 +128,7 @@ export const CustomClasses: StoryObj = {
|
||||
export const WithSelection: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -160,7 +159,7 @@ export const WithSelection: StoryObj = {
|
||||
export const WithSelectionData: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -193,7 +192,7 @@ export const WithSelectionData: StoryObj = {
|
||||
export const WithSelectionIds: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -233,7 +232,7 @@ export const WithSorting: StoryObj = {
|
||||
},
|
||||
},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name', enableSorting: true },
|
||||
@@ -269,7 +268,7 @@ export const WithSorting: StoryObj = {
|
||||
export const WithColumnAlignment: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name', align: 'left' as const },
|
||||
@@ -289,7 +288,7 @@ export const WithColumnAlignment: StoryObj = {
|
||||
export const WithCustomCellSlots: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, Badge },
|
||||
components: { Table, Badge, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -335,7 +334,7 @@ export const WithCustomCellSlots: StoryObj = {
|
||||
export const WithCustomHeaderSlots: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -365,7 +364,7 @@ export const WithCustomHeaderSlots: StoryObj = {
|
||||
export const WithHeaderSlot: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, ButtonStyled },
|
||||
components: { Table, Button, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -383,9 +382,7 @@ export const WithHeaderSlot: StoryObj = {
|
||||
<div class="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
|
||||
<div class="text-lg font-semibold text-contrast">Team Members</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<ButtonStyled color="brand">
|
||||
<button type="button">Invite member</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" native-type="button">Invite member</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -397,7 +394,7 @@ export const WithHeaderSlot: StoryObj = {
|
||||
export const WithActionsColumn: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, ButtonStyled, EditIcon, TrashIcon },
|
||||
components: { Table, EditIcon, TrashIcon, Button, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -421,18 +418,14 @@ export const WithActionsColumn: StoryObj = {
|
||||
<Table :columns="columns" :data="data">
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<ButtonStyled color="brand" type="transparent" @click="handleEdit(row)">
|
||||
<button class="flex items-center gap-1">
|
||||
<EditIcon class="size-4" />
|
||||
Edit
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red" type="transparent" @click="handleDelete(row)">
|
||||
<button class="flex items-center gap-1">
|
||||
<TrashIcon class="size-4" />
|
||||
Delete
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="brand" @click="handleEdit(row)" class="flex items-center gap-1">
|
||||
<EditIcon class="size-4" />
|
||||
Edit
|
||||
</Button>
|
||||
<Button type="quiet" color="red" @click="handleDelete(row)" class="flex items-center gap-1">
|
||||
<TrashIcon class="size-4" />
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -443,7 +436,7 @@ export const WithActionsColumn: StoryObj = {
|
||||
export const WithLocalizedActionsColumn: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, ButtonStyled, EditIcon, TrashIcon },
|
||||
components: { Table, EditIcon, TrashIcon, Button, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Nombre' },
|
||||
@@ -467,18 +460,14 @@ export const WithLocalizedActionsColumn: StoryObj = {
|
||||
<Table :columns="columns" :data="data">
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<ButtonStyled color="brand" type="transparent" @click="handleEdit(row)">
|
||||
<button class="flex items-center gap-1">
|
||||
<EditIcon class="size-4" />
|
||||
Editar
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red" type="transparent" @click="handleDelete(row)">
|
||||
<button class="flex items-center gap-1">
|
||||
<TrashIcon class="size-4" />
|
||||
Eliminar
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="brand" @click="handleEdit(row)" class="flex items-center gap-1">
|
||||
<EditIcon class="size-4" />
|
||||
Editar
|
||||
</Button>
|
||||
<Button type="quiet" color="red" @click="handleDelete(row)" class="flex items-center gap-1">
|
||||
<TrashIcon class="size-4" />
|
||||
Eliminar
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -489,7 +478,7 @@ export const WithLocalizedActionsColumn: StoryObj = {
|
||||
export const FullFeatured: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, Badge, ButtonStyled, EditIcon, TrashIcon },
|
||||
components: { Table, Badge, EditIcon, TrashIcon, Button, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name', enableSorting: true },
|
||||
@@ -565,18 +554,14 @@ export const FullFeatured: StoryObj = {
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<ButtonStyled color="brand" type="transparent" @click="handleEdit(row)">
|
||||
<button class="flex items-center gap-1">
|
||||
<EditIcon class="size-4" />
|
||||
Edit
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red" type="transparent" @click="handleDelete(row)">
|
||||
<button class="flex items-center gap-1">
|
||||
<TrashIcon class="size-4" />
|
||||
Delete
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="brand" @click="handleEdit(row)" class="flex items-center gap-1">
|
||||
<EditIcon class="size-4" />
|
||||
Edit
|
||||
</Button>
|
||||
<Button type="quiet" color="red" @click="handleDelete(row)" class="flex items-center gap-1">
|
||||
<TrashIcon class="size-4" />
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -592,7 +577,7 @@ export const FullFeatured: StoryObj = {
|
||||
export const VirtualizedLargeData: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, Badge },
|
||||
components: { Table, Badge, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name', enableSorting: true },
|
||||
@@ -710,7 +695,15 @@ export const VirtualizedLargeData: StoryObj = {
|
||||
export const WithOverflowMenu: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table, Badge, ButtonStyled, OverflowMenu, MoreVerticalIcon, EditIcon, TrashIcon },
|
||||
components: {
|
||||
Table,
|
||||
Badge,
|
||||
MoreVerticalIcon,
|
||||
EditIcon,
|
||||
TrashIcon,
|
||||
Button,
|
||||
TeleportOverflowMenu,
|
||||
},
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
@@ -737,17 +730,19 @@ export const WithOverflowMenu: StoryObj = {
|
||||
const getMenuOptions = (row: User) => [
|
||||
{
|
||||
id: 'edit',
|
||||
label: 'Edit',
|
||||
action: () => alert(`Edit user: ${row.name}`),
|
||||
},
|
||||
{
|
||||
id: 'duplicate',
|
||||
label: 'Duplicate',
|
||||
action: () => alert(`Duplicate user: ${row.name}`),
|
||||
},
|
||||
{ divider: true },
|
||||
{ type: 'divider' },
|
||||
{
|
||||
id: 'delete',
|
||||
color: 'red' as const,
|
||||
hoverFilled: true,
|
||||
label: 'Delete',
|
||||
tone: 'red',
|
||||
action: () => alert(`Delete user: ${row.name}`),
|
||||
},
|
||||
]
|
||||
@@ -766,26 +761,23 @@ export const WithOverflowMenu: StoryObj = {
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex justify-end">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
<TeleportOverflowMenu type="quiet" label="More options"
|
||||
:options="getMenuOptions(row)"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #edit>
|
||||
<EditIcon class="size-4" aria-hidden="true" />
|
||||
Edit
|
||||
</template>
|
||||
<template #duplicate>
|
||||
<EditIcon class="size-4" aria-hidden="true" />
|
||||
Duplicate
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon class="size-4" aria-hidden="true" />
|
||||
Delete
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #edit>
|
||||
<EditIcon class="size-4" aria-hidden="true" />
|
||||
Edit
|
||||
</template>
|
||||
<template #duplicate>
|
||||
<EditIcon class="size-4" aria-hidden="true" />
|
||||
Duplicate
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon class="size-4" aria-hidden="true" />
|
||||
Delete
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -796,7 +788,7 @@ export const WithOverflowMenu: StoryObj = {
|
||||
export const EmptyState: StoryObj = {
|
||||
args: {},
|
||||
render: () => ({
|
||||
components: { Table },
|
||||
components: { Table, TeleportOverflowMenu },
|
||||
setup() {
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Name' },
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
import { DownloadIcon, ExternalIcon, HeartIcon, SettingsIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from '../../components/base/buttons/Button.vue'
|
||||
import ButtonLink from '../../components/base/buttons/ButtonLink.vue'
|
||||
import IconButton from '../../components/base/buttons/IconButton.vue'
|
||||
|
||||
const types = ['base', 'colored', 'outlined', 'quiet'] as const
|
||||
const sizes = ['sm', 'md', 'lg', 'xl'] as const
|
||||
const colors = ['brand', 'red', 'orange', 'green', 'blue', 'purple', 'medal_promotion'] as const
|
||||
const sizeColumns = [
|
||||
{ value: 'sm', label: 'Small' },
|
||||
{ value: 'md', label: 'Medium' },
|
||||
{ value: 'lg', label: 'Large' },
|
||||
{ value: 'xl', label: 'Extra large' },
|
||||
] as const
|
||||
const typeRows = [
|
||||
{ label: 'Base', type: 'base' },
|
||||
{ label: 'Outlined', type: 'outlined' },
|
||||
{ label: 'Quiet', type: 'quiet' },
|
||||
...colors.map((color) => ({
|
||||
label: `Colored / ${color.charAt(0).toUpperCase()}${color.slice(1)}`,
|
||||
type: 'colored' as const,
|
||||
color,
|
||||
})),
|
||||
...colors.map((color) => ({
|
||||
label: `Outlined / ${color.charAt(0).toUpperCase()}${color.slice(1)}`,
|
||||
type: 'outlined' as const,
|
||||
color,
|
||||
})),
|
||||
...colors.map((color) => ({
|
||||
label: `Quiet / ${color.charAt(0).toUpperCase()}${color.slice(1)}`,
|
||||
type: 'quiet' as const,
|
||||
color,
|
||||
})),
|
||||
]
|
||||
|
||||
const meta = {
|
||||
title: 'Buttons/Button',
|
||||
component: Button,
|
||||
argTypes: {
|
||||
type: {
|
||||
control: 'select',
|
||||
options: types,
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: sizes,
|
||||
},
|
||||
color: {
|
||||
control: 'select',
|
||||
options: colors,
|
||||
},
|
||||
nativeType: {
|
||||
control: 'select',
|
||||
options: ['button', 'submit', 'reset'],
|
||||
},
|
||||
disabled: { control: 'boolean' },
|
||||
loading: { control: 'boolean' },
|
||||
},
|
||||
args: {
|
||||
type: 'base',
|
||||
size: 'md',
|
||||
color: 'brand',
|
||||
nativeType: 'button',
|
||||
disabled: false,
|
||||
loading: false,
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { Button, DownloadIcon },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<Button v-bind="args">
|
||||
<DownloadIcon />
|
||||
Download
|
||||
</Button>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof Button>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Playground: Story = {}
|
||||
|
||||
export const AllTypes: Story = {
|
||||
render: () => ({
|
||||
components: { Button, DownloadIcon },
|
||||
setup() {
|
||||
return { sizeColumns, typeRows }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="grid grid-cols-[max-content_repeat(4,max-content)] items-center gap-4 overflow-x-auto p-1">
|
||||
<div />
|
||||
<div v-for="size in sizeColumns" :key="size.value" class="font-semibold text-contrast">
|
||||
{{ size.label }}
|
||||
</div>
|
||||
|
||||
<template v-for="row in typeRows" :key="row.label">
|
||||
<div class="whitespace-nowrap font-semibold text-secondary">{{ row.label }}</div>
|
||||
<Button
|
||||
v-for="size in sizeColumns"
|
||||
:key="size.value"
|
||||
:type="row.type"
|
||||
:color="row.color"
|
||||
:size="size.value"
|
||||
>
|
||||
<DownloadIcon />Button
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Quiet: Story = {
|
||||
render: () => ({
|
||||
components: { Button, DownloadIcon, IconButton, SettingsIcon },
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<Button type="quiet"><DownloadIcon />Quiet</Button>
|
||||
<Button type="quiet" color="red"><DownloadIcon />Quiet destructive</Button>
|
||||
<IconButton label="Settings" type="quiet"><SettingsIcon /></IconButton>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Sizes: Story = {
|
||||
render: () => ({
|
||||
components: { Button, DownloadIcon, IconButton },
|
||||
setup() {
|
||||
return { sizes }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<template v-for="size in sizes" :key="size">
|
||||
<Button :size="size"><DownloadIcon />{{ size }}</Button>
|
||||
<IconButton :label="size" :size="size"><DownloadIcon /></IconButton>
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Colors: Story = {
|
||||
render: () => ({
|
||||
components: { Button },
|
||||
setup() {
|
||||
return { colors }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<Button v-for="color in colors" :key="color" type="colored" :color="color">
|
||||
{{ color }}
|
||||
</Button>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Content: Story = {
|
||||
render: () => ({
|
||||
components: { Button, DownloadIcon, SettingsIcon },
|
||||
template: /*html*/ `
|
||||
<div class="flex max-w-2xl flex-col items-start gap-4">
|
||||
<Button>Text only</Button>
|
||||
<Button><DownloadIcon />Leading icon</Button>
|
||||
<Button>Trailing icon<SettingsIcon /></Button>
|
||||
<Button class="w-full">Full width</Button>
|
||||
<Button>Continue with a deliberately long translated action label</Button>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const InteractionStates: Story = {
|
||||
render: () => ({
|
||||
components: { Button },
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<Button>Enabled</Button>
|
||||
<Button disabled>Disabled</Button>
|
||||
<Button loading>Loading</Button>
|
||||
<Button type="colored">Colored</Button>
|
||||
<Button type="colored" disabled>Colored disabled</Button>
|
||||
<Button type="outlined">Outlined</Button>
|
||||
<Button type="quiet">Quiet</Button>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const LinksAndIconButton: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonLink, ExternalIcon, HeartIcon, IconButton },
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<ButtonLink to="/library">Internal link</ButtonLink>
|
||||
<ButtonLink href="https://modrinth.com" target="_blank" type="outlined">
|
||||
Modrinth<ExternalIcon />
|
||||
</ButtonLink>
|
||||
<ButtonLink href="https://modrinth.com" disabled>Disabled link</ButtonLink>
|
||||
<IconButton label="Favorite"><HeartIcon /></IconButton>
|
||||
<IconButton label="Favorite" type="colored"><HeartIcon /></IconButton>
|
||||
<IconButton label="Favorite" type="outlined"><HeartIcon /></IconButton>
|
||||
<IconButton label="Favorite" type="quiet"><HeartIcon /></IconButton>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { PlayIcon, SettingsIcon, StopCircleIcon, TrashIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from '../../components/base/buttons/Button.vue'
|
||||
import ButtonGroup from '../../components/base/buttons/ButtonGroup.vue'
|
||||
import SplitButton from '../../components/base/buttons/SplitButton.vue'
|
||||
import type { OverflowMenuOption } from '../../components/base/buttons/types'
|
||||
|
||||
const splitOptions: OverflowMenuOption[] = [
|
||||
{
|
||||
id: 'settings',
|
||||
label: 'Server settings',
|
||||
icon: SettingsIcon,
|
||||
action: () => undefined,
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
id: 'delete',
|
||||
label: 'Delete server',
|
||||
icon: TrashIcon,
|
||||
tone: 'red',
|
||||
action: () => undefined,
|
||||
},
|
||||
]
|
||||
|
||||
const meta = {
|
||||
title: 'Buttons/Button Group',
|
||||
component: ButtonGroup,
|
||||
} satisfies Meta<typeof ButtonGroup>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Joined: Story = {
|
||||
render: () => ({
|
||||
components: { Button, ButtonGroup },
|
||||
template: /*html*/ `
|
||||
<ButtonGroup label="Pagination">
|
||||
<Button type="outlined">Previous</Button>
|
||||
<Button type="outlined">Next</Button>
|
||||
</ButtonGroup>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Split: Story = {
|
||||
render: () => ({
|
||||
components: { PlayIcon, SplitButton },
|
||||
setup() {
|
||||
return { splitOptions }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<SplitButton
|
||||
menu-label="More server actions"
|
||||
group-label="Server actions"
|
||||
type="colored"
|
||||
:options="splitOptions"
|
||||
>
|
||||
<PlayIcon />Start server
|
||||
</SplitButton>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const IndependentDisabledStates: Story = {
|
||||
render: () => ({
|
||||
components: { SplitButton, StopCircleIcon },
|
||||
setup() {
|
||||
return { splitOptions }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-wrap gap-4">
|
||||
<SplitButton menu-label="More actions" :options="splitOptions" primary-disabled>
|
||||
<StopCircleIcon />Primary disabled
|
||||
</SplitButton>
|
||||
<SplitButton menu-label="More actions" :options="splitOptions" menu-disabled>
|
||||
<StopCircleIcon />Menu disabled
|
||||
</SplitButton>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { UploadIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import FileButton from '../../components/base/buttons/FileButton.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Buttons/File Button',
|
||||
component: FileButton,
|
||||
argTypes: {
|
||||
type: {
|
||||
control: 'select',
|
||||
options: ['base', 'colored', 'outlined', 'quiet'],
|
||||
},
|
||||
size: {
|
||||
control: 'select',
|
||||
options: ['sm', 'md', 'lg', 'xl'],
|
||||
},
|
||||
color: {
|
||||
control: 'select',
|
||||
options: ['brand', 'red', 'orange', 'green', 'blue', 'purple', 'medal_promotion'],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
prompt: 'Select file',
|
||||
type: 'base',
|
||||
size: 'md',
|
||||
multiple: false,
|
||||
disabled: false,
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { FileButton, UploadIcon },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<FileButton v-bind="args">
|
||||
<UploadIcon />
|
||||
</FileButton>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof FileButton>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const MultipleImages: Story = {
|
||||
args: {
|
||||
prompt: 'Select images',
|
||||
accept: 'image/*',
|
||||
multiple: true,
|
||||
type: 'colored',
|
||||
},
|
||||
}
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
disabled: true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
DownloadIcon,
|
||||
ExternalIcon,
|
||||
MoreVerticalIcon,
|
||||
SettingsIcon,
|
||||
TrashIcon,
|
||||
} from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import TeleportOverflowMenu from '../../components/base/buttons/TeleportOverflowMenu.vue'
|
||||
import type { OverflowMenuOption } from '../../components/base/buttons/types'
|
||||
|
||||
const options: OverflowMenuOption[] = [
|
||||
{
|
||||
id: 'download',
|
||||
label: 'Download',
|
||||
icon: DownloadIcon,
|
||||
action: () => undefined,
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
label: 'Project settings',
|
||||
icon: SettingsIcon,
|
||||
type: 'link',
|
||||
to: '/settings',
|
||||
},
|
||||
{
|
||||
id: 'website',
|
||||
label: 'Open website',
|
||||
icon: ExternalIcon,
|
||||
type: 'link',
|
||||
href: 'https://modrinth.com',
|
||||
target: '_blank',
|
||||
},
|
||||
{
|
||||
id: 'unavailable',
|
||||
label: 'Unavailable action',
|
||||
disabled: true,
|
||||
tooltip: 'This action is currently unavailable',
|
||||
action: () => undefined,
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
id: 'delete',
|
||||
label: 'Delete project',
|
||||
icon: TrashIcon,
|
||||
tone: 'red',
|
||||
action: () => undefined,
|
||||
},
|
||||
]
|
||||
|
||||
const meta = {
|
||||
title: 'Buttons/Teleport Overflow Menu',
|
||||
component: TeleportOverflowMenu,
|
||||
args: {
|
||||
label: 'More actions',
|
||||
options,
|
||||
type: 'base',
|
||||
size: 'md',
|
||||
placement: 'bottom-end',
|
||||
disabled: false,
|
||||
hoverable: false,
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { MoreVerticalIcon, TeleportOverflowMenu },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /*html*/ `
|
||||
<TeleportOverflowMenu v-bind="args">
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
`,
|
||||
}),
|
||||
} satisfies Meta<typeof TeleportOverflowMenu>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const ColoredTrigger: Story = {
|
||||
args: {
|
||||
type: 'colored',
|
||||
},
|
||||
}
|
||||
|
||||
export const OutlinedTrigger: Story = {
|
||||
args: {
|
||||
type: 'outlined',
|
||||
},
|
||||
}
|
||||
|
||||
export const QuietTrigger: Story = {
|
||||
args: {
|
||||
type: 'quiet',
|
||||
},
|
||||
}
|
||||
|
||||
export const Hoverable: Story = {
|
||||
args: {
|
||||
hoverable: true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { SettingsIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from '../../components/base/buttons/Button.vue'
|
||||
import TeleportPopoutMenu from '../../components/base/buttons/TeleportPopoutMenu.vue'
|
||||
|
||||
const meta = {
|
||||
title: 'Buttons/Teleport Popout Menu',
|
||||
component: TeleportPopoutMenu,
|
||||
} satisfies Meta<typeof TeleportPopoutMenu>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const ArbitraryContent: Story = {
|
||||
render: () => ({
|
||||
components: { Button, SettingsIcon, TeleportPopoutMenu },
|
||||
template: /*html*/ `
|
||||
<TeleportPopoutMenu label="Configure versions" placement="bottom-start">
|
||||
<template #trigger>
|
||||
<SettingsIcon />Configure
|
||||
</template>
|
||||
|
||||
<template #panel="{ close }">
|
||||
<div class="flex w-72 flex-col gap-4">
|
||||
<label class="flex flex-col gap-2 font-semibold text-contrast">
|
||||
Version name
|
||||
<input class="rounded-lg bg-surface-4 px-3 py-2 text-primary ring-1 ring-surface-5" value="1.21.8" />
|
||||
</label>
|
||||
<Button type="colored" @click="close()">Apply</Button>
|
||||
</div>
|
||||
</template>
|
||||
</TeleportPopoutMenu>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const IconTrigger: Story = {
|
||||
render: () => ({
|
||||
components: { SettingsIcon, TeleportPopoutMenu },
|
||||
template: /*html*/ `
|
||||
<TeleportPopoutMenu label="Open settings" type="quiet" icon-only>
|
||||
<template #trigger><SettingsIcon /></template>
|
||||
<template #panel>
|
||||
<p class="m-0 w-64">Arbitrary teleported content can live here.</p>
|
||||
</template>
|
||||
</TeleportPopoutMenu>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { fn } from 'storybook/test'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button, IconButton } from '../../components/base/buttons'
|
||||
import ContentCardTable from '../../layouts/shared/content-tab/components/ContentCardTable.vue'
|
||||
import type { ContentCardTableItem } from '../../layouts/shared/content-tab/types'
|
||||
|
||||
@@ -539,7 +539,7 @@ export const InteractiveActions: Story = {
|
||||
|
||||
export const WithCustomItemButtons: Story = {
|
||||
render: () => ({
|
||||
components: { ContentCardTable, ButtonStyled, EyeIcon, FolderOpenIcon, DownloadIcon },
|
||||
components: { ContentCardTable, EyeIcon, FolderOpenIcon, DownloadIcon, Button, IconButton },
|
||||
setup() {
|
||||
return { items: sampleItems }
|
||||
},
|
||||
@@ -551,23 +551,17 @@ export const WithCustomItemButtons: Story = {
|
||||
@delete="(id) => console.log('Delete', id)"
|
||||
>
|
||||
<template #itemButtonsLeft="{ item }">
|
||||
<ButtonStyled v-tooltip="'Download'" circular type="transparent" color="green" color-fill="text">
|
||||
<button @click="console.log('Download', item.id)">
|
||||
<DownloadIcon class="size-5" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton type="quiet" color="green" label="Download" v-tooltip="'Download'" @click="console.log('Download', item.id)" class="!text-green [&>svg]:!text-green">
|
||||
<DownloadIcon class="size-5" />
|
||||
</IconButton>
|
||||
</template>
|
||||
<template #itemButtonsRight="{ item }">
|
||||
<ButtonStyled v-tooltip="'View on Modrinth'" circular type="transparent">
|
||||
<button @click="console.log('View', item.id)">
|
||||
<EyeIcon class="size-5 text-secondary" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-tooltip="'Open folder'" circular type="transparent">
|
||||
<button @click="console.log('Open folder', item.id)">
|
||||
<FolderOpenIcon class="size-5 text-secondary" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton type="quiet" label="View on Modrinth" v-tooltip="'View on Modrinth'" @click="console.log('View', item.id)">
|
||||
<EyeIcon class="size-5 text-secondary" />
|
||||
</IconButton>
|
||||
<IconButton type="quiet" label="Open folder" v-tooltip="'Open folder'" @click="console.log('Open folder', item.id)">
|
||||
<FolderOpenIcon class="size-5 text-secondary" />
|
||||
</IconButton>
|
||||
</template>
|
||||
</ContentCardTable>
|
||||
`,
|
||||
@@ -582,15 +576,13 @@ export const WithEmptyState: Story = {
|
||||
|
||||
export const WithCustomEmptyState: Story = {
|
||||
render: () => ({
|
||||
components: { ContentCardTable, ButtonStyled },
|
||||
components: { ContentCardTable, Button, IconButton },
|
||||
template: /*html*/ `
|
||||
<ContentCardTable :items="[]">
|
||||
<template #empty>
|
||||
<div class="flex flex-col items-center gap-4 py-8">
|
||||
<span class="text-lg text-secondary">No mods installed</span>
|
||||
<ButtonStyled color="green">
|
||||
<button>Browse mods</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="green">Browse mods</Button>
|
||||
</div>
|
||||
</template>
|
||||
</ContentCardTable>
|
||||
@@ -781,7 +773,7 @@ export const WithOverflowMenu: Story = {
|
||||
|
||||
export const BulkActionsDemo: Story = {
|
||||
render: () => ({
|
||||
components: { ContentCardTable, ButtonStyled },
|
||||
components: { ContentCardTable, Button, IconButton },
|
||||
setup() {
|
||||
const items = ref<ContentCardTableItem[]>([
|
||||
{ ...sodiumItem, enabled: true },
|
||||
@@ -825,15 +817,9 @@ export const BulkActionsDemo: Story = {
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-secondary">{{ selectedIds.length }} selected</span>
|
||||
<template v-if="selectedIds.length > 0">
|
||||
<ButtonStyled size="small" color="green">
|
||||
<button @click="enableSelected">Enable</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="small" type="transparent">
|
||||
<button @click="disableSelected">Disable</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled size="small" color="red">
|
||||
<button @click="deleteSelected">Delete</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="green" size="xs" @click="enableSelected" class="!h-6">Enable</Button>
|
||||
<Button type="quiet" size="xs" @click="disableSelected" class="!h-6">Disable</Button>
|
||||
<Button type="colored" color="red" size="xs" @click="deleteSelected" class="!h-6">Delete</Button>
|
||||
</template>
|
||||
</div>
|
||||
<ContentCardTable
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import ContentDependencyWarningModal from '../../layouts/shared/content-tab/components/modals/ContentDependencyWarningModal.vue'
|
||||
import type { ContentCardTableItem } from '../../layouts/shared/content-tab/types'
|
||||
|
||||
@@ -165,7 +165,7 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const InstanceDependency: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, ContentDependencyWarningModal },
|
||||
components: { ContentDependencyWarningModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentDependencyWarningModal> | null>(null)
|
||||
const deleted = ref(false)
|
||||
@@ -186,9 +186,7 @@ export const InstanceDependency: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="modalRef?.show()">Delete dependency</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" @click="modalRef?.show()">Delete dependency</Button>
|
||||
<p v-if="deleted" class="m-0 text-sm text-secondary">Dependency deletion confirmed</p>
|
||||
<ContentDependencyWarningModal
|
||||
ref="modalRef"
|
||||
@@ -210,7 +208,7 @@ export const InstanceDependency: Story = {
|
||||
|
||||
export const ServerDependency: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, ContentDependencyWarningModal },
|
||||
components: { ContentDependencyWarningModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentDependencyWarningModal> | null>(null)
|
||||
const deleted = ref(false)
|
||||
@@ -227,9 +225,7 @@ export const ServerDependency: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="modalRef?.show()">Delete server dependency</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" @click="modalRef?.show()">Delete server dependency</Button>
|
||||
<p v-if="deleted" class="m-0 text-sm text-secondary">Server dependency deletion confirmed</p>
|
||||
<ContentDependencyWarningModal
|
||||
ref="modalRef"
|
||||
@@ -246,7 +242,7 @@ export const ServerDependency: Story = {
|
||||
|
||||
export const BulkDependencies: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, ContentDependencyWarningModal },
|
||||
components: { ContentDependencyWarningModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentDependencyWarningModal> | null>(null)
|
||||
const deleted = ref(false)
|
||||
@@ -267,9 +263,7 @@ export const BulkDependencies: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="modalRef?.show()">Delete selected dependencies</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" @click="modalRef?.show()">Delete selected dependencies</Button>
|
||||
<p v-if="deleted" class="m-0 text-sm text-secondary">Bulk dependency deletion confirmed</p>
|
||||
<ContentDependencyWarningModal
|
||||
ref="modalRef"
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { fn } from 'storybook/test'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import ContentUpdaterModal from '../../layouts/shared/content-tab/components/modals/content-updater-modal/index.vue'
|
||||
|
||||
// Real version data from Modrinth API - Sodium (mod)
|
||||
@@ -265,7 +265,7 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const ModExample: Story = {
|
||||
render: (args) => ({
|
||||
components: { ContentUpdaterModal, ButtonStyled },
|
||||
components: { ContentUpdaterModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentUpdaterModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -277,9 +277,7 @@ export const ModExample: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Update Sodium</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Update Sodium</Button>
|
||||
<ContentUpdaterModal
|
||||
ref="modalRef"
|
||||
v-bind="args"
|
||||
@@ -306,7 +304,7 @@ export const ModExample: Story = {
|
||||
|
||||
export const ModpackExample: Story = {
|
||||
render: (args) => ({
|
||||
components: { ContentUpdaterModal, ButtonStyled },
|
||||
components: { ContentUpdaterModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentUpdaterModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -318,9 +316,7 @@ export const ModpackExample: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Update Cobblemon Modpack</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Update Cobblemon Modpack</Button>
|
||||
<ContentUpdaterModal
|
||||
ref="modalRef"
|
||||
v-bind="args"
|
||||
@@ -347,7 +343,7 @@ export const ModpackExample: Story = {
|
||||
|
||||
export const WithIncompatibleVersions: Story = {
|
||||
render: (args) => ({
|
||||
components: { ContentUpdaterModal, ButtonStyled },
|
||||
components: { ContentUpdaterModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentUpdaterModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -355,9 +351,7 @@ export const WithIncompatibleVersions: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Update (Shows Incompatible)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Update (Shows Incompatible)</Button>
|
||||
<ContentUpdaterModal
|
||||
ref="modalRef"
|
||||
v-bind="args"
|
||||
@@ -381,7 +375,7 @@ export const WithIncompatibleVersions: Story = {
|
||||
|
||||
export const AllVersionTypes: Story = {
|
||||
render: (args) => ({
|
||||
components: { ContentUpdaterModal, ButtonStyled },
|
||||
components: { ContentUpdaterModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentUpdaterModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -389,9 +383,7 @@ export const AllVersionTypes: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View All Version Types</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View All Version Types</Button>
|
||||
<ContentUpdaterModal
|
||||
ref="modalRef"
|
||||
v-bind="args"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import ModpackContentModal from '../../layouts/shared/content-tab/components/modals/ModpackContentModal.vue'
|
||||
import type { ContentItem } from '../../layouts/shared/content-tab/types'
|
||||
|
||||
@@ -409,7 +409,7 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show(mixedModpackContent)
|
||||
@@ -417,9 +417,7 @@ export const Default: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View Modpack Content (Mixed)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View Modpack Content (Mixed)</Button>
|
||||
<ModpackContentModal
|
||||
ref="modalRef"
|
||||
modpack-name="Cobblemon Official Modpack"
|
||||
@@ -432,7 +430,7 @@ export const Default: Story = {
|
||||
|
||||
export const ModsOnly: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show(modsOnlyContent)
|
||||
@@ -440,9 +438,7 @@ export const ModsOnly: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View Modpack Content (Mods Only)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View Modpack Content (Mods Only)</Button>
|
||||
<ModpackContentModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
@@ -455,7 +451,7 @@ export const ModsOnly: Story = {
|
||||
|
||||
export const LoadingState: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => {
|
||||
@@ -469,9 +465,7 @@ export const LoadingState: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View Content (With Loading)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View Content (With Loading)</Button>
|
||||
<ModpackContentModal
|
||||
ref="modalRef"
|
||||
modpack-name="Fabulously Optimized"
|
||||
@@ -488,7 +482,7 @@ export const LoadingState: Story = {
|
||||
|
||||
export const EmptyContent: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show([])
|
||||
@@ -496,9 +490,7 @@ export const EmptyContent: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View Empty Modpack</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View Empty Modpack</Button>
|
||||
<ModpackContentModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
@@ -511,7 +503,7 @@ export const EmptyContent: Story = {
|
||||
|
||||
export const LargeModpack: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show(largeModpackContent)
|
||||
@@ -519,9 +511,7 @@ export const LargeModpack: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View Large Modpack (47 items)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View Large Modpack (47 items)</Button>
|
||||
<ModpackContentModal
|
||||
ref="modalRef"
|
||||
modpack-name="All the Mods 10"
|
||||
@@ -538,7 +528,7 @@ export const LargeModpack: Story = {
|
||||
|
||||
export const SearchDemo: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show(mixedModpackContent)
|
||||
@@ -549,9 +539,7 @@ export const SearchDemo: Story = {
|
||||
<p class="text-sm text-secondary max-w-md text-center">
|
||||
Click the button and try searching for "sodium", "shader", or "faithful" to test the search functionality.
|
||||
</p>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Test Search</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Test Search</Button>
|
||||
<ModpackContentModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
@@ -564,7 +552,7 @@ export const SearchDemo: Story = {
|
||||
|
||||
export const FilterDemo: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show(mixedModpackContent)
|
||||
@@ -575,9 +563,7 @@ export const FilterDemo: Story = {
|
||||
<p class="text-sm text-secondary max-w-md text-center">
|
||||
Click the button and try the filter chips (Mods, Shaders, Resource Packs) to filter content by type.
|
||||
</p>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Test Filters</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Test Filters</Button>
|
||||
<ModpackContentModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
@@ -590,7 +576,7 @@ export const FilterDemo: Story = {
|
||||
|
||||
export const MixedOwnerTypes: Story = {
|
||||
render: () => ({
|
||||
components: { ModpackContentModal, ButtonStyled },
|
||||
components: { ModpackContentModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ModpackContentModal> | null>(null)
|
||||
// Mix of user and organization owners
|
||||
@@ -608,9 +594,7 @@ export const MixedOwnerTypes: Story = {
|
||||
<p class="text-sm text-secondary max-w-md text-center">
|
||||
Shows content with different owner types: users (circular avatar) and organizations (rounded + icon).
|
||||
</p>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">View Mixed Owners</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">View Mixed Owners</Button>
|
||||
<ModpackContentModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import ConfirmLeaveModal from '../../components/modal/ConfirmLeaveModal.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -14,7 +14,7 @@ type Story = StoryObj<typeof ConfirmLeaveModal>
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { ConfirmLeaveModal, ButtonStyled },
|
||||
components: { ConfirmLeaveModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ConfirmLeaveModal> | null>(null)
|
||||
const result = ref<string>('')
|
||||
@@ -27,9 +27,7 @@ export const Default: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="openModal">Trigger Leave Confirmation</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" @click="openModal">Trigger Leave Confirmation</Button>
|
||||
<p v-if="result" class="mt-4 text-secondary">{{ result }}</p>
|
||||
<ConfirmLeaveModal ref="modalRef" />
|
||||
</div>
|
||||
@@ -39,7 +37,7 @@ export const Default: Story = {
|
||||
|
||||
export const CustomMessages: Story = {
|
||||
render: () => ({
|
||||
components: { ConfirmLeaveModal, ButtonStyled },
|
||||
components: { ConfirmLeaveModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ConfirmLeaveModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.prompt()
|
||||
@@ -47,9 +45,7 @@ export const CustomMessages: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="openModal">Discard Draft?</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" @click="openModal">Discard Draft?</Button>
|
||||
<ConfirmLeaveModal
|
||||
ref="modalRef"
|
||||
title="Discard draft?"
|
||||
@@ -65,7 +61,7 @@ export const CustomMessages: Story = {
|
||||
|
||||
export const WarningAdmonition: Story = {
|
||||
render: () => ({
|
||||
components: { ConfirmLeaveModal, ButtonStyled },
|
||||
components: { ConfirmLeaveModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ConfirmLeaveModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.prompt()
|
||||
@@ -73,9 +69,7 @@ export const WarningAdmonition: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="openModal">Open Warning Variant</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" @click="openModal">Open Warning Variant</Button>
|
||||
<ConfirmLeaveModal
|
||||
ref="modalRef"
|
||||
admonition-type="warning"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import NewModal from '../../components/modal/NewModal.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -14,7 +14,7 @@ type Story = StoryObj<typeof NewModal>
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -22,9 +22,7 @@ export const Default: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Modal</Button>
|
||||
<NewModal ref="modalRef" header="Example Modal">
|
||||
<p>This is the modal content.</p>
|
||||
<p class="text-secondary mt-2">You can put any content here.</p>
|
||||
@@ -36,7 +34,7 @@ export const Default: Story = {
|
||||
|
||||
export const WithActions: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -44,19 +42,13 @@ export const WithActions: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Modal with Actions</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Modal with Actions</Button>
|
||||
<NewModal ref="modalRef" header="Confirm Action">
|
||||
<p>Are you sure you want to proceed with this action?</p>
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled>
|
||||
<button @click="modalRef?.hide()">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.hide()">Confirm</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="modalRef?.hide()">Cancel</Button>
|
||||
<Button type="colored" color="brand" @click="modalRef?.hide()">Confirm</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -67,7 +59,7 @@ export const WithActions: Story = {
|
||||
|
||||
export const DangerFade: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -75,19 +67,13 @@ export const DangerFade: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="openModal">Open Danger Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" @click="openModal">Open Danger Modal</Button>
|
||||
<NewModal ref="modalRef" header="Delete Item" fade="danger">
|
||||
<p>Are you sure you want to delete this item? This action cannot be undone.</p>
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled>
|
||||
<button @click="modalRef?.hide()">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="modalRef?.hide()">Delete</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="modalRef?.hide()">Cancel</Button>
|
||||
<Button type="colored" color="red" @click="modalRef?.hide()">Delete</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -98,7 +84,7 @@ export const DangerFade: Story = {
|
||||
|
||||
export const WarningFade: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -106,19 +92,13 @@ export const WarningFade: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="openModal">Open Warning Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" @click="openModal">Open Warning Modal</Button>
|
||||
<NewModal ref="modalRef" header="Warning" fade="warning">
|
||||
<p>This action may have unintended consequences. Please review before proceeding.</p>
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled>
|
||||
<button @click="modalRef?.hide()">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="modalRef?.hide()">Proceed</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="modalRef?.hide()">Cancel</Button>
|
||||
<Button type="colored" color="orange" @click="modalRef?.hide()">Proceed</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -129,7 +109,7 @@ export const WarningFade: Story = {
|
||||
|
||||
export const Scrollable: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -137,9 +117,7 @@ export const Scrollable: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Scrollable Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Scrollable Modal</Button>
|
||||
<NewModal ref="modalRef" header="Scrollable Content" scrollable max-content-height="300px">
|
||||
<div class="space-y-4">
|
||||
<p v-for="i in 20" :key="i">
|
||||
@@ -148,9 +126,7 @@ export const Scrollable: Story = {
|
||||
</div>
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.hide()">Close</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.hide()">Close</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -161,7 +137,7 @@ export const Scrollable: Story = {
|
||||
|
||||
export const MergedHeader: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -169,9 +145,7 @@ export const MergedHeader: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Modal (Merged Header)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Modal (Merged Header)</Button>
|
||||
<NewModal ref="modalRef" hide-header merge-header>
|
||||
<div class="text-center py-8">
|
||||
<h2 class="text-xl font-bold mb-4">Custom Header Area</h2>
|
||||
@@ -185,7 +159,7 @@ export const MergedHeader: Story = {
|
||||
|
||||
export const NotClosable: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -193,17 +167,13 @@ export const NotClosable: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Non-Closable Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Non-Closable Modal</Button>
|
||||
<NewModal ref="modalRef" header="Processing..." :closable="false" :close-on-esc="false" :close-on-click-outside="false">
|
||||
<p>This modal cannot be closed by clicking outside or pressing escape.</p>
|
||||
<p class="text-secondary mt-2">Only the action button can close it.</p>
|
||||
<template #actions>
|
||||
<div class="flex justify-end">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.hide()">I understand, close</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.hide()">I understand, close</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -214,7 +184,7 @@ export const NotClosable: Story = {
|
||||
|
||||
export const NoPadding: Story = {
|
||||
render: () => ({
|
||||
components: { NewModal, ButtonStyled },
|
||||
components: { NewModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof NewModal> | null>(null)
|
||||
const openModal = () => modalRef.value?.show()
|
||||
@@ -222,16 +192,12 @@ export const NoPadding: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Modal (No Padding)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Modal (No Padding)</Button>
|
||||
<NewModal ref="modalRef" header="No Padding Modal" no-padding>
|
||||
<p>This modal has no default padding on the content area.</p>
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end p-6 pt-0">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.hide()">Close</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.hide()">Close</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import ShareModal from '../../components/modal/ShareModal.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -20,7 +20,7 @@ export const LinkShare: Story = {
|
||||
link: true,
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { ShareModal, ButtonStyled },
|
||||
components: { ShareModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ShareModal> | null>(null)
|
||||
const openModal = () => {
|
||||
@@ -30,9 +30,7 @@ export const LinkShare: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Link Share Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Link Share Modal</Button>
|
||||
<ShareModal ref="modalRef" v-bind="args" />
|
||||
</div>
|
||||
`,
|
||||
@@ -47,7 +45,7 @@ export const TextShare: Story = {
|
||||
link: false,
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { ShareModal, ButtonStyled },
|
||||
components: { ShareModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ShareModal> | null>(null)
|
||||
const openModal = () => {
|
||||
@@ -57,9 +55,7 @@ export const TextShare: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Open Text Share Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Open Text Share Modal</Button>
|
||||
<ShareModal ref="modalRef" v-bind="args" />
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import type { StoryObj } from '@storybook/vue3-vite'
|
||||
import { defineComponent, h, ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import UnsavedChangesPopup from '../../components/base/UnsavedChangesPopup.vue'
|
||||
import TabbedModal from '../../components/modal/TabbedModal.vue'
|
||||
|
||||
@@ -42,7 +42,7 @@ export default meta
|
||||
|
||||
export const Default: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled },
|
||||
components: { TabbedModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -66,9 +66,7 @@ export const Default: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open Tabbed Modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open Tabbed Modal</Button>
|
||||
<TabbedModal ref="modalRef" header="Settings" :tabs="tabs" />
|
||||
</div>
|
||||
`,
|
||||
@@ -77,7 +75,7 @@ export const Default: StoryObj = {
|
||||
|
||||
export const WithTitleSlot: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled, SettingsIcon },
|
||||
components: { TabbedModal, SettingsIcon, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -96,9 +94,7 @@ export const WithTitleSlot: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open with Title Slot</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open with Title Slot</Button>
|
||||
<TabbedModal ref="modalRef" :tabs="tabs">
|
||||
<template #title>
|
||||
<span class="flex items-center gap-2 text-lg font-extrabold text-contrast">
|
||||
@@ -113,7 +109,7 @@ export const WithTitleSlot: StoryObj = {
|
||||
|
||||
export const WithFooter: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled },
|
||||
components: { TabbedModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -137,9 +133,7 @@ export const WithFooter: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open with Footer</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open with Footer</Button>
|
||||
<TabbedModal ref="modalRef" header="Settings" :tabs="tabs">
|
||||
<template #footer>
|
||||
<div class="mt-auto text-secondary text-sm">
|
||||
@@ -155,7 +149,7 @@ export const WithFooter: StoryObj = {
|
||||
|
||||
export const WithFloatingActionBar: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled, UnsavedChangesPopup },
|
||||
components: { TabbedModal, UnsavedChangesPopup, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const dirty = ref(true)
|
||||
@@ -170,9 +164,7 @@ export const WithFloatingActionBar: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="dirty = true; modalRef?.show()">Open with Floating Action Bar</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="dirty = true; modalRef?.show()">Open with Floating Action Bar</Button>
|
||||
<TabbedModal
|
||||
ref="modalRef"
|
||||
header="Settings"
|
||||
@@ -196,7 +188,7 @@ export const WithFloatingActionBar: StoryObj = {
|
||||
|
||||
export const WithBadge: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled },
|
||||
components: { TabbedModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -221,9 +213,7 @@ export const WithBadge: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open with Badge</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open with Badge</Button>
|
||||
<TabbedModal ref="modalRef" header="Settings" :tabs="tabs" />
|
||||
</div>
|
||||
`,
|
||||
@@ -232,7 +222,7 @@ export const WithBadge: StoryObj = {
|
||||
|
||||
export const HiddenTabs: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled },
|
||||
components: { TabbedModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -257,9 +247,7 @@ export const HiddenTabs: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open with Hidden Tab</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open with Hidden Tab</Button>
|
||||
<TabbedModal ref="modalRef" header="Settings" :tabs="tabs" />
|
||||
</div>
|
||||
`,
|
||||
@@ -268,7 +256,7 @@ export const HiddenTabs: StoryObj = {
|
||||
|
||||
export const ManyTabs: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled },
|
||||
components: { TabbedModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -331,9 +319,7 @@ export const ManyTabs: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open with Many Tabs</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open with Many Tabs</Button>
|
||||
<TabbedModal ref="modalRef" header="Settings" :tabs="tabs" />
|
||||
</div>
|
||||
`,
|
||||
@@ -342,7 +328,7 @@ export const ManyTabs: StoryObj = {
|
||||
|
||||
export const ScrollableContent: StoryObj = {
|
||||
render: () => ({
|
||||
components: { TabbedModal, ButtonStyled },
|
||||
components: { TabbedModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof TabbedModal> | null>(null)
|
||||
const tabs = [
|
||||
@@ -361,9 +347,7 @@ export const ScrollableContent: StoryObj = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open with Scrollable Content</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open with Scrollable Content</Button>
|
||||
<TabbedModal ref="modalRef" header="Scrollable Demo" :tabs="tabs" />
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import UnknownFileWarningModal from '../../components/modal/UnknownFileWarningModal.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -17,15 +17,13 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Modpack: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, UnknownFileWarningModal },
|
||||
components: { UnknownFileWarningModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof UnknownFileWarningModal> | null>(null)
|
||||
return { modalRef }
|
||||
},
|
||||
template: /* html */ `
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open modpack warning</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open modpack warning</Button>
|
||||
<UnknownFileWarningModal
|
||||
ref="modalRef"
|
||||
mode="modpack"
|
||||
@@ -45,15 +43,13 @@ export const Modpack: Story = {
|
||||
|
||||
export const Mod: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, UnknownFileWarningModal },
|
||||
components: { UnknownFileWarningModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof UnknownFileWarningModal> | null>(null)
|
||||
return { modalRef }
|
||||
},
|
||||
template: /* html */ `
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open file warning</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open file warning</Button>
|
||||
<UnknownFileWarningModal
|
||||
ref="modalRef"
|
||||
mode="mod"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DownloadIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import ContentDiffModal from '../../layouts/shared/installation-settings/components/ContentDiffModal.vue'
|
||||
import type { ContentDiffItem } from '../../layouts/shared/installation-settings/types'
|
||||
|
||||
@@ -66,15 +66,13 @@ const diffs: ContentDiffItem[] = [
|
||||
|
||||
export const ExternalFiles: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, ContentDiffModal },
|
||||
components: { ContentDiffModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof ContentDiffModal> | null>(null)
|
||||
return { diffs, DownloadIcon, modalRef }
|
||||
},
|
||||
template: /* html */ `
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show()">Open update warning</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show()">Open update warning</Button>
|
||||
<ContentDiffModal
|
||||
ref="modalRef"
|
||||
header="Update to play"
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { UploadHandle, UploadProgress } from '@modrinth/api-client'
|
||||
import type { StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import UploadProgressModal from '../../components/modal/UploadProgressModal.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -52,7 +52,7 @@ function createMockUploadHandle(totalBytes: number, durationMs: number): UploadH
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { UploadProgressModal, ButtonStyled },
|
||||
components: { UploadProgressModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof UploadProgressModal> | null>(null)
|
||||
const startUpload = () => {
|
||||
@@ -63,9 +63,7 @@ export const Default: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="startUpload">Upload 50MB file (3s)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="startUpload">Upload 50MB file (3s)</Button>
|
||||
<UploadProgressModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
@@ -74,7 +72,7 @@ export const Default: Story = {
|
||||
|
||||
export const LargeFile: Story = {
|
||||
render: () => ({
|
||||
components: { UploadProgressModal, ButtonStyled },
|
||||
components: { UploadProgressModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof UploadProgressModal> | null>(null)
|
||||
const startUpload = () => {
|
||||
@@ -85,9 +83,7 @@ export const LargeFile: Story = {
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="startUpload">Upload 500MB file (8s)</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="startUpload">Upload 500MB file (8s)</Button>
|
||||
<UploadProgressModal ref="modalRef" />
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import NotificationPanel from '../../components/nav/NotificationPanel.vue'
|
||||
import { injectNotificationManager } from '../../providers'
|
||||
|
||||
@@ -13,7 +13,7 @@ export default meta
|
||||
|
||||
export const Default: StoryObj = {
|
||||
render: () => ({
|
||||
components: { NotificationPanel, ButtonStyled },
|
||||
components: { NotificationPanel, Button },
|
||||
setup() {
|
||||
const notificationManager = injectNotificationManager()
|
||||
|
||||
@@ -87,24 +87,12 @@ export const Default: StoryObj = {
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ButtonStyled color="green">
|
||||
<button @click="showSuccess">Success</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="showError">Error</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="showWarning">Warning</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="blue">
|
||||
<button @click="showInfo">Info</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showActions">Actions</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="clearAll">Clear All</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="green" @click="showSuccess">Success</Button>
|
||||
<Button type="colored" color="red" @click="showError">Error</Button>
|
||||
<Button type="colored" color="orange" @click="showWarning">Warning</Button>
|
||||
<Button type="colored" color="blue" @click="showInfo">Info</Button>
|
||||
<Button @click="showActions">Actions</Button>
|
||||
<Button @click="clearAll">Clear All</Button>
|
||||
</div>
|
||||
<NotificationPanel />
|
||||
</div>
|
||||
@@ -114,7 +102,7 @@ export const Default: StoryObj = {
|
||||
|
||||
export const LeftSide: StoryObj = {
|
||||
render: () => ({
|
||||
components: { NotificationPanel, ButtonStyled },
|
||||
components: { NotificationPanel, Button },
|
||||
setup() {
|
||||
const notificationManager = injectNotificationManager()
|
||||
notificationManager.setNotificationLocation('left')
|
||||
@@ -161,21 +149,11 @@ export const LeftSide: StoryObj = {
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ButtonStyled color="green">
|
||||
<button @click="showSuccess">Success</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="showError">Error</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="showWarning">Warning</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="blue">
|
||||
<button @click="showInfo">Info</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="clearAll">Clear All</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="green" @click="showSuccess">Success</Button>
|
||||
<Button type="colored" color="red" @click="showError">Error</Button>
|
||||
<Button type="colored" color="orange" @click="showWarning">Warning</Button>
|
||||
<Button type="colored" color="blue" @click="showInfo">Info</Button>
|
||||
<Button @click="clearAll">Clear All</Button>
|
||||
</div>
|
||||
<NotificationPanel />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import PopupNotificationPanel from '../../components/nav/PopupNotificationPanel.vue'
|
||||
import { injectPopupNotificationManager } from '../../providers'
|
||||
|
||||
@@ -13,7 +13,7 @@ export default meta
|
||||
|
||||
export const Default: StoryObj = {
|
||||
render: () => ({
|
||||
components: { PopupNotificationPanel, ButtonStyled },
|
||||
components: { PopupNotificationPanel, Button },
|
||||
setup() {
|
||||
const popupManager = injectPopupNotificationManager()
|
||||
|
||||
@@ -209,39 +209,17 @@ export const Default: StoryObj = {
|
||||
template: /* html */ `
|
||||
<div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ButtonStyled color="green">
|
||||
<button @click="showSuccess">Install Complete</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="showError">Download Failed</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="showWarning">Update Available (Permanent)</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="blue">
|
||||
<button @click="showInfo">Download Complete</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showNoButtons">No Buttons</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showPermanent">Permanent</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showBlocking">Blocking</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showWaitingProgress">Waiting Progress</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showDeterminateProgress">Determinate Progress</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="showGroupedDownloads">Grouped Downloads</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="clearAll">Clear All</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="green" @click="showSuccess">Install Complete</Button>
|
||||
<Button type="colored" color="red" @click="showError">Download Failed</Button>
|
||||
<Button type="colored" color="orange" @click="showWarning">Update Available (Permanent)</Button>
|
||||
<Button type="colored" color="blue" @click="showInfo">Download Complete</Button>
|
||||
<Button @click="showNoButtons">No Buttons</Button>
|
||||
<Button @click="showPermanent">Permanent</Button>
|
||||
<Button @click="showBlocking">Blocking</Button>
|
||||
<Button @click="showWaitingProgress">Waiting Progress</Button>
|
||||
<Button @click="showDeterminateProgress">Determinate Progress</Button>
|
||||
<Button @click="showGroupedDownloads">Grouped Downloads</Button>
|
||||
<Button @click="clearAll">Clear All</Button>
|
||||
</div>
|
||||
<PopupNotificationPanel />
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DownloadIcon, MoreVerticalIcon } from '@modrinth/assets'
|
||||
import type { GameVersionTag, Version } from '@modrinth/utils'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { ButtonLink, IconButton } from '../../components/base/buttons'
|
||||
import ProjectPageVersions from '../../components/project/ProjectPageVersions.vue'
|
||||
|
||||
type StoryVersion = Version & {
|
||||
@@ -148,28 +148,24 @@ const meta = {
|
||||
versionLink: (version: Version) => `https://modrinth.com/mod/sodium/version/${version.id}`,
|
||||
},
|
||||
render: (args) => ({
|
||||
components: { ButtonStyled, DownloadIcon, MoreVerticalIcon, ProjectPageVersions },
|
||||
components: { DownloadIcon, MoreVerticalIcon, ProjectPageVersions, ButtonLink, IconButton },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
template: /* html */ `
|
||||
<ProjectPageVersions v-bind="args">
|
||||
<template #actions="{ version }">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<a
|
||||
<ButtonLink type="quiet"
|
||||
v-tooltip="'Download'"
|
||||
:href="version.files[0]?.url"
|
||||
:download="version.files[0]?.filename"
|
||||
aria-label="Download"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<button v-tooltip="'More options'" aria-label="More options">
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</ButtonLink>
|
||||
<IconButton type="quiet" label="More options" v-tooltip="'More options'" aria-label="More options">
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
</IconButton>
|
||||
</template>
|
||||
</ProjectPageVersions>
|
||||
`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import type { CreationFlowContextValue } from '../../components/flows/creation-flow-modal/creation-flow-context'
|
||||
import CreationFlowModal from '../../components/flows/creation-flow-modal/index.vue'
|
||||
|
||||
@@ -23,7 +23,7 @@ type Story = StoryObj<typeof meta>
|
||||
export const CreateWorld: Story = {
|
||||
name: 'Create World (Hosting)',
|
||||
render: () => ({
|
||||
components: { CreationFlowModal, ButtonStyled },
|
||||
components: { CreationFlowModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof CreationFlowModal> | null>(null)
|
||||
const lastEvent = ref('')
|
||||
@@ -36,9 +36,7 @@ export const CreateWorld: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-col gap-4 items-center">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Create World</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Create World</Button>
|
||||
<p v-if="lastEvent" class="text-sm text-secondary mt-2">Last event: {{ lastEvent }}</p>
|
||||
<CreationFlowModal
|
||||
ref="modalRef"
|
||||
@@ -60,7 +58,7 @@ export const CreateWorld: Story = {
|
||||
export const ServerOnboarding: Story = {
|
||||
name: 'Server Setup (Legacy) (Hosting)',
|
||||
render: () => ({
|
||||
components: { CreationFlowModal, ButtonStyled },
|
||||
components: { CreationFlowModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof CreationFlowModal> | null>(null)
|
||||
const lastEvent = ref('')
|
||||
@@ -73,9 +71,7 @@ export const ServerOnboarding: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-col gap-4 items-center">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Set Up Server</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Set Up Server</Button>
|
||||
<p v-if="lastEvent" class="text-sm text-secondary mt-2">Last event: {{ lastEvent }}</p>
|
||||
<CreationFlowModal
|
||||
ref="modalRef"
|
||||
@@ -97,7 +93,7 @@ export const ServerOnboarding: Story = {
|
||||
export const Instance: Story = {
|
||||
name: 'Create Instance (App)',
|
||||
render: () => ({
|
||||
components: { CreationFlowModal, ButtonStyled },
|
||||
components: { CreationFlowModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof CreationFlowModal> | null>(null)
|
||||
const lastEvent = ref('')
|
||||
@@ -110,9 +106,7 @@ export const Instance: Story = {
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div class="flex flex-col gap-4 items-center">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openModal">Create Instance</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openModal">Create Instance</Button>
|
||||
<p v-if="lastEvent" class="text-sm text-secondary mt-2">Last event: {{ lastEvent }}</p>
|
||||
<CreationFlowModal
|
||||
ref="modalRef"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import GrantAccessModal from '../../components/servers/access/GrantAccessModal.vue'
|
||||
import type {
|
||||
GrantServerAccessPayload,
|
||||
@@ -27,7 +27,7 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, GrantAccessModal },
|
||||
components: { GrantAccessModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof GrantAccessModal> | null>(null)
|
||||
const lastAddedUser = ref('')
|
||||
@@ -49,9 +49,7 @@ export const Default: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show($event)">Add user</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show($event)">Add user</Button>
|
||||
<p v-if="lastAddedUser" class="m-0 text-sm text-secondary">Last added: {{ lastAddedUser }}</p>
|
||||
<GrantAccessModal ref="modalRef" :search-users="searchUsers" @grant="handleGrant" />
|
||||
</div>
|
||||
@@ -61,7 +59,7 @@ export const Default: Story = {
|
||||
|
||||
export const ExistingMember: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, GrantAccessModal },
|
||||
components: { GrantAccessModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof GrantAccessModal> | null>(null)
|
||||
const users = [
|
||||
@@ -88,9 +86,7 @@ export const ExistingMember: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show($event)">Add existing user</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show($event)">Add existing user</Button>
|
||||
<GrantAccessModal ref="modalRef" :members="members" :search-users="searchUsers" />
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import RemoveAccessModal from '../../components/servers/access/RemoveAccessModal.vue'
|
||||
|
||||
const meta = {
|
||||
@@ -17,7 +17,7 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, RemoveAccessModal },
|
||||
components: { RemoveAccessModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof RemoveAccessModal> | null>(null)
|
||||
const removed = ref(false)
|
||||
@@ -28,9 +28,7 @@ export const Default: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="red">
|
||||
<button @click="modalRef?.show()">Remove user</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" @click="modalRef?.show()">Remove user</Button>
|
||||
<p v-if="removed" class="m-0 text-sm text-secondary">User removed</p>
|
||||
<RemoveAccessModal
|
||||
ref="modalRef"
|
||||
@@ -46,7 +44,7 @@ export const Default: Story = {
|
||||
|
||||
export const CancelInvite: Story = {
|
||||
render: () => ({
|
||||
components: { ButtonStyled, RemoveAccessModal },
|
||||
components: { RemoveAccessModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof RemoveAccessModal> | null>(null)
|
||||
const cancelled = ref(false)
|
||||
@@ -57,9 +55,7 @@ export const CancelInvite: Story = {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="red">
|
||||
<button @click="modalRef?.show()">Cancel invite</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" @click="modalRef?.show()">Cancel invite</Button>
|
||||
<p v-if="cancelled" class="m-0 text-sm text-secondary">Invite cancelled</p>
|
||||
<RemoveAccessModal
|
||||
ref="modalRef"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { RotateCounterClockwiseIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Admonition from '../../components/base/Admonition.vue'
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
|
||||
type AdmonitionType = 'info' | 'warning' | 'critical' | 'success'
|
||||
type ActionType = 'Cancel' | 'Retry' | 'Dismiss'
|
||||
@@ -191,7 +191,7 @@ const sections: CopySection[] = [
|
||||
|
||||
export const AllCopy: Story = {
|
||||
render: () => ({
|
||||
components: { Admonition, ButtonStyled, RotateCounterClockwiseIcon },
|
||||
components: { Admonition, RotateCounterClockwiseIcon, Button },
|
||||
setup() {
|
||||
return { sections }
|
||||
},
|
||||
@@ -221,19 +221,11 @@ export const AllCopy: Story = {
|
||||
"
|
||||
#top-right-actions
|
||||
>
|
||||
<ButtonStyled v-if="item.action === 'Cancel'" type="outlined" color="blue">
|
||||
<button class="!border" type="button">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled
|
||||
v-else
|
||||
type="outlined"
|
||||
color="red"
|
||||
>
|
||||
<button class="!border" type="button">
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
Retry
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" v-if="item.action === 'Cancel'" class="!border !text-blue [&>svg]:!text-blue !shadow-[inset_0_0_0_1px_var(--color-blue)]" native-type="button">Cancel</Button>
|
||||
<Button type="outlined" v-else class="!border !text-red [&>svg]:!text-red !shadow-[inset_0_0_0_1px_var(--color-red)]" native-type="button">
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
Retry
|
||||
</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
</div>
|
||||
@@ -246,7 +238,7 @@ export const AllCopy: Story = {
|
||||
|
||||
export const TitleTreatmentExperiment: Story = {
|
||||
render: () => ({
|
||||
components: { Admonition, ButtonStyled, RotateCounterClockwiseIcon },
|
||||
components: { Admonition, RotateCounterClockwiseIcon, Button },
|
||||
template: /* html */ `
|
||||
<div style="max-width: 840px;">
|
||||
<Admonition
|
||||
@@ -256,12 +248,10 @@ export const TitleTreatmentExperiment: Story = {
|
||||
>
|
||||
Something went wrong while creating World backup. Please try again or contact support if the issue continues.
|
||||
<template #top-right-actions>
|
||||
<ButtonStyled type="outlined" color="red">
|
||||
<button class="!border" type="button">
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
Retry
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" class="!border !text-red [&>svg]:!text-red !shadow-[inset_0_0_0_1px_var(--color-red)]" native-type="button">
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
Retry
|
||||
</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Labrinth } from '@modrinth/api-client'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button } from '../../components/base/buttons'
|
||||
import InvitePlayersModal from '../../components/sharing/invite-players-modal/index.vue'
|
||||
import type {
|
||||
InviteLinkSettings,
|
||||
@@ -98,7 +98,7 @@ function createSearchUsers() {
|
||||
|
||||
function createRender(args: Record<string, unknown>) {
|
||||
return {
|
||||
components: { ButtonStyled, InvitePlayersModal },
|
||||
components: { InvitePlayersModal, Button },
|
||||
setup() {
|
||||
const modalRef = ref<InstanceType<typeof InvitePlayersModal> | null>(null)
|
||||
const friends = ref<InvitePlayersUser[]>(createFriends())
|
||||
@@ -166,9 +166,7 @@ function createRender(args: Record<string, unknown>) {
|
||||
},
|
||||
template: /* html */ `
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="modalRef?.show($event)">Open modal</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="modalRef?.show($event)">Open modal</Button>
|
||||
<p v-if="lastAction" class="m-0 text-sm text-secondary">{{ lastAction }}</p>
|
||||
<InvitePlayersModal
|
||||
ref="modalRef"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EditIcon, TrashIcon } from '@modrinth/assets'
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import ButtonStyled from '../../components/base/ButtonStyled.vue'
|
||||
import { Button, IconButton } from '../../components/base/buttons'
|
||||
import SkinButton from '../../components/skin/SkinButton.vue'
|
||||
|
||||
const frontImage = `data:image/svg+xml,${encodeURIComponent(`
|
||||
@@ -62,7 +62,7 @@ export const ActiveUnselected: Story = {
|
||||
|
||||
export const WithActions: Story = {
|
||||
render: (args) => ({
|
||||
components: { ButtonStyled, EditIcon, SkinButton, TrashIcon },
|
||||
components: { EditIcon, SkinButton, TrashIcon, Button, IconButton },
|
||||
setup() {
|
||||
return { args }
|
||||
},
|
||||
@@ -70,16 +70,12 @@ export const WithActions: Story = {
|
||||
<div class="w-[156px]">
|
||||
<SkinButton v-bind="args">
|
||||
<template #overlay-buttons>
|
||||
<ButtonStyled color="brand">
|
||||
<button class="pointer-events-auto">
|
||||
<EditIcon /> Edit
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular color="red">
|
||||
<button class="pointer-events-auto" aria-label="Delete skin">
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" class="pointer-events-auto">
|
||||
<EditIcon /> Edit
|
||||
</Button>
|
||||
<IconButton type="colored" color="red" label="Delete skin" class="pointer-events-auto">
|
||||
<TrashIcon />
|
||||
</IconButton>
|
||||
</template>
|
||||
</SkinButton>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user