chore(stories): eslint/tsconfig migration
Migrate Storybook package to eslint flat config + composite tsconfig.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
storybook-static
|
||||
*.log
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { StorybookConfig } from '@storybook/vue3-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../stories/**/*.stories.ts'],
|
||||
addons: [
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-a11y',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/vue3-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
defaultName: 'Docs',
|
||||
},
|
||||
typescript: {
|
||||
check: false,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,110 @@
|
||||
.sb-dialog-trigger,
|
||||
.sb-dialog-close,
|
||||
.sb-toggle {
|
||||
font: inherit;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #888;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sb-toggle[aria-pressed='true'] { background: #222; color: #fff; }
|
||||
.sb-toggle[data-disabled] { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
.sb-dialog-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4); }
|
||||
|
||||
.sb-dialog-content {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
min-width: 320px;
|
||||
max-width: 90vw;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.sb-dialog-title { margin: 0; font-size: 1.125rem; font-weight: 600; }
|
||||
.sb-dialog-desc { margin: 0; color: #555; }
|
||||
|
||||
.sb-switch {
|
||||
position: relative;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #888;
|
||||
background: #ddd;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.sb-switch[aria-checked='true'] { background: #10b981; border-color: #10b981; }
|
||||
.sb-switch-thumb {
|
||||
display: block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
transform: translateX(2px);
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
.sb-switch[aria-checked='true'] .sb-switch-thumb { transform: translateX(22px); }
|
||||
|
||||
.sb-progress {
|
||||
position: relative;
|
||||
width: 240px;
|
||||
height: 8px;
|
||||
background: #eee;
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.sb-progress-ind { height: 100%; background: #3b82f6; transition: width 0.2s; }
|
||||
.sb-progress[data-state='indeterminate'] .sb-progress-ind {
|
||||
animation: sb-progress-indeterminate 1.2s ease-in-out infinite;
|
||||
background: linear-gradient(90deg, transparent, #3b82f6, transparent);
|
||||
}
|
||||
@keyframes sb-progress-indeterminate {
|
||||
from { transform: translateX(-100%); }
|
||||
to { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
.sb-collapsible { font-family: system-ui; max-width: 320px; }
|
||||
.sb-collapsible-trigger {
|
||||
font: inherit;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #888;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.sb-collapsible-content {
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px dashed #bbb;
|
||||
border-top: none;
|
||||
border-radius: 0 0 6px 6px;
|
||||
}
|
||||
|
||||
.sb-avatar {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: #e5e7eb;
|
||||
font-family: system-ui;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sb-avatar-img { width: 100%; height: 100%; object-fit: cover; }
|
||||
.sb-avatar-fallback { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Preview } from '@storybook/vue3-vite';
|
||||
import './preview-styles.css';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
a11y: {
|
||||
test: 'todo',
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
};
|
||||
|
||||
export default preview;
|
||||
@@ -0,0 +1,9 @@
|
||||
import { base, compose, imports, stylistic, typescript } from '@robonen/eslint';
|
||||
|
||||
export default compose(base, typescript, imports, stylistic, {
|
||||
name: 'stories/overrides',
|
||||
files: ['**/*.vue', '**/*.stories.ts'],
|
||||
rules: {
|
||||
'@stylistic/no-multiple-empty-lines': 'off',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@robonen/stories",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "Storybook for @robonen/primitives",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"lint:check": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"dev": "storybook dev -p 6006 --no-open",
|
||||
"build": "storybook build -o storybook-static"
|
||||
},
|
||||
"dependencies": {
|
||||
"@robonen/primitives": "workspace:*",
|
||||
"@robonen/vue": "workspace:*",
|
||||
"vue": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@robonen/eslint": "workspace:*",
|
||||
"@robonen/tsconfig": "workspace:*",
|
||||
"@storybook/addon-a11y": "^10.2.1",
|
||||
"@storybook/addon-docs": "^10.2.1",
|
||||
"@storybook/vue3-vite": "^10.2.1",
|
||||
"@vitejs/plugin-vue": "^6.0.6",
|
||||
"eslint": "catalog:",
|
||||
"storybook": "^10.2.1",
|
||||
"vite": "^7.1.9"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { AspectRatio } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Layout/AspectRatio',
|
||||
component: AspectRatio,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
ratio: { control: { type: 'number', min: 0.1, step: 0.1 } },
|
||||
},
|
||||
args: { ratio: 16 / 9 },
|
||||
} satisfies Meta<typeof AspectRatio>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Widescreen: Story = {
|
||||
render: args => ({
|
||||
components: { AspectRatio },
|
||||
setup: () => ({ args }),
|
||||
template: `
|
||||
<div style="width: 400px; border-radius: 8px; overflow: hidden">
|
||||
<AspectRatio v-bind="args">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1535025183041-0991a977e25b?w=800"
|
||||
alt="landscape"
|
||||
style="width:100%;height:100%;object-fit:cover"
|
||||
/>
|
||||
</AspectRatio>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Square: Story = {
|
||||
args: { ratio: 1 },
|
||||
render: args => ({
|
||||
components: { AspectRatio },
|
||||
setup: () => ({ args }),
|
||||
template: `
|
||||
<div style="width: 200px; background: #eee; border-radius: 8px; overflow: hidden">
|
||||
<AspectRatio v-bind="args">
|
||||
<div style="display:flex;align-items:center;justify-content:center;height:100%;font-family:system-ui">1 : 1</div>
|
||||
</AspectRatio>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { AvatarFallback, AvatarImage, AvatarRoot } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Media/Avatar',
|
||||
component: AvatarRoot,
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof AvatarRoot>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Loaded: Story = {
|
||||
render: () => ({
|
||||
components: { AvatarRoot, AvatarImage, AvatarFallback },
|
||||
template: `
|
||||
<AvatarRoot class="sb-avatar">
|
||||
<AvatarImage src="https://i.pravatar.cc/96?img=5" alt="User" class="sb-avatar-img" />
|
||||
<AvatarFallback class="sb-avatar-fallback" :delayMs="300">CT</AvatarFallback>
|
||||
</AvatarRoot>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Fallback: Story = {
|
||||
render: () => ({
|
||||
components: { AvatarRoot, AvatarImage, AvatarFallback },
|
||||
template: `
|
||||
<AvatarRoot class="sb-avatar">
|
||||
<AvatarImage src="https://invalid.example.com/missing.png" alt="User" class="sb-avatar-img" />
|
||||
<AvatarFallback class="sb-avatar-fallback">AB</AvatarFallback>
|
||||
</AvatarRoot>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Disclosure/Collapsible',
|
||||
component: CollapsibleRoot,
|
||||
tags: ['autodocs'],
|
||||
args: { defaultOpen: false, disabled: false },
|
||||
} satisfies Meta<typeof CollapsibleRoot>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: args => ({
|
||||
components: { CollapsibleRoot, CollapsibleTrigger, CollapsibleContent },
|
||||
setup: () => ({ args }),
|
||||
template: `
|
||||
<CollapsibleRoot v-bind="args" class="sb-collapsible">
|
||||
<CollapsibleTrigger class="sb-collapsible-trigger">
|
||||
<template #default="{ open }">{{ open ? 'Hide' : 'Show' }} details</template>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent class="sb-collapsible-content">
|
||||
<p style="margin:0.5rem 0 0">Hidden content revealed when the trigger is activated.</p>
|
||||
</CollapsibleContent>
|
||||
</CollapsibleRoot>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const OpenByDefault: Story = { args: { defaultOpen: true }, render: Default.render };
|
||||
export const Disabled: Story = { args: { disabled: true }, render: Default.render };
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import {
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogRoot,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Overlays/Dialog',
|
||||
component: DialogRoot,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
modal: { control: 'boolean' },
|
||||
defaultOpen: { control: 'boolean' },
|
||||
},
|
||||
args: {
|
||||
modal: true,
|
||||
defaultOpen: false,
|
||||
},
|
||||
} satisfies Meta<typeof DialogRoot>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const render = (args: Record<string, unknown>) => ({
|
||||
components: {
|
||||
DialogRoot,
|
||||
DialogTrigger,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogClose,
|
||||
},
|
||||
setup: () => ({ args }),
|
||||
template: `
|
||||
<DialogRoot v-bind="args">
|
||||
<DialogTrigger class="sb-dialog-trigger">Open Dialog</DialogTrigger>
|
||||
<DialogPortal>
|
||||
<DialogOverlay class="sb-dialog-overlay" />
|
||||
<DialogContent class="sb-dialog-content">
|
||||
<DialogTitle class="sb-dialog-title">Dialog Title</DialogTitle>
|
||||
<DialogDescription class="sb-dialog-desc">
|
||||
Traps focus, locks scroll, and dismisses on Escape or outside click.
|
||||
</DialogDescription>
|
||||
<DialogClose class="sb-dialog-close">Close</DialogClose>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</DialogRoot>
|
||||
`,
|
||||
});
|
||||
|
||||
export const Default: Story = { render };
|
||||
|
||||
export const OpenByDefault: Story = {
|
||||
args: { defaultOpen: true },
|
||||
render,
|
||||
};
|
||||
|
||||
export const NonModal: Story = {
|
||||
args: { modal: false },
|
||||
render: args => ({
|
||||
components: {
|
||||
DialogRoot,
|
||||
DialogTrigger,
|
||||
DialogPortal,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogClose,
|
||||
},
|
||||
setup: () => ({ args }),
|
||||
template: `
|
||||
<DialogRoot v-bind="args">
|
||||
<DialogTrigger class="sb-dialog-trigger">Open non-modal</DialogTrigger>
|
||||
<DialogPortal>
|
||||
<DialogContent class="sb-dialog-content">
|
||||
<DialogTitle class="sb-dialog-title">Non-modal dialog</DialogTitle>
|
||||
<DialogDescription class="sb-dialog-desc">
|
||||
No overlay, no scroll lock, no focus trap.
|
||||
</DialogDescription>
|
||||
<DialogClose class="sb-dialog-close">Close</DialogClose>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</DialogRoot>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { Label } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Forms/Label',
|
||||
component: Label,
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof Label>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => ({
|
||||
components: { Label },
|
||||
template: `
|
||||
<div style="font-family: system-ui; display: grid; gap: 0.25rem; max-width: 300px">
|
||||
<Label for="email">Email address</Label>
|
||||
<input id="email" type="email" placeholder="you@example.com" style="padding:0.5rem;border:1px solid #888;border-radius:4px" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const WithCheckbox: Story = {
|
||||
render: () => ({
|
||||
components: { Label },
|
||||
template: `
|
||||
<div style="font-family: system-ui">
|
||||
<Label style="display:flex;align-items:center;gap:0.5rem;cursor:pointer">
|
||||
<input type="checkbox" />
|
||||
Subscribe to the newsletter
|
||||
</Label>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ProgressIndicator, ProgressRoot } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Feedback/Progress',
|
||||
component: ProgressRoot,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
modelValue: { control: { type: 'number', min: 0, max: 100, step: 1 } },
|
||||
max: { control: { type: 'number', min: 1 } },
|
||||
},
|
||||
args: { modelValue: 40, max: 100 },
|
||||
} satisfies Meta<typeof ProgressRoot>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Determinate: Story = {
|
||||
render: args => ({
|
||||
components: { ProgressRoot, ProgressIndicator },
|
||||
setup: () => ({ args }),
|
||||
template: `
|
||||
<ProgressRoot v-bind="args" class="sb-progress">
|
||||
<template #default="{ value, max }">
|
||||
<ProgressIndicator
|
||||
class="sb-progress-ind"
|
||||
:style="{ width: value == null ? '100%' : (value / max * 100) + '%' }"
|
||||
/>
|
||||
</template>
|
||||
</ProgressRoot>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Indeterminate: Story = {
|
||||
args: { modelValue: null },
|
||||
render: Determinate.render,
|
||||
};
|
||||
|
||||
export const Complete: Story = {
|
||||
args: { modelValue: 100 },
|
||||
render: Determinate.render,
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { Separator } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Layout/Separator',
|
||||
component: Separator,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
orientation: { control: 'radio', options: ['horizontal', 'vertical'] },
|
||||
decorative: { control: 'boolean' },
|
||||
},
|
||||
args: { orientation: 'horizontal', decorative: false },
|
||||
} satisfies Meta<typeof Separator>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const baseStyle = `
|
||||
background: #888;
|
||||
display: block;
|
||||
`;
|
||||
const horizontal = `${baseStyle} height: 1px; width: 100%;`;
|
||||
const vertical = `${baseStyle} height: 24px; width: 1px; display: inline-block; margin: 0 0.75rem;`;
|
||||
|
||||
export const Horizontal: Story = {
|
||||
render: args => ({
|
||||
components: { Separator },
|
||||
setup: () => ({ args, horizontal }),
|
||||
template: `
|
||||
<div style="max-width: 320px; font-family: system-ui">
|
||||
<p style="margin:0 0 0.5rem">Section one</p>
|
||||
<Separator v-bind="args" :style="horizontal" />
|
||||
<p style="margin:0.5rem 0 0">Section two</p>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Vertical: Story = {
|
||||
args: { orientation: 'vertical' },
|
||||
render: args => ({
|
||||
components: { Separator },
|
||||
setup: () => ({ args, vertical }),
|
||||
template: `
|
||||
<nav style="font-family: system-ui">
|
||||
<a href="#">Home</a>
|
||||
<Separator v-bind="args" :style="vertical" />
|
||||
<a href="#">About</a>
|
||||
<Separator v-bind="args" :style="vertical" />
|
||||
<a href="#">Contact</a>
|
||||
</nav>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { Label, Switch } from '@robonen/primitives';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const meta = {
|
||||
title: 'Forms/Switch',
|
||||
component: Switch,
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof Switch>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const BooleanDefault: Story = {
|
||||
name: 'Boolean (default)',
|
||||
render: () => ({
|
||||
components: { Switch, Label },
|
||||
template: `
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
|
||||
<Switch id="airplane" class="sb-switch">
|
||||
<span class="sb-switch-thumb" />
|
||||
</Switch>
|
||||
<Label for="airplane">Airplane mode</Label>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const StringPair: Story = {
|
||||
name: 'String pair ("on" / "off")',
|
||||
render: () => ({
|
||||
components: { Switch, Label },
|
||||
setup() {
|
||||
const value = ref<'on' | 'off'>('off');
|
||||
return { value };
|
||||
},
|
||||
template: `
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
|
||||
<Switch
|
||||
v-model="value"
|
||||
truthy="on"
|
||||
falsy="off"
|
||||
id="mode"
|
||||
class="sb-switch"
|
||||
>
|
||||
<span class="sb-switch-thumb" />
|
||||
</Switch>
|
||||
<Label for="mode">Mode: {{ value }}</Label>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const ObjectPair: Story = {
|
||||
name: 'Object pair (generic)',
|
||||
render: () => ({
|
||||
components: { Switch, Label },
|
||||
setup() {
|
||||
const LIGHT = { theme: 'light' as const };
|
||||
const DARK = { theme: 'dark' as const };
|
||||
const value = ref<typeof LIGHT | typeof DARK>(LIGHT);
|
||||
return { value, LIGHT, DARK };
|
||||
},
|
||||
template: `
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
|
||||
<Switch
|
||||
v-model="value"
|
||||
:truthy="DARK"
|
||||
:falsy="LIGHT"
|
||||
id="theme"
|
||||
class="sb-switch"
|
||||
>
|
||||
<span class="sb-switch-thumb" />
|
||||
</Switch>
|
||||
<Label for="theme">Theme: {{ value.theme }}</Label>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
name: 'Disabled',
|
||||
render: () => ({
|
||||
components: { Switch, Label },
|
||||
template: `
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;font-family:system-ui">
|
||||
<Switch id="disabled-sw" class="sb-switch" disabled :default-value="true">
|
||||
<span class="sb-switch-thumb" />
|
||||
</Switch>
|
||||
<Label for="disabled-sw">Disabled (checked)</Label>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { Toggle } from '@robonen/primitives';
|
||||
|
||||
const meta = {
|
||||
title: 'Forms/Toggle',
|
||||
component: Toggle,
|
||||
tags: ['autodocs'],
|
||||
argTypes: { disabled: { control: 'boolean' }, defaultPressed: { control: 'boolean' } },
|
||||
args: { disabled: false, defaultPressed: false },
|
||||
} satisfies Meta<typeof Toggle>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
const template = `
|
||||
<Toggle v-bind="args" class="sb-toggle">
|
||||
<template #default="{ pressed }">{{ pressed ? 'Bold ●' : 'Bold' }}</template>
|
||||
</Toggle>
|
||||
`;
|
||||
|
||||
export const Default: Story = {
|
||||
render: args => ({ components: { Toggle }, setup: () => ({ args }), template }),
|
||||
};
|
||||
|
||||
export const Pressed: Story = {
|
||||
args: { defaultPressed: true },
|
||||
render: args => ({ components: { Toggle }, setup: () => ({ args }), template }),
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: { disabled: true },
|
||||
render: args => ({ components: { Toggle }, setup: () => ({ args }), template }),
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.src.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@robonen/tsconfig/tsconfig.node.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
|
||||
},
|
||||
"include": ["*.config.ts", ".storybook/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "@robonen/tsconfig/tsconfig.vue.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["vite/client"],
|
||||
"allowImportingTsExtensions": false,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.src.tsbuildinfo"
|
||||
},
|
||||
"include": ["stories/**/*.ts", "stories/**/*.vue"]
|
||||
}
|
||||
Reference in New Issue
Block a user