mirror of
https://github.com/modrinth/code.git
synced 2026-08-31 03:55:59 +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:
@@ -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' },
|
||||
|
||||
Reference in New Issue
Block a user