agnosticui-cli 2.0.0-alpha.21 → 2.0.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +5 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +23 -9
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +58 -10
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/storybook.d.ts.map +1 -1
- package/dist/commands/storybook.js +48 -27
- package/dist/commands/storybook.js.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/dependencies.d.ts +4 -1
- package/dist/utils/dependencies.d.ts.map +1 -1
- package/dist/utils/dependencies.js +19 -9
- package/dist/utils/dependencies.js.map +1 -1
- package/dist/utils/stories.d.ts +3 -0
- package/dist/utils/stories.d.ts.map +1 -1
- package/dist/utils/stories.js +3218 -6
- package/dist/utils/stories.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/view.d.ts +0 -3
- package/dist/commands/view.d.ts.map +0 -1
- package/dist/commands/view.js +0 -109
- package/dist/commands/view.js.map +0 -1
- package/dist/utils/viewer.d.ts +0 -4
- package/dist/utils/viewer.d.ts.map +0 -1
- package/dist/utils/viewer.js +0 -1067
- package/dist/utils/viewer.js.map +0 -1
package/dist/utils/stories.js
CHANGED
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
// Component classification sets
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
const TEXT_CHILD_COMPONENTS = new Set([
|
|
5
|
-
'Alert', 'Badge', '
|
|
5
|
+
'Alert', 'Badge', 'Button', 'Card',
|
|
6
6
|
'Kbd', 'Link', 'Mark', 'MessageBubble', 'Tag',
|
|
7
7
|
]);
|
|
8
|
+
// Components with non-standard Vue file naming (exported via index.ts)
|
|
9
|
+
const VUE_INDEX_EXPORTS = {
|
|
10
|
+
Timeline: 'VueTimeline',
|
|
11
|
+
};
|
|
12
|
+
// Components that have Fx animation props
|
|
13
|
+
const FX_COMPONENTS = new Set(['BadgeFx', 'ButtonFx', 'IconButtonFx']);
|
|
8
14
|
// Components with an `open` prop that are invisible until triggered
|
|
9
15
|
const OPEN_CONTROLLED_COMPONENTS = new Set([
|
|
10
16
|
'Dialog', 'Drawer', 'Toast', 'Collapsible',
|
|
@@ -14,6 +20,15 @@ const REQUIRED_ARRAY_COMPONENTS = new Set([
|
|
|
14
20
|
'Combobox',
|
|
15
21
|
]);
|
|
16
22
|
// ---------------------------------------------------------------------------
|
|
23
|
+
// Shared header prepended to every auto-generated file
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
const STORY_FILE_HEADER = `// ================================================================
|
|
26
|
+
// AUTO-GENERATED by agnosticui-cli: do not edit by hand.
|
|
27
|
+
// Run: npx agnosticui-cli storybook --force to regenerate
|
|
28
|
+
// To keep customizations: copy to <ComponentName>.stories.custom.*
|
|
29
|
+
// ================================================================
|
|
30
|
+
`;
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
17
32
|
// Storybook config generators
|
|
18
33
|
// ---------------------------------------------------------------------------
|
|
19
34
|
export function generateStorybookMain(framework) {
|
|
@@ -31,7 +46,22 @@ export function generateStorybookMain(framework) {
|
|
|
31
46
|
frameworkPkg = '@storybook/web-components-vite';
|
|
32
47
|
storybookImportType = '@storybook/web-components-vite';
|
|
33
48
|
}
|
|
34
|
-
|
|
49
|
+
// Vue: pass isCustomElement via framework.options so ag-* tags aren't treated as unresolved Vue components
|
|
50
|
+
const frameworkConfig = framework === 'vue'
|
|
51
|
+
? `{
|
|
52
|
+
name: '${frameworkPkg}',
|
|
53
|
+
options: {
|
|
54
|
+
vuePluginOptions: {
|
|
55
|
+
template: {
|
|
56
|
+
compilerOptions: {
|
|
57
|
+
isCustomElement: (tag: string) => tag.startsWith('ag-'),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}`
|
|
63
|
+
: `'${frameworkPkg}'`;
|
|
64
|
+
return STORY_FILE_HEADER + `import type { StorybookConfig } from '${storybookImportType}';
|
|
35
65
|
import type { InlineConfig } from 'vite';
|
|
36
66
|
|
|
37
67
|
const config: StorybookConfig = {
|
|
@@ -43,7 +73,7 @@ const config: StorybookConfig = {
|
|
|
43
73
|
'@storybook/addon-docs',
|
|
44
74
|
'@storybook/addon-a11y',
|
|
45
75
|
],
|
|
46
|
-
framework:
|
|
76
|
+
framework: ${frameworkConfig},
|
|
47
77
|
async viteFinal(config): Promise<InlineConfig> {
|
|
48
78
|
// Remove noDiscovery so Storybook can pre-bundle its own dependencies.
|
|
49
79
|
// The main app uses noDiscovery for performance, but Storybook needs discovery on.
|
|
@@ -70,7 +100,7 @@ export function generateStorybookPreview(framework, componentsRelPath) {
|
|
|
70
100
|
// componentsRelPath is relative from .storybook/ to src/components/ag
|
|
71
101
|
// The CSS imports in preview.ts are relative to .storybook/
|
|
72
102
|
const cssPath = `${componentsRelPath}/styles`;
|
|
73
|
-
return `import type { Preview } from '${previewImportPkg}';
|
|
103
|
+
return STORY_FILE_HEADER + `import type { Preview } from '${previewImportPkg}';
|
|
74
104
|
import '${cssPath}/ag-tokens.css';
|
|
75
105
|
import '${cssPath}/ag-tokens-dark.css';
|
|
76
106
|
|
|
@@ -91,6 +121,19 @@ const preview: Preview = {
|
|
|
91
121
|
export default preview;
|
|
92
122
|
`;
|
|
93
123
|
}
|
|
124
|
+
export function generateStorybookManager() {
|
|
125
|
+
return STORY_FILE_HEADER + `import { addons } from 'storybook/manager-api';
|
|
126
|
+
import { create } from 'storybook/theming/create';
|
|
127
|
+
|
|
128
|
+
const theme = create({
|
|
129
|
+
base: 'light',
|
|
130
|
+
brandTitle: 'AgnosticUI: Auto-generated stories (run: ag storybook --force to regenerate)',
|
|
131
|
+
brandUrl: 'https://github.com/AgnosticUI/agnosticui',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
addons.setConfig({ theme });
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
94
137
|
// ---------------------------------------------------------------------------
|
|
95
138
|
// argTypes registry
|
|
96
139
|
// ---------------------------------------------------------------------------
|
|
@@ -130,6 +173,45 @@ const ARGTYPES = {
|
|
|
130
173
|
},
|
|
131
174
|
dot: { control: 'boolean' },
|
|
132
175
|
interactive: { control: 'boolean' },
|
|
176
|
+
},`,
|
|
177
|
+
BadgeFx: ` argTypes: {
|
|
178
|
+
fx: {
|
|
179
|
+
control: 'select',
|
|
180
|
+
options: ['bounce', 'pulse', 'jelly'],
|
|
181
|
+
},
|
|
182
|
+
fxSpeed: {
|
|
183
|
+
control: 'select',
|
|
184
|
+
options: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
185
|
+
},
|
|
186
|
+
fxEase: {
|
|
187
|
+
control: 'select',
|
|
188
|
+
options: ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'bounce', 'spring-sm', 'spring-md', 'spring-lg'],
|
|
189
|
+
},
|
|
190
|
+
fxDisabled: { control: 'boolean' },
|
|
191
|
+
},`,
|
|
192
|
+
ButtonFx: ` argTypes: {
|
|
193
|
+
fx: {
|
|
194
|
+
control: 'select',
|
|
195
|
+
options: ['bounce', 'pulse', 'jelly', 'shake', 'pulse-wobble'],
|
|
196
|
+
},
|
|
197
|
+
fxSpeed: {
|
|
198
|
+
control: 'select',
|
|
199
|
+
options: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
200
|
+
},
|
|
201
|
+
fxEase: {
|
|
202
|
+
control: 'select',
|
|
203
|
+
options: ['ease', 'ease-in', 'ease-out', 'ease-in-out', 'bounce', 'spring-sm', 'spring-md', 'spring-lg'],
|
|
204
|
+
},
|
|
205
|
+
fxDisabled: { control: 'boolean' },
|
|
206
|
+
variant: {
|
|
207
|
+
control: 'select',
|
|
208
|
+
options: ['', 'primary', 'secondary', 'success', 'warning', 'danger', 'monochrome'],
|
|
209
|
+
},
|
|
210
|
+
size: {
|
|
211
|
+
control: 'select',
|
|
212
|
+
options: ['x-sm', 'sm', 'md', 'lg', 'xl'],
|
|
213
|
+
},
|
|
214
|
+
disabled: { control: 'boolean' },
|
|
133
215
|
},`,
|
|
134
216
|
Button: ` argTypes: {
|
|
135
217
|
variant: {
|
|
@@ -363,10 +445,12 @@ const DEFAULT_ARGS = {
|
|
|
363
445
|
children: '5',
|
|
364
446
|
},`,
|
|
365
447
|
BadgeFx: ` args: {
|
|
448
|
+
fx: 'bounce',
|
|
366
449
|
children: 'BadgeFx',
|
|
367
450
|
},`,
|
|
368
451
|
ButtonFx: ` args: {
|
|
369
|
-
|
|
452
|
+
fx: 'bounce',
|
|
453
|
+
children: 'Click me',
|
|
370
454
|
},`,
|
|
371
455
|
Card: ` args: {
|
|
372
456
|
children: 'Card content goes here.',
|
|
@@ -489,9 +573,247 @@ export const Disabled: Story = {
|
|
|
489
573
|
return '';
|
|
490
574
|
}
|
|
491
575
|
// ---------------------------------------------------------------------------
|
|
576
|
+
// React story overrides — components that need rich custom content
|
|
577
|
+
// ---------------------------------------------------------------------------
|
|
578
|
+
const REACT_STORY_OVERRIDES = {
|
|
579
|
+
Sidebar: `import type { Meta, StoryObj } from '@storybook/react';
|
|
580
|
+
import { useState } from 'react';
|
|
581
|
+
import { ReactSidebar } from './ReactSidebar';
|
|
582
|
+
import {
|
|
583
|
+
ReactSidebarNav,
|
|
584
|
+
ReactSidebarNavItem,
|
|
585
|
+
ReactSidebarNavSubmenu,
|
|
586
|
+
ReactSidebarNavPopoverSubmenu,
|
|
587
|
+
} from '../../SidebarNav/react/index';
|
|
588
|
+
import { ReactPopover } from '../../Popover/react/ReactPopover';
|
|
589
|
+
|
|
590
|
+
const HomeIcon = () => (
|
|
591
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
592
|
+
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
|
593
|
+
<polyline points="9 22 9 12 15 12 15 22"/>
|
|
594
|
+
</svg>
|
|
595
|
+
);
|
|
596
|
+
const FolderIcon = () => (
|
|
597
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
598
|
+
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
|
|
599
|
+
</svg>
|
|
600
|
+
);
|
|
601
|
+
const UsersIcon = () => (
|
|
602
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
603
|
+
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
|
|
604
|
+
<circle cx="9" cy="7" r="4"/>
|
|
605
|
+
<path d="M22 21v-2a4 4 0 0 0-3-3.87"/>
|
|
606
|
+
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
|
607
|
+
</svg>
|
|
608
|
+
);
|
|
609
|
+
const SettingsIcon = () => (
|
|
610
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
611
|
+
<circle cx="12" cy="12" r="3"/>
|
|
612
|
+
<path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/>
|
|
613
|
+
</svg>
|
|
614
|
+
);
|
|
615
|
+
const ChevronRightIcon = () => (
|
|
616
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
617
|
+
<polyline points="9 18 15 12 9 6"/>
|
|
618
|
+
</svg>
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
const NAV_CSS = \`
|
|
622
|
+
.nav-button {
|
|
623
|
+
display: flex;
|
|
624
|
+
align-items: center;
|
|
625
|
+
gap: var(--ag-space-3);
|
|
626
|
+
position: relative;
|
|
627
|
+
padding: var(--ag-space-2) var(--ag-space-3);
|
|
628
|
+
margin-block-end: var(--ag-space-1);
|
|
629
|
+
border: none;
|
|
630
|
+
background: none;
|
|
631
|
+
cursor: pointer;
|
|
632
|
+
width: 100%;
|
|
633
|
+
text-align: left;
|
|
634
|
+
border-radius: var(--ag-radius-sm);
|
|
635
|
+
transition: background var(--ag-fx-duration-sm);
|
|
636
|
+
color: inherit;
|
|
637
|
+
}
|
|
638
|
+
.nav-button svg { flex-shrink: 0; }
|
|
639
|
+
.nav-button:hover { background: var(--ag-background-secondary); }
|
|
640
|
+
.nav-button.active {
|
|
641
|
+
background: var(--ag-primary-background);
|
|
642
|
+
color: var(--ag-primary-text);
|
|
643
|
+
font-weight: 500;
|
|
644
|
+
}
|
|
645
|
+
.nav-label {
|
|
646
|
+
flex-grow: 1;
|
|
647
|
+
overflow: hidden;
|
|
648
|
+
text-overflow: ellipsis;
|
|
649
|
+
white-space: nowrap;
|
|
650
|
+
transition: opacity var(--ag-sidebar-transition-duration) var(--ag-sidebar-transition-easing);
|
|
651
|
+
}
|
|
652
|
+
.chevron {
|
|
653
|
+
display: flex;
|
|
654
|
+
align-items: center;
|
|
655
|
+
transition: transform var(--ag-fx-duration-md), opacity var(--ag-fx-duration-sm);
|
|
656
|
+
margin-left: auto;
|
|
657
|
+
}
|
|
658
|
+
.nav-button[aria-expanded="true"] .chevron { transform: rotate(90deg); }
|
|
659
|
+
ag-sidebar[collapsed] .nav-label,
|
|
660
|
+
ag-sidebar[collapsed] .chevron {
|
|
661
|
+
opacity: 0;
|
|
662
|
+
pointer-events: none;
|
|
663
|
+
display: none;
|
|
664
|
+
}
|
|
665
|
+
ag-sidebar[collapsed] .nav-button {
|
|
666
|
+
width: auto;
|
|
667
|
+
padding: var(--ag-space-2);
|
|
668
|
+
}
|
|
669
|
+
ag-sidebar[collapsed] ag-sidebar-nav-submenu:not(.popover-submenu),
|
|
670
|
+
ag-sidebar:not([collapsed]) ag-popover,
|
|
671
|
+
ag-sidebar[collapsed] .nav-button-expanded,
|
|
672
|
+
ag-sidebar:not([collapsed]) .nav-button-collapsed {
|
|
673
|
+
display: none !important;
|
|
674
|
+
}
|
|
675
|
+
ag-sidebar[collapsed] ag-popover.nav-button-collapsed {
|
|
676
|
+
display: block !important;
|
|
677
|
+
}
|
|
678
|
+
ag-sidebar-nav-submenu {
|
|
679
|
+
display: none;
|
|
680
|
+
overflow: hidden;
|
|
681
|
+
}
|
|
682
|
+
ag-sidebar-nav-submenu[open] {
|
|
683
|
+
display: block;
|
|
684
|
+
}
|
|
685
|
+
.nav-sublink {
|
|
686
|
+
display: block;
|
|
687
|
+
padding: var(--ag-space-2) var(--ag-space-3);
|
|
688
|
+
margin-block-end: var(--ag-space-1);
|
|
689
|
+
color: inherit;
|
|
690
|
+
text-decoration: none;
|
|
691
|
+
border-radius: var(--ag-radius-sm);
|
|
692
|
+
transition: background var(--ag-fx-duration-sm);
|
|
693
|
+
}
|
|
694
|
+
.nav-sublink:hover { background: var(--ag-background-secondary); }
|
|
695
|
+
.nav-button-collapsed::part(ag-popover-body) { padding: var(--ag-space-1); }
|
|
696
|
+
\`;
|
|
697
|
+
|
|
698
|
+
function handleSubmenuToggle(e: React.MouseEvent) {
|
|
699
|
+
e.preventDefault();
|
|
700
|
+
e.stopPropagation();
|
|
701
|
+
const button = e.currentTarget as HTMLElement;
|
|
702
|
+
const navItem = button.closest('ag-sidebar-nav-item');
|
|
703
|
+
const submenu = navItem?.querySelector('ag-sidebar-nav-submenu');
|
|
704
|
+
if (!submenu) return;
|
|
705
|
+
const isExpanded = button.getAttribute('aria-expanded') === 'true';
|
|
706
|
+
button.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
|
|
707
|
+
if (isExpanded) submenu.removeAttribute('open');
|
|
708
|
+
else submenu.setAttribute('open', '');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function SidebarDemo({ initialCollapsed = false, variant = 'default' }: { initialCollapsed?: boolean; variant?: string }) {
|
|
712
|
+
const [collapsed, setCollapsed] = useState(initialCollapsed);
|
|
713
|
+
return (
|
|
714
|
+
<>
|
|
715
|
+
<style>{NAV_CSS}</style>
|
|
716
|
+
<div style={{ position: 'relative', display: 'flex', height: '500px', border: '1px solid var(--ag-border-color)', borderRadius: '0.5rem', overflow: 'hidden', contain: 'layout' as any }}>
|
|
717
|
+
<ReactSidebar collapsed={collapsed} variant={variant as any} aria-label="Main navigation" showMobileToggle>
|
|
718
|
+
<h2 slot="ag-header-start" style={{ margin: 0, fontSize: '1.125rem', fontWeight: 600 }}>Dashboard</h2>
|
|
719
|
+
<button
|
|
720
|
+
type="button"
|
|
721
|
+
slot="ag-header-toggle"
|
|
722
|
+
onClick={() => setCollapsed(c => !c)}
|
|
723
|
+
style={{ background: 'none', border: 'none', padding: '8px 0', cursor: 'pointer', display: 'flex', alignItems: 'center', color: 'inherit' }}
|
|
724
|
+
aria-label="Toggle sidebar"
|
|
725
|
+
>
|
|
726
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
727
|
+
<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/>
|
|
728
|
+
<path d="M9 3v18"/>
|
|
729
|
+
</svg>
|
|
730
|
+
</button>
|
|
731
|
+
<ReactSidebarNav>
|
|
732
|
+
<ReactSidebarNavItem>
|
|
733
|
+
<button type="button" className="nav-button active" aria-current="page">
|
|
734
|
+
<HomeIcon /><span className="nav-label">Dashboard</span>
|
|
735
|
+
</button>
|
|
736
|
+
</ReactSidebarNavItem>
|
|
737
|
+
<ReactSidebarNavItem>
|
|
738
|
+
<button type="button" className="nav-button nav-button-expanded" aria-expanded="false" onClick={handleSubmenuToggle}>
|
|
739
|
+
<FolderIcon /><span className="nav-label">Projects</span>
|
|
740
|
+
<span className="chevron"><ChevronRightIcon /></span>
|
|
741
|
+
</button>
|
|
742
|
+
<ReactPopover className="nav-button-collapsed" placement="right-start" triggerType="click" distance={8} arrow={true}>
|
|
743
|
+
<button slot="trigger" type="button" className="nav-button"><FolderIcon /></button>
|
|
744
|
+
<ReactSidebarNavPopoverSubmenu slot="content">
|
|
745
|
+
<a href="#" className="nav-sublink" onClick={e => e.preventDefault()}>Project Alpha</a>
|
|
746
|
+
<a href="#" className="nav-sublink" onClick={e => e.preventDefault()}>Project Beta</a>
|
|
747
|
+
<a href="#" className="nav-sublink" onClick={e => e.preventDefault()}>Project Gamma</a>
|
|
748
|
+
</ReactSidebarNavPopoverSubmenu>
|
|
749
|
+
</ReactPopover>
|
|
750
|
+
<ReactSidebarNavSubmenu>
|
|
751
|
+
<a className="nav-sublink" href="#" onClick={e => e.preventDefault()}>Project Alpha</a>
|
|
752
|
+
<a className="nav-sublink" href="#" onClick={e => e.preventDefault()}>Project Beta</a>
|
|
753
|
+
<a className="nav-sublink" href="#" onClick={e => e.preventDefault()}>Project Gamma</a>
|
|
754
|
+
</ReactSidebarNavSubmenu>
|
|
755
|
+
</ReactSidebarNavItem>
|
|
756
|
+
<ReactSidebarNavItem>
|
|
757
|
+
<button type="button" className="nav-button">
|
|
758
|
+
<UsersIcon /><span className="nav-label">Team</span>
|
|
759
|
+
</button>
|
|
760
|
+
</ReactSidebarNavItem>
|
|
761
|
+
<ReactSidebarNavItem>
|
|
762
|
+
<button type="button" className="nav-button nav-button-expanded" aria-expanded="false" onClick={handleSubmenuToggle}>
|
|
763
|
+
<SettingsIcon /><span className="nav-label">Settings</span>
|
|
764
|
+
<span className="chevron"><ChevronRightIcon /></span>
|
|
765
|
+
</button>
|
|
766
|
+
<ReactPopover className="nav-button-collapsed" placement="right-start" triggerType="click" distance={8} arrow={true}>
|
|
767
|
+
<button slot="trigger" type="button" className="nav-button"><SettingsIcon /></button>
|
|
768
|
+
<ReactSidebarNavPopoverSubmenu slot="content">
|
|
769
|
+
<a href="#" className="nav-sublink" onClick={e => e.preventDefault()}>Profile</a>
|
|
770
|
+
<a href="#" className="nav-sublink" onClick={e => e.preventDefault()}>Billing</a>
|
|
771
|
+
<a href="#" className="nav-sublink" onClick={e => e.preventDefault()}>Security</a>
|
|
772
|
+
</ReactSidebarNavPopoverSubmenu>
|
|
773
|
+
</ReactPopover>
|
|
774
|
+
<ReactSidebarNavSubmenu>
|
|
775
|
+
<a className="nav-sublink" href="#" onClick={e => e.preventDefault()}>Profile</a>
|
|
776
|
+
<a className="nav-sublink" href="#" onClick={e => e.preventDefault()}>Billing</a>
|
|
777
|
+
<a className="nav-sublink" href="#" onClick={e => e.preventDefault()}>Security</a>
|
|
778
|
+
</ReactSidebarNavSubmenu>
|
|
779
|
+
</ReactSidebarNavItem>
|
|
780
|
+
</ReactSidebarNav>
|
|
781
|
+
<div slot="ag-footer" style={{ fontSize: '0.875rem', color: 'var(--ag-text-secondary)' }}>
|
|
782
|
+
© 2024 Company
|
|
783
|
+
</div>
|
|
784
|
+
</ReactSidebar>
|
|
785
|
+
<main style={{ flex: 1, padding: '2rem', overflow: 'auto', background: 'var(--ag-background)' }}>
|
|
786
|
+
<h1 style={{ marginTop: 0 }}>Main Content</h1>
|
|
787
|
+
<p>Click the header toggle to collapse the sidebar into rail mode.</p>
|
|
788
|
+
<p>When collapsed, click icon-only items with submenus to see them in popovers.</p>
|
|
789
|
+
</main>
|
|
790
|
+
</div>
|
|
791
|
+
</>
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const meta = {
|
|
796
|
+
title: 'AgnosticUI/Sidebar',
|
|
797
|
+
component: ReactSidebar,
|
|
798
|
+
tags: ['autodocs'],
|
|
799
|
+
parameters: { layout: 'fullscreen' },
|
|
800
|
+
} satisfies Meta<typeof ReactSidebar>;
|
|
801
|
+
|
|
802
|
+
export default meta;
|
|
803
|
+
type Story = StoryObj<typeof meta>;
|
|
804
|
+
|
|
805
|
+
export const Default: Story = { render: () => <SidebarDemo /> };
|
|
806
|
+
export const Collapsed: Story = { render: () => <SidebarDemo initialCollapsed={true} /> };
|
|
807
|
+
export const Elevated: Story = { render: () => <SidebarDemo variant="elevated" /> };
|
|
808
|
+
`,
|
|
809
|
+
};
|
|
810
|
+
// ---------------------------------------------------------------------------
|
|
492
811
|
// Per-component story generator (React only)
|
|
493
812
|
// ---------------------------------------------------------------------------
|
|
494
813
|
export function generateReactStory(name) {
|
|
814
|
+
if (REACT_STORY_OVERRIDES[name]) {
|
|
815
|
+
return STORY_FILE_HEADER + REACT_STORY_OVERRIDES[name];
|
|
816
|
+
}
|
|
495
817
|
const lines = [];
|
|
496
818
|
// Imports
|
|
497
819
|
lines.push(`import type { Meta, StoryObj } from '@storybook/react';`);
|
|
@@ -656,6 +978,2896 @@ export function generateReactStory(name) {
|
|
|
656
978
|
if (extras) {
|
|
657
979
|
lines.push(extras);
|
|
658
980
|
}
|
|
659
|
-
return lines.join('\n') + '\n';
|
|
981
|
+
return STORY_FILE_HEADER + lines.join('\n') + '\n';
|
|
982
|
+
}
|
|
983
|
+
// ---------------------------------------------------------------------------
|
|
984
|
+
// Vue story overrides — full story content for components needing special handling
|
|
985
|
+
// ---------------------------------------------------------------------------
|
|
986
|
+
const VUE_STORY_OVERRIDES = {
|
|
987
|
+
Breadcrumb: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
988
|
+
import VueBreadcrumb from './VueBreadcrumb.vue';
|
|
989
|
+
|
|
990
|
+
const ITEMS = [
|
|
991
|
+
{ label: 'Home', href: '#', isCurrent: false },
|
|
992
|
+
{ label: 'Category', href: '#', isCurrent: false },
|
|
993
|
+
{ label: 'Current Page', href: '#', isCurrent: true },
|
|
994
|
+
];
|
|
995
|
+
|
|
996
|
+
const meta = {
|
|
997
|
+
title: 'AgnosticUI/Breadcrumb',
|
|
998
|
+
component: VueBreadcrumb,
|
|
999
|
+
tags: ['autodocs'],
|
|
1000
|
+
argTypes: {
|
|
1001
|
+
type: {
|
|
1002
|
+
control: 'select',
|
|
1003
|
+
options: ['default', 'slash', 'bullet', 'arrow'],
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
args: { items: ITEMS, type: 'default' },
|
|
1007
|
+
render: (args: any) => ({
|
|
1008
|
+
components: { VueBreadcrumb },
|
|
1009
|
+
setup() { return { args }; },
|
|
1010
|
+
template: '<VueBreadcrumb v-bind="args" />',
|
|
1011
|
+
}),
|
|
1012
|
+
} satisfies Meta<typeof VueBreadcrumb>;
|
|
1013
|
+
|
|
1014
|
+
export default meta;
|
|
1015
|
+
type Story = StoryObj<typeof meta>;
|
|
1016
|
+
|
|
1017
|
+
export const Default: Story = {};
|
|
1018
|
+
export const Slash: Story = { args: { type: 'slash' } };
|
|
1019
|
+
export const Arrow: Story = { args: { type: 'arrow' } };
|
|
1020
|
+
`,
|
|
1021
|
+
Flex: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1022
|
+
import VueFlexRow from './VueFlexRow.vue';
|
|
1023
|
+
|
|
1024
|
+
const meta = {
|
|
1025
|
+
title: 'AgnosticUI/Flex',
|
|
1026
|
+
component: VueFlexRow,
|
|
1027
|
+
tags: ['autodocs'],
|
|
1028
|
+
argTypes: {
|
|
1029
|
+
justify: {
|
|
1030
|
+
control: 'select',
|
|
1031
|
+
options: ['flex-start', 'flex-end', 'center', 'space-between', 'space-around', 'space-evenly'],
|
|
1032
|
+
},
|
|
1033
|
+
wrap: {
|
|
1034
|
+
control: 'select',
|
|
1035
|
+
options: ['nowrap', 'wrap', 'wrap-reverse'],
|
|
1036
|
+
},
|
|
1037
|
+
align: {
|
|
1038
|
+
control: 'select',
|
|
1039
|
+
options: ['flex-start', 'flex-end', 'center', 'baseline', 'stretch'],
|
|
1040
|
+
},
|
|
1041
|
+
},
|
|
1042
|
+
render: (args: any) => ({
|
|
1043
|
+
components: { VueFlexRow },
|
|
1044
|
+
setup() { return { args }; },
|
|
1045
|
+
template: \`
|
|
1046
|
+
<VueFlexRow v-bind="args" style="--flex-gap: 1rem">
|
|
1047
|
+
<div style="padding:1rem;background:#e0e7ff;border:1px solid #6366f1">Item 1</div>
|
|
1048
|
+
<div style="padding:1rem;background:#dbeafe;border:1px solid #3b82f6">Item 2</div>
|
|
1049
|
+
<div style="padding:1rem;background:#ddd6fe;border:1px solid #8b5cf6">Item 3</div>
|
|
1050
|
+
</VueFlexRow>
|
|
1051
|
+
\`,
|
|
1052
|
+
}),
|
|
1053
|
+
} satisfies Meta<typeof VueFlexRow>;
|
|
1054
|
+
|
|
1055
|
+
export default meta;
|
|
1056
|
+
type Story = StoryObj<typeof meta>;
|
|
1057
|
+
|
|
1058
|
+
export const Default: Story = {};
|
|
1059
|
+
export const Centered: Story = { args: { justify: 'center' } };
|
|
1060
|
+
export const SpaceBetween: Story = { args: { justify: 'space-between' } };
|
|
1061
|
+
`,
|
|
1062
|
+
Header: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1063
|
+
import VueHeader from './VueHeader.vue';
|
|
1064
|
+
|
|
1065
|
+
const meta = {
|
|
1066
|
+
title: 'AgnosticUI/Header',
|
|
1067
|
+
component: VueHeader,
|
|
1068
|
+
tags: ['autodocs'],
|
|
1069
|
+
argTypes: {
|
|
1070
|
+
sticky: { control: 'boolean' },
|
|
1071
|
+
contentJustify: {
|
|
1072
|
+
control: 'select',
|
|
1073
|
+
options: ['start', 'end', 'between', 'around', 'center'],
|
|
1074
|
+
},
|
|
1075
|
+
},
|
|
1076
|
+
args: { sticky: false, contentJustify: 'between' },
|
|
1077
|
+
render: (args: any) => ({
|
|
1078
|
+
components: { VueHeader },
|
|
1079
|
+
setup() { return { args }; },
|
|
1080
|
+
template: \`
|
|
1081
|
+
<VueHeader v-bind="args">
|
|
1082
|
+
<template #logo>
|
|
1083
|
+
<a href="#" style="text-decoration:none;font-weight:700;font-size:1.25rem">AgnosticUI</a>
|
|
1084
|
+
</template>
|
|
1085
|
+
<nav>
|
|
1086
|
+
<ul style="display:flex;gap:1.5rem;list-style:none;margin:0;padding:0">
|
|
1087
|
+
<li><a href="#">Home</a></li>
|
|
1088
|
+
<li><a href="#">About</a></li>
|
|
1089
|
+
<li><a href="#">Contact</a></li>
|
|
1090
|
+
</ul>
|
|
1091
|
+
</nav>
|
|
1092
|
+
</VueHeader>
|
|
1093
|
+
\`,
|
|
1094
|
+
}),
|
|
1095
|
+
} satisfies Meta<typeof VueHeader>;
|
|
1096
|
+
|
|
1097
|
+
export default meta;
|
|
1098
|
+
type Story = StoryObj<typeof meta>;
|
|
1099
|
+
|
|
1100
|
+
export const Default: Story = {};
|
|
1101
|
+
`,
|
|
1102
|
+
Icon: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1103
|
+
import VueIcon from './VueIcon.vue';
|
|
1104
|
+
import { defineComponent, h } from 'vue';
|
|
1105
|
+
|
|
1106
|
+
const StarIcon = defineComponent({
|
|
1107
|
+
render() {
|
|
1108
|
+
return h('svg', { width: '1em', height: '1em', viewBox: '0 0 24 24', fill: 'currentColor' }, [
|
|
1109
|
+
h('path', { d: 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z' }),
|
|
1110
|
+
]);
|
|
1111
|
+
},
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
const meta = {
|
|
1115
|
+
title: 'AgnosticUI/Icon',
|
|
1116
|
+
component: VueIcon,
|
|
1117
|
+
tags: ['autodocs'],
|
|
1118
|
+
argTypes: {
|
|
1119
|
+
size: {
|
|
1120
|
+
control: 'select',
|
|
1121
|
+
options: ['14', '16', '18', '20', '24', '32', '36', '48'],
|
|
1122
|
+
},
|
|
1123
|
+
type: {
|
|
1124
|
+
control: 'select',
|
|
1125
|
+
options: ['', 'info', 'action', 'success', 'warning', 'error'],
|
|
1126
|
+
},
|
|
1127
|
+
noFill: { control: 'boolean' },
|
|
1128
|
+
},
|
|
1129
|
+
args: { size: '24', type: '' },
|
|
1130
|
+
render: (args: any) => ({
|
|
1131
|
+
components: { VueIcon, StarIcon },
|
|
1132
|
+
setup() { return { args }; },
|
|
1133
|
+
template: '<VueIcon v-bind="args" no-fill><StarIcon /></VueIcon>',
|
|
1134
|
+
}),
|
|
1135
|
+
} satisfies Meta<typeof VueIcon>;
|
|
1136
|
+
|
|
1137
|
+
export default meta;
|
|
1138
|
+
type Story = StoryObj<typeof meta>;
|
|
1139
|
+
|
|
1140
|
+
export const Default: Story = {};
|
|
1141
|
+
export const Types: Story = {
|
|
1142
|
+
render: () => ({
|
|
1143
|
+
components: { VueIcon, StarIcon },
|
|
1144
|
+
template: \`
|
|
1145
|
+
<div style="display:flex;gap:1rem;align-items:center">
|
|
1146
|
+
<VueIcon no-fill><StarIcon /></VueIcon>
|
|
1147
|
+
<VueIcon type="info" no-fill><StarIcon /></VueIcon>
|
|
1148
|
+
<VueIcon type="success" no-fill><StarIcon /></VueIcon>
|
|
1149
|
+
<VueIcon type="warning" no-fill><StarIcon /></VueIcon>
|
|
1150
|
+
<VueIcon type="error" no-fill><StarIcon /></VueIcon>
|
|
1151
|
+
</div>
|
|
1152
|
+
\`,
|
|
1153
|
+
}),
|
|
1154
|
+
};
|
|
1155
|
+
export const Sizes: Story = {
|
|
1156
|
+
render: () => ({
|
|
1157
|
+
components: { VueIcon, StarIcon },
|
|
1158
|
+
template: \`
|
|
1159
|
+
<div style="display:flex;gap:1rem;align-items:center">
|
|
1160
|
+
<VueIcon size="16" no-fill><StarIcon /></VueIcon>
|
|
1161
|
+
<VueIcon size="20" no-fill><StarIcon /></VueIcon>
|
|
1162
|
+
<VueIcon size="24" no-fill><StarIcon /></VueIcon>
|
|
1163
|
+
<VueIcon size="32" no-fill><StarIcon /></VueIcon>
|
|
1164
|
+
<VueIcon size="48" no-fill><StarIcon /></VueIcon>
|
|
1165
|
+
</div>
|
|
1166
|
+
\`,
|
|
1167
|
+
}),
|
|
1168
|
+
};
|
|
1169
|
+
`,
|
|
1170
|
+
IconButton: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1171
|
+
import VueIconButton from './VueIconButton.vue';
|
|
1172
|
+
|
|
1173
|
+
const meta = {
|
|
1174
|
+
title: 'AgnosticUI/IconButton',
|
|
1175
|
+
component: VueIconButton,
|
|
1176
|
+
tags: ['autodocs'],
|
|
1177
|
+
argTypes: {
|
|
1178
|
+
variant: {
|
|
1179
|
+
control: 'select',
|
|
1180
|
+
options: ['primary', 'secondary', 'success', 'warning', 'danger', 'ghost', 'monochrome'],
|
|
1181
|
+
},
|
|
1182
|
+
size: {
|
|
1183
|
+
control: 'select',
|
|
1184
|
+
options: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
1185
|
+
},
|
|
1186
|
+
disabled: { control: 'boolean' },
|
|
1187
|
+
loading: { control: 'boolean' },
|
|
1188
|
+
label: { control: 'text' },
|
|
1189
|
+
unicode: { control: 'text' },
|
|
1190
|
+
},
|
|
1191
|
+
args: { label: 'Action', unicode: '★', size: 'md', variant: 'primary' },
|
|
1192
|
+
render: (args: any) => ({
|
|
1193
|
+
components: { VueIconButton },
|
|
1194
|
+
setup() { return { args }; },
|
|
1195
|
+
template: '<VueIconButton v-bind="args" />',
|
|
1196
|
+
}),
|
|
1197
|
+
} satisfies Meta<typeof VueIconButton>;
|
|
1198
|
+
|
|
1199
|
+
export default meta;
|
|
1200
|
+
type Story = StoryObj<typeof meta>;
|
|
1201
|
+
|
|
1202
|
+
export const Default: Story = {};
|
|
1203
|
+
export const Variants: Story = {
|
|
1204
|
+
render: () => ({
|
|
1205
|
+
components: { VueIconButton },
|
|
1206
|
+
template: \`
|
|
1207
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
1208
|
+
<VueIconButton label="Primary" unicode="★" variant="primary" />
|
|
1209
|
+
<VueIconButton label="Secondary" unicode="★" variant="secondary" />
|
|
1210
|
+
<VueIconButton label="Success" unicode="★" variant="success" />
|
|
1211
|
+
<VueIconButton label="Danger" unicode="★" variant="danger" />
|
|
1212
|
+
<VueIconButton label="Ghost" unicode="★" variant="ghost" />
|
|
1213
|
+
</div>
|
|
1214
|
+
\`,
|
|
1215
|
+
}),
|
|
1216
|
+
};
|
|
1217
|
+
export const Sizes: Story = {
|
|
1218
|
+
render: () => ({
|
|
1219
|
+
components: { VueIconButton },
|
|
1220
|
+
template: \`
|
|
1221
|
+
<div style="display:flex;gap:8px;align-items:center">
|
|
1222
|
+
<VueIconButton label="XS" unicode="★" size="xs" variant="primary" />
|
|
1223
|
+
<VueIconButton label="SM" unicode="★" size="sm" variant="primary" />
|
|
1224
|
+
<VueIconButton label="MD" unicode="★" size="md" variant="primary" />
|
|
1225
|
+
<VueIconButton label="LG" unicode="★" size="lg" variant="primary" />
|
|
1226
|
+
<VueIconButton label="XL" unicode="★" size="xl" variant="primary" />
|
|
1227
|
+
</div>
|
|
1228
|
+
\`,
|
|
1229
|
+
}),
|
|
1230
|
+
};
|
|
1231
|
+
`,
|
|
1232
|
+
IconButtonFx: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1233
|
+
import VueIconButtonFx from './VueIconButtonFx.vue';
|
|
1234
|
+
import { defineComponent, h } from 'vue';
|
|
1235
|
+
|
|
1236
|
+
const HeartIcon = defineComponent({
|
|
1237
|
+
render() {
|
|
1238
|
+
return h('svg', { width: '1em', height: '1em', viewBox: '0 0 24 24', fill: 'currentColor' }, [
|
|
1239
|
+
h('path', { d: 'M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z' }),
|
|
1240
|
+
]);
|
|
1241
|
+
},
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
const meta = {
|
|
1245
|
+
title: 'AgnosticUI/IconButtonFx',
|
|
1246
|
+
component: VueIconButtonFx,
|
|
1247
|
+
tags: ['autodocs'],
|
|
1248
|
+
argTypes: {
|
|
1249
|
+
fx: {
|
|
1250
|
+
control: 'select',
|
|
1251
|
+
options: ['bounce', 'pulse', 'jelly', 'shake', 'pulse-wobble'],
|
|
1252
|
+
},
|
|
1253
|
+
fxSpeed: {
|
|
1254
|
+
control: 'select',
|
|
1255
|
+
options: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
1256
|
+
},
|
|
1257
|
+
fxDisabled: { control: 'boolean' },
|
|
1258
|
+
variant: {
|
|
1259
|
+
control: 'select',
|
|
1260
|
+
options: ['primary', 'secondary', 'success', 'warning', 'danger', 'ghost', 'monochrome'],
|
|
1261
|
+
},
|
|
1262
|
+
size: {
|
|
1263
|
+
control: 'select',
|
|
1264
|
+
options: ['xs', 'sm', 'md', 'lg', 'xl'],
|
|
1265
|
+
},
|
|
1266
|
+
disabled: { control: 'boolean' },
|
|
1267
|
+
label: { control: 'text' },
|
|
1268
|
+
},
|
|
1269
|
+
args: { fx: 'bounce', label: 'Like', size: 'md', variant: 'primary' },
|
|
1270
|
+
render: (args: any) => ({
|
|
1271
|
+
components: { VueIconButtonFx, HeartIcon },
|
|
1272
|
+
setup() { return { args }; },
|
|
1273
|
+
template: '<VueIconButtonFx v-bind="args"><HeartIcon /></VueIconButtonFx>',
|
|
1274
|
+
}),
|
|
1275
|
+
} satisfies Meta<typeof VueIconButtonFx>;
|
|
1276
|
+
|
|
1277
|
+
export default meta;
|
|
1278
|
+
type Story = StoryObj<typeof meta>;
|
|
1279
|
+
|
|
1280
|
+
export const Default: Story = {};
|
|
1281
|
+
export const Variants: Story = {
|
|
1282
|
+
render: () => ({
|
|
1283
|
+
components: { VueIconButtonFx, HeartIcon },
|
|
1284
|
+
template: \`
|
|
1285
|
+
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
|
1286
|
+
<VueIconButtonFx fx="bounce" label="Primary" variant="primary"><HeartIcon /></VueIconButtonFx>
|
|
1287
|
+
<VueIconButtonFx fx="pulse" label="Secondary" variant="secondary"><HeartIcon /></VueIconButtonFx>
|
|
1288
|
+
<VueIconButtonFx fx="jelly" label="Ghost" variant="ghost"><HeartIcon /></VueIconButtonFx>
|
|
1289
|
+
</div>
|
|
1290
|
+
\`,
|
|
1291
|
+
}),
|
|
1292
|
+
};
|
|
1293
|
+
`,
|
|
1294
|
+
Image: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1295
|
+
import VueImage from './VueImage.vue';
|
|
1296
|
+
|
|
1297
|
+
const meta = {
|
|
1298
|
+
title: 'AgnosticUI/Image',
|
|
1299
|
+
component: VueImage,
|
|
1300
|
+
tags: ['autodocs'],
|
|
1301
|
+
argTypes: {
|
|
1302
|
+
fit: {
|
|
1303
|
+
control: 'select',
|
|
1304
|
+
options: ['cover', 'contain', 'fill', 'none', 'scale-down'],
|
|
1305
|
+
},
|
|
1306
|
+
loading: {
|
|
1307
|
+
control: 'select',
|
|
1308
|
+
options: ['lazy', 'eager'],
|
|
1309
|
+
},
|
|
1310
|
+
fade: { control: 'boolean' },
|
|
1311
|
+
},
|
|
1312
|
+
args: {
|
|
1313
|
+
src: 'https://picsum.photos/400/300',
|
|
1314
|
+
alt: 'Sample landscape photo',
|
|
1315
|
+
width: 400,
|
|
1316
|
+
height: 300,
|
|
1317
|
+
},
|
|
1318
|
+
render: (args: any) => ({
|
|
1319
|
+
components: { VueImage },
|
|
1320
|
+
setup() { return { args }; },
|
|
1321
|
+
template: '<VueImage v-bind="args" />',
|
|
1322
|
+
}),
|
|
1323
|
+
} satisfies Meta<typeof VueImage>;
|
|
1324
|
+
|
|
1325
|
+
export default meta;
|
|
1326
|
+
type Story = StoryObj<typeof meta>;
|
|
1327
|
+
|
|
1328
|
+
export const Default: Story = {};
|
|
1329
|
+
export const WithFade: Story = { args: { fade: true } };
|
|
1330
|
+
export const ContainFit: Story = {
|
|
1331
|
+
args: {
|
|
1332
|
+
src: 'https://picsum.photos/400/300?grayscale',
|
|
1333
|
+
alt: 'Contained image',
|
|
1334
|
+
fit: 'contain',
|
|
1335
|
+
aspectRatio: '16/9',
|
|
1336
|
+
},
|
|
1337
|
+
};
|
|
1338
|
+
`,
|
|
1339
|
+
IntlFormatter: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1340
|
+
import VueIntlFormatter from './VueIntlFormatter.vue';
|
|
1341
|
+
|
|
1342
|
+
const meta = {
|
|
1343
|
+
title: 'AgnosticUI/IntlFormatter',
|
|
1344
|
+
component: VueIntlFormatter,
|
|
1345
|
+
tags: ['autodocs'],
|
|
1346
|
+
argTypes: {
|
|
1347
|
+
type: {
|
|
1348
|
+
control: 'select',
|
|
1349
|
+
options: ['date', 'number', 'percent', 'currency'],
|
|
1350
|
+
},
|
|
1351
|
+
lang: { control: 'text' },
|
|
1352
|
+
},
|
|
1353
|
+
args: { type: 'number', value: 1234567.89, lang: 'en-US' },
|
|
1354
|
+
render: (args: any) => ({
|
|
1355
|
+
components: { VueIntlFormatter },
|
|
1356
|
+
setup() { return { args }; },
|
|
1357
|
+
template: '<VueIntlFormatter v-bind="args" />',
|
|
1358
|
+
}),
|
|
1359
|
+
} satisfies Meta<typeof VueIntlFormatter>;
|
|
1360
|
+
|
|
1361
|
+
export default meta;
|
|
1362
|
+
type Story = StoryObj<typeof meta>;
|
|
1363
|
+
|
|
1364
|
+
export const Default: Story = {};
|
|
1365
|
+
export const Currency: Story = {
|
|
1366
|
+
args: { type: 'currency', value: 9999.99, currency: 'USD', lang: 'en-US' },
|
|
1367
|
+
};
|
|
1368
|
+
export const Date: Story = {
|
|
1369
|
+
args: { type: 'date', date: '2024-06-15', dateStyle: 'long', lang: 'en-US' },
|
|
1370
|
+
};
|
|
1371
|
+
export const Percent: Story = {
|
|
1372
|
+
args: { type: 'percent', value: 0.754, lang: 'en-US' },
|
|
1373
|
+
};
|
|
1374
|
+
`,
|
|
1375
|
+
Menu: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1376
|
+
import { VueMenu, VueMenuItem, VueMenuSeparator } from './index';
|
|
1377
|
+
|
|
1378
|
+
const meta = {
|
|
1379
|
+
title: 'AgnosticUI/Menu',
|
|
1380
|
+
component: VueMenu,
|
|
1381
|
+
tags: ['autodocs'],
|
|
1382
|
+
argTypes: {
|
|
1383
|
+
buttonVariant: {
|
|
1384
|
+
control: 'select',
|
|
1385
|
+
options: ['', 'primary', 'secondary', 'success', 'warning', 'danger', 'monochrome'],
|
|
1386
|
+
},
|
|
1387
|
+
size: {
|
|
1388
|
+
control: 'select',
|
|
1389
|
+
options: ['x-sm', 'sm', 'md', 'lg', 'xl'],
|
|
1390
|
+
},
|
|
1391
|
+
disabled: { control: 'boolean' },
|
|
1392
|
+
},
|
|
1393
|
+
args: { size: 'md' },
|
|
1394
|
+
render: (args: any) => ({
|
|
1395
|
+
components: { VueMenu, VueMenuItem, VueMenuSeparator },
|
|
1396
|
+
setup() { return { args }; },
|
|
1397
|
+
template: \`
|
|
1398
|
+
<VueMenu v-bind="args">
|
|
1399
|
+
<template #menu>
|
|
1400
|
+
<VueMenuItem value="edit">Edit</VueMenuItem>
|
|
1401
|
+
<VueMenuItem value="duplicate">Duplicate</VueMenuItem>
|
|
1402
|
+
<VueMenuSeparator />
|
|
1403
|
+
<VueMenuItem value="delete">Delete</VueMenuItem>
|
|
1404
|
+
</template>
|
|
1405
|
+
</VueMenu>
|
|
1406
|
+
\`,
|
|
1407
|
+
}),
|
|
1408
|
+
} satisfies Meta<typeof VueMenu>;
|
|
1409
|
+
|
|
1410
|
+
export default meta;
|
|
1411
|
+
type Story = StoryObj<typeof meta>;
|
|
1412
|
+
|
|
1413
|
+
export const Default: Story = {};
|
|
1414
|
+
`,
|
|
1415
|
+
Popover: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1416
|
+
import VuePopover from './VuePopover.vue';
|
|
1417
|
+
|
|
1418
|
+
const meta = {
|
|
1419
|
+
title: 'AgnosticUI/Popover',
|
|
1420
|
+
component: VuePopover,
|
|
1421
|
+
tags: ['autodocs'],
|
|
1422
|
+
argTypes: {
|
|
1423
|
+
placement: {
|
|
1424
|
+
control: 'select',
|
|
1425
|
+
options: ['top', 'top-start', 'top-end', 'right', 'bottom', 'bottom-start', 'bottom-end', 'left'],
|
|
1426
|
+
},
|
|
1427
|
+
triggerType: {
|
|
1428
|
+
control: 'select',
|
|
1429
|
+
options: ['click', 'hover', 'focus'],
|
|
1430
|
+
},
|
|
1431
|
+
arrow: { control: 'boolean' },
|
|
1432
|
+
showCloseButton: { control: 'boolean' },
|
|
1433
|
+
disabled: { control: 'boolean' },
|
|
1434
|
+
},
|
|
1435
|
+
args: { placement: 'bottom', arrow: true, triggerType: 'click', showCloseButton: true },
|
|
1436
|
+
render: (args: any) => ({
|
|
1437
|
+
components: { VuePopover },
|
|
1438
|
+
setup() { return { args }; },
|
|
1439
|
+
template: \`
|
|
1440
|
+
<VuePopover v-bind="args">
|
|
1441
|
+
<button slot="trigger">Open Popover</button>
|
|
1442
|
+
<span slot="title">Popover Title</span>
|
|
1443
|
+
<div slot="content">
|
|
1444
|
+
<p>Popover content goes here.</p>
|
|
1445
|
+
</div>
|
|
1446
|
+
</VuePopover>
|
|
1447
|
+
\`,
|
|
1448
|
+
}),
|
|
1449
|
+
} satisfies Meta<typeof VuePopover>;
|
|
1450
|
+
|
|
1451
|
+
export default meta;
|
|
1452
|
+
type Story = StoryObj<typeof meta>;
|
|
1453
|
+
|
|
1454
|
+
export const Default: Story = {};
|
|
1455
|
+
export const HoverTrigger: Story = {
|
|
1456
|
+
args: { triggerType: 'hover' },
|
|
1457
|
+
render: (args: any) => ({
|
|
1458
|
+
components: { VuePopover },
|
|
1459
|
+
setup() { return { args }; },
|
|
1460
|
+
template: \`
|
|
1461
|
+
<VuePopover v-bind="args">
|
|
1462
|
+
<button slot="trigger">Hover Me</button>
|
|
1463
|
+
<span slot="title">Hover Popover</span>
|
|
1464
|
+
<div slot="content"><p>Opens on hover.</p></div>
|
|
1465
|
+
</VuePopover>
|
|
1466
|
+
\`,
|
|
1467
|
+
}),
|
|
1468
|
+
};
|
|
1469
|
+
`,
|
|
1470
|
+
Sidebar: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1471
|
+
import { onMounted } from 'vue';
|
|
1472
|
+
import VueSidebar from './VueSidebar.vue';
|
|
1473
|
+
import { VueSidebarNav, VueSidebarNavItem, VueSidebarNavSubmenu } from '../../SidebarNav/vue/index';
|
|
1474
|
+
import VuePopover from '../../Popover/vue/VuePopover.vue';
|
|
1475
|
+
import { VueSidebarNavPopoverSubmenu } from '../../SidebarNav/vue/index';
|
|
1476
|
+
|
|
1477
|
+
const HOME_SVG = \`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>\`;
|
|
1478
|
+
const FOLDER_SVG = \`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>\`;
|
|
1479
|
+
const USERS_SVG = \`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>\`;
|
|
1480
|
+
const SETTINGS_SVG = \`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>\`;
|
|
1481
|
+
const CHEVRON_RIGHT_SVG = \`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>\`;
|
|
1482
|
+
const PANEL_TOGGLE_SVG = \`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><path d="M9 3v18"/></svg>\`;
|
|
1483
|
+
|
|
1484
|
+
const NAV_STYLES = \`
|
|
1485
|
+
.nav-button {
|
|
1486
|
+
display: flex;
|
|
1487
|
+
align-items: center;
|
|
1488
|
+
gap: var(--ag-space-3);
|
|
1489
|
+
position: relative;
|
|
1490
|
+
padding: var(--ag-space-2) var(--ag-space-3);
|
|
1491
|
+
margin-block-end: var(--ag-space-1);
|
|
1492
|
+
border: none;
|
|
1493
|
+
background: none;
|
|
1494
|
+
cursor: pointer;
|
|
1495
|
+
width: 100%;
|
|
1496
|
+
text-align: left;
|
|
1497
|
+
border-radius: var(--ag-radius-sm);
|
|
1498
|
+
transition: background var(--ag-fx-duration-sm);
|
|
1499
|
+
color: inherit;
|
|
1500
|
+
}
|
|
1501
|
+
.nav-button svg { flex-shrink: 0; }
|
|
1502
|
+
.nav-button:hover { background: var(--ag-background-secondary); }
|
|
1503
|
+
.nav-button.active {
|
|
1504
|
+
background: var(--ag-primary-background);
|
|
1505
|
+
color: var(--ag-primary-text);
|
|
1506
|
+
font-weight: 500;
|
|
1507
|
+
}
|
|
1508
|
+
.nav-label {
|
|
1509
|
+
flex-grow: 1;
|
|
1510
|
+
overflow: hidden;
|
|
1511
|
+
text-overflow: ellipsis;
|
|
1512
|
+
white-space: nowrap;
|
|
1513
|
+
transition: opacity var(--ag-sidebar-transition-duration) var(--ag-sidebar-transition-easing);
|
|
1514
|
+
}
|
|
1515
|
+
.chevron {
|
|
1516
|
+
display: flex;
|
|
1517
|
+
align-items: center;
|
|
1518
|
+
transition: transform var(--ag-fx-duration-md), opacity var(--ag-fx-duration-sm);
|
|
1519
|
+
margin-left: auto;
|
|
1520
|
+
}
|
|
1521
|
+
.nav-button[aria-expanded="true"] .chevron { transform: rotate(90deg); }
|
|
1522
|
+
ag-sidebar[collapsed] .nav-label,
|
|
1523
|
+
ag-sidebar[collapsed] .chevron {
|
|
1524
|
+
opacity: 0;
|
|
1525
|
+
pointer-events: none;
|
|
1526
|
+
display: none;
|
|
1527
|
+
}
|
|
1528
|
+
ag-sidebar[collapsed] .nav-button {
|
|
1529
|
+
width: auto;
|
|
1530
|
+
padding: var(--ag-space-2);
|
|
1531
|
+
}
|
|
1532
|
+
ag-sidebar[collapsed] ag-sidebar-nav-submenu:not(.popover-submenu),
|
|
1533
|
+
ag-sidebar:not([collapsed]) ag-popover,
|
|
1534
|
+
ag-sidebar[collapsed] .nav-button-expanded,
|
|
1535
|
+
ag-sidebar:not([collapsed]) .nav-button-collapsed {
|
|
1536
|
+
display: none !important;
|
|
1537
|
+
}
|
|
1538
|
+
ag-sidebar[collapsed] ag-popover.nav-button-collapsed {
|
|
1539
|
+
display: block !important;
|
|
1540
|
+
}
|
|
1541
|
+
ag-sidebar-nav-submenu {
|
|
1542
|
+
display: none;
|
|
1543
|
+
overflow: hidden;
|
|
1544
|
+
}
|
|
1545
|
+
ag-sidebar-nav-submenu[open] {
|
|
1546
|
+
display: block;
|
|
1547
|
+
}
|
|
1548
|
+
.nav-sublink {
|
|
1549
|
+
display: block;
|
|
1550
|
+
padding: var(--ag-space-2) var(--ag-space-3);
|
|
1551
|
+
margin-block-end: var(--ag-space-1);
|
|
1552
|
+
color: inherit;
|
|
1553
|
+
text-decoration: none;
|
|
1554
|
+
border-radius: var(--ag-radius-sm);
|
|
1555
|
+
transition: background var(--ag-fx-duration-sm);
|
|
1556
|
+
}
|
|
1557
|
+
.nav-sublink:hover { background: var(--ag-background-secondary); }
|
|
1558
|
+
.nav-button-collapsed::part(ag-popover-body) { padding: var(--ag-space-1); }
|
|
1559
|
+
\`;
|
|
1560
|
+
|
|
1561
|
+
const meta = {
|
|
1562
|
+
title: 'AgnosticUI/Sidebar',
|
|
1563
|
+
component: VueSidebar,
|
|
1564
|
+
tags: ['autodocs'],
|
|
1565
|
+
parameters: { layout: 'fullscreen' },
|
|
1566
|
+
} satisfies Meta<typeof VueSidebar>;
|
|
1567
|
+
|
|
1568
|
+
export default meta;
|
|
1569
|
+
type Story = StoryObj<typeof meta>;
|
|
1570
|
+
|
|
1571
|
+
function makeStory(initialCollapsed = false, variant = 'default') {
|
|
1572
|
+
return {
|
|
1573
|
+
render: () => ({
|
|
1574
|
+
components: { VueSidebar, VueSidebarNav, VueSidebarNavItem, VueSidebarNavSubmenu, VueSidebarNavPopoverSubmenu, VuePopover },
|
|
1575
|
+
setup() {
|
|
1576
|
+
onMounted(() => {
|
|
1577
|
+
const id = 'ag-sidebar-story-styles';
|
|
1578
|
+
if (!document.getElementById(id)) {
|
|
1579
|
+
const el = document.createElement('style');
|
|
1580
|
+
el.id = id;
|
|
1581
|
+
el.textContent = NAV_STYLES;
|
|
1582
|
+
document.head.appendChild(el);
|
|
1583
|
+
}
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
const toggleCollapse = (e: Event) => {
|
|
1587
|
+
const sidebar = (e.target as HTMLElement).closest('ag-sidebar') as any;
|
|
1588
|
+
if (sidebar && sidebar.toggleCollapse) sidebar.toggleCollapse();
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
const handleSubmenuToggle = (e: Event) => {
|
|
1592
|
+
e.preventDefault();
|
|
1593
|
+
e.stopPropagation();
|
|
1594
|
+
const button = e.currentTarget as HTMLElement;
|
|
1595
|
+
const navItem = button.closest('ag-sidebar-nav-item');
|
|
1596
|
+
const submenu = navItem?.querySelector('ag-sidebar-nav-submenu');
|
|
1597
|
+
if (!submenu) return;
|
|
1598
|
+
const isExpanded = button.getAttribute('aria-expanded') === 'true';
|
|
1599
|
+
button.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
|
|
1600
|
+
if (isExpanded) submenu.removeAttribute('open');
|
|
1601
|
+
else submenu.setAttribute('open', '');
|
|
1602
|
+
};
|
|
1603
|
+
|
|
1604
|
+
return {
|
|
1605
|
+
initialCollapsed, variant, toggleCollapse, handleSubmenuToggle,
|
|
1606
|
+
HOME_SVG, FOLDER_SVG, USERS_SVG, SETTINGS_SVG, CHEVRON_RIGHT_SVG, PANEL_TOGGLE_SVG,
|
|
1607
|
+
};
|
|
1608
|
+
},
|
|
1609
|
+
template: \`
|
|
1610
|
+
<div style="position:relative;display:flex;height:500px;border:1px solid var(--ag-border-color);border-radius:0.5rem;overflow:hidden;contain:layout">
|
|
1611
|
+
<VueSidebar :collapsed="initialCollapsed" :variant="variant" aria-label="Main navigation" show-mobile-toggle>
|
|
1612
|
+
<h2 slot="ag-header-start" style="margin:0;font-size:1.125rem;font-weight:600">Dashboard</h2>
|
|
1613
|
+
<button type="button" slot="ag-header-toggle" @click="toggleCollapse"
|
|
1614
|
+
style="background:none;border:none;padding:8px 0;cursor:pointer;display:flex;align-items:center;color:inherit"
|
|
1615
|
+
aria-label="Toggle sidebar">
|
|
1616
|
+
<span v-html="PANEL_TOGGLE_SVG"></span>
|
|
1617
|
+
</button>
|
|
1618
|
+
<VueSidebarNav>
|
|
1619
|
+
<VueSidebarNavItem>
|
|
1620
|
+
<button type="button" class="nav-button active" aria-current="page">
|
|
1621
|
+
<span v-html="HOME_SVG"></span>
|
|
1622
|
+
<span class="nav-label">Dashboard</span>
|
|
1623
|
+
</button>
|
|
1624
|
+
</VueSidebarNavItem>
|
|
1625
|
+
<VueSidebarNavItem>
|
|
1626
|
+
<button type="button" class="nav-button nav-button-expanded" aria-expanded="false" @click="handleSubmenuToggle">
|
|
1627
|
+
<span v-html="FOLDER_SVG"></span>
|
|
1628
|
+
<span class="nav-label">Projects</span>
|
|
1629
|
+
<span class="chevron" v-html="CHEVRON_RIGHT_SVG"></span>
|
|
1630
|
+
</button>
|
|
1631
|
+
<VuePopover class="nav-button-collapsed" placement="right-start" trigger-type="click" :distance="8" :arrow="true">
|
|
1632
|
+
<button slot="trigger" type="button" class="nav-button">
|
|
1633
|
+
<span v-html="FOLDER_SVG"></span>
|
|
1634
|
+
</button>
|
|
1635
|
+
<VueSidebarNavPopoverSubmenu slot="content">
|
|
1636
|
+
<a href="#" class="nav-sublink" @click.prevent>Project Alpha</a>
|
|
1637
|
+
<a href="#" class="nav-sublink" @click.prevent>Project Beta</a>
|
|
1638
|
+
<a href="#" class="nav-sublink" @click.prevent>Project Gamma</a>
|
|
1639
|
+
</VueSidebarNavPopoverSubmenu>
|
|
1640
|
+
</VuePopover>
|
|
1641
|
+
<VueSidebarNavSubmenu>
|
|
1642
|
+
<a class="nav-sublink" href="#" @click.prevent>Project Alpha</a>
|
|
1643
|
+
<a class="nav-sublink" href="#" @click.prevent>Project Beta</a>
|
|
1644
|
+
<a class="nav-sublink" href="#" @click.prevent>Project Gamma</a>
|
|
1645
|
+
</VueSidebarNavSubmenu>
|
|
1646
|
+
</VueSidebarNavItem>
|
|
1647
|
+
<VueSidebarNavItem>
|
|
1648
|
+
<button type="button" class="nav-button">
|
|
1649
|
+
<span v-html="USERS_SVG"></span>
|
|
1650
|
+
<span class="nav-label">Team</span>
|
|
1651
|
+
</button>
|
|
1652
|
+
</VueSidebarNavItem>
|
|
1653
|
+
<VueSidebarNavItem>
|
|
1654
|
+
<button type="button" class="nav-button nav-button-expanded" aria-expanded="false" @click="handleSubmenuToggle">
|
|
1655
|
+
<span v-html="SETTINGS_SVG"></span>
|
|
1656
|
+
<span class="nav-label">Settings</span>
|
|
1657
|
+
<span class="chevron" v-html="CHEVRON_RIGHT_SVG"></span>
|
|
1658
|
+
</button>
|
|
1659
|
+
<VuePopover class="nav-button-collapsed" placement="right-start" trigger-type="click" :distance="8" :arrow="true">
|
|
1660
|
+
<button slot="trigger" type="button" class="nav-button">
|
|
1661
|
+
<span v-html="SETTINGS_SVG"></span>
|
|
1662
|
+
</button>
|
|
1663
|
+
<VueSidebarNavPopoverSubmenu slot="content">
|
|
1664
|
+
<a href="#" class="nav-sublink" @click.prevent>Profile</a>
|
|
1665
|
+
<a href="#" class="nav-sublink" @click.prevent>Billing</a>
|
|
1666
|
+
<a href="#" class="nav-sublink" @click.prevent>Security</a>
|
|
1667
|
+
</VueSidebarNavPopoverSubmenu>
|
|
1668
|
+
</VuePopover>
|
|
1669
|
+
<VueSidebarNavSubmenu>
|
|
1670
|
+
<a class="nav-sublink" href="#" @click.prevent>Profile</a>
|
|
1671
|
+
<a class="nav-sublink" href="#" @click.prevent>Billing</a>
|
|
1672
|
+
<a class="nav-sublink" href="#" @click.prevent>Security</a>
|
|
1673
|
+
</VueSidebarNavSubmenu>
|
|
1674
|
+
</VueSidebarNavItem>
|
|
1675
|
+
</VueSidebarNav>
|
|
1676
|
+
<div slot="ag-footer" style="font-size:0.875rem;color:var(--ag-text-secondary)">
|
|
1677
|
+
© 2024 Company
|
|
1678
|
+
</div>
|
|
1679
|
+
</VueSidebar>
|
|
1680
|
+
<main style="flex:1;padding:2rem;overflow:auto;background:var(--ag-background)">
|
|
1681
|
+
<h1 style="margin-top:0">Main Content</h1>
|
|
1682
|
+
<p>Click the header toggle to collapse the sidebar into rail mode.</p>
|
|
1683
|
+
<p>When collapsed, click icon-only items with submenus to see them in popovers.</p>
|
|
1684
|
+
</main>
|
|
1685
|
+
</div>
|
|
1686
|
+
\`,
|
|
1687
|
+
}),
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
export const Default: Story = makeStory() as Story;
|
|
1692
|
+
export const Collapsed: Story = makeStory(true) as Story;
|
|
1693
|
+
export const Elevated: Story = makeStory(false, 'elevated') as Story;
|
|
1694
|
+
`,
|
|
1695
|
+
Accordion: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1696
|
+
import { VueAccordion, VueAccordionItem, VueAccordionHeader, VueAccordionContent } from './index';
|
|
1697
|
+
|
|
1698
|
+
const meta = {
|
|
1699
|
+
title: 'AgnosticUI/Accordion',
|
|
1700
|
+
component: VueAccordionItem,
|
|
1701
|
+
tags: ['autodocs'],
|
|
1702
|
+
argTypes: {
|
|
1703
|
+
useChevron: { control: 'boolean' },
|
|
1704
|
+
useX: { control: 'boolean' },
|
|
1705
|
+
useMinus: { control: 'boolean' },
|
|
1706
|
+
noIndicator: { control: 'boolean' },
|
|
1707
|
+
bordered: { control: 'boolean' },
|
|
1708
|
+
background: { control: 'boolean' },
|
|
1709
|
+
disabled: { control: 'boolean' },
|
|
1710
|
+
},
|
|
1711
|
+
args: { useChevron: true },
|
|
1712
|
+
render: (args: any) => ({
|
|
1713
|
+
components: { VueAccordion, VueAccordionItem, VueAccordionHeader, VueAccordionContent },
|
|
1714
|
+
setup() { return { args }; },
|
|
1715
|
+
template: \`
|
|
1716
|
+
<VueAccordion>
|
|
1717
|
+
<VueAccordionItem v-bind="args">
|
|
1718
|
+
<VueAccordionHeader>What is AgnosticUI?</VueAccordionHeader>
|
|
1719
|
+
<VueAccordionContent>
|
|
1720
|
+
AgnosticUI is a framework-agnostic UI component library that works with React, Vue, and Lit.
|
|
1721
|
+
</VueAccordionContent>
|
|
1722
|
+
</VueAccordionItem>
|
|
1723
|
+
<VueAccordionItem v-bind="args">
|
|
1724
|
+
<VueAccordionHeader>How do I install it?</VueAccordionHeader>
|
|
1725
|
+
<VueAccordionContent>
|
|
1726
|
+
Run <code>npx agnosticui-cli init</code> to set up your project, then use <code>npx agnosticui-cli add Button</code> to add components.
|
|
1727
|
+
</VueAccordionContent>
|
|
1728
|
+
</VueAccordionItem>
|
|
1729
|
+
<VueAccordionItem v-bind="args">
|
|
1730
|
+
<VueAccordionHeader>Is it accessible?</VueAccordionHeader>
|
|
1731
|
+
<VueAccordionContent>
|
|
1732
|
+
Yes — all components are built with accessibility in mind, following WAI-ARIA patterns.
|
|
1733
|
+
</VueAccordionContent>
|
|
1734
|
+
</VueAccordionItem>
|
|
1735
|
+
</VueAccordion>
|
|
1736
|
+
\`,
|
|
1737
|
+
}),
|
|
1738
|
+
} satisfies Meta<typeof VueAccordionItem>;
|
|
1739
|
+
|
|
1740
|
+
export default meta;
|
|
1741
|
+
type Story = StoryObj<typeof meta>;
|
|
1742
|
+
|
|
1743
|
+
export const Default: Story = {};
|
|
1744
|
+
export const OpenByDefault: Story = {
|
|
1745
|
+
render: (args: any) => ({
|
|
1746
|
+
components: { VueAccordion, VueAccordionItem, VueAccordionHeader, VueAccordionContent },
|
|
1747
|
+
setup() { return { args }; },
|
|
1748
|
+
template: \`
|
|
1749
|
+
<VueAccordion>
|
|
1750
|
+
<VueAccordionItem v-bind="args" :open="true">
|
|
1751
|
+
<VueAccordionHeader>Open by default</VueAccordionHeader>
|
|
1752
|
+
<VueAccordionContent>This item starts open.</VueAccordionContent>
|
|
1753
|
+
</VueAccordionItem>
|
|
1754
|
+
<VueAccordionItem v-bind="args">
|
|
1755
|
+
<VueAccordionHeader>Closed by default</VueAccordionHeader>
|
|
1756
|
+
<VueAccordionContent>Click to expand.</VueAccordionContent>
|
|
1757
|
+
</VueAccordionItem>
|
|
1758
|
+
</VueAccordion>
|
|
1759
|
+
\`,
|
|
1760
|
+
}),
|
|
1761
|
+
};
|
|
1762
|
+
export const Bordered: Story = { args: { bordered: true } };
|
|
1763
|
+
export const PlusMinusIndicator: Story = { args: { useMinus: true, useChevron: false } };
|
|
1764
|
+
`,
|
|
1765
|
+
Fieldset: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1766
|
+
import VueFieldset from './VueFieldset.vue';
|
|
1767
|
+
import VueInput from '../../Input/vue/VueInput.vue';
|
|
1768
|
+
import VueCheckbox from '../../Checkbox/vue/VueCheckbox.vue';
|
|
1769
|
+
|
|
1770
|
+
const meta = {
|
|
1771
|
+
title: 'AgnosticUI/Fieldset',
|
|
1772
|
+
component: VueFieldset,
|
|
1773
|
+
tags: ['autodocs'],
|
|
1774
|
+
argTypes: {
|
|
1775
|
+
bordered: { control: 'boolean' },
|
|
1776
|
+
layout: { control: 'select', options: ['vertical', 'horizontal'] },
|
|
1777
|
+
legendHidden: { control: 'boolean' },
|
|
1778
|
+
legend: { control: 'text' },
|
|
1779
|
+
},
|
|
1780
|
+
args: { legend: 'Personal Information', bordered: false, layout: 'vertical' },
|
|
1781
|
+
render: (args: any) => ({
|
|
1782
|
+
components: { VueFieldset, VueInput },
|
|
1783
|
+
setup() { return { args }; },
|
|
1784
|
+
template: \`
|
|
1785
|
+
<VueFieldset v-bind="args">
|
|
1786
|
+
<VueInput label="First Name" type="text" />
|
|
1787
|
+
<VueInput label="Last Name" type="text" />
|
|
1788
|
+
<VueInput label="Email" type="email" />
|
|
1789
|
+
</VueFieldset>
|
|
1790
|
+
\`,
|
|
1791
|
+
}),
|
|
1792
|
+
} satisfies Meta<typeof VueFieldset>;
|
|
1793
|
+
|
|
1794
|
+
export default meta;
|
|
1795
|
+
type Story = StoryObj<typeof meta>;
|
|
1796
|
+
|
|
1797
|
+
export const Default: Story = {};
|
|
1798
|
+
export const Bordered: Story = { args: { legend: 'Contact Preferences', bordered: true } };
|
|
1799
|
+
export const Horizontal: Story = { args: { legend: 'Options', layout: 'horizontal' } };
|
|
1800
|
+
`,
|
|
1801
|
+
Progress: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1802
|
+
import VueProgress from './VueProgress.vue';
|
|
1803
|
+
|
|
1804
|
+
const meta = {
|
|
1805
|
+
title: 'AgnosticUI/Progress',
|
|
1806
|
+
component: VueProgress,
|
|
1807
|
+
tags: ['autodocs'],
|
|
1808
|
+
argTypes: {
|
|
1809
|
+
value: { control: { type: 'number', min: 0, max: 100 } },
|
|
1810
|
+
max: { control: { type: 'number' } },
|
|
1811
|
+
label: { control: 'text' },
|
|
1812
|
+
size: { control: 'select', options: ['small', 'medium', 'large'] },
|
|
1813
|
+
},
|
|
1814
|
+
args: { value: 60, max: 100, label: 'Loading progress' },
|
|
1815
|
+
render: (args: any) => ({
|
|
1816
|
+
components: { VueProgress },
|
|
1817
|
+
setup() { return { args }; },
|
|
1818
|
+
template: '<VueProgress v-bind="args" />',
|
|
1819
|
+
}),
|
|
1820
|
+
} satisfies Meta<typeof VueProgress>;
|
|
1821
|
+
|
|
1822
|
+
export default meta;
|
|
1823
|
+
type Story = StoryObj<typeof meta>;
|
|
1824
|
+
|
|
1825
|
+
export const Default: Story = {};
|
|
1826
|
+
export const HalfComplete: Story = { args: { value: 50, label: '50% complete' } };
|
|
1827
|
+
export const NearlyDone: Story = { args: { value: 85, label: 'Almost there' } };
|
|
1828
|
+
export const Indeterminate: Story = { args: { value: undefined, label: 'Loading...' } };
|
|
1829
|
+
`,
|
|
1830
|
+
ProgressRing: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1831
|
+
import VueProgressRing from './VueProgressRing.vue';
|
|
1832
|
+
|
|
1833
|
+
const meta = {
|
|
1834
|
+
title: 'AgnosticUI/ProgressRing',
|
|
1835
|
+
component: VueProgressRing,
|
|
1836
|
+
tags: ['autodocs'],
|
|
1837
|
+
argTypes: {
|
|
1838
|
+
value: { control: { type: 'range', min: 0, max: 100, step: 1 } },
|
|
1839
|
+
size: { control: 'select', options: ['small', 'medium', 'large'] },
|
|
1840
|
+
variant: { control: 'select', options: ['primary', 'success', 'warning', 'danger', 'info'] },
|
|
1841
|
+
label: { control: 'text' },
|
|
1842
|
+
},
|
|
1843
|
+
args: { value: 65, size: 'medium', variant: 'primary', label: 'Progress' },
|
|
1844
|
+
render: (args: any) => ({
|
|
1845
|
+
components: { VueProgressRing },
|
|
1846
|
+
setup() { return { args }; },
|
|
1847
|
+
template: '<VueProgressRing v-bind="args" />',
|
|
1848
|
+
}),
|
|
1849
|
+
} satisfies Meta<typeof VueProgressRing>;
|
|
1850
|
+
|
|
1851
|
+
export default meta;
|
|
1852
|
+
type Story = StoryObj<typeof meta>;
|
|
1853
|
+
|
|
1854
|
+
export const Default: Story = {};
|
|
1855
|
+
export const Variants: Story = {
|
|
1856
|
+
render: () => ({
|
|
1857
|
+
components: { VueProgressRing },
|
|
1858
|
+
template: \`
|
|
1859
|
+
<div style="display:flex;gap:1.5rem;align-items:center;flex-wrap:wrap">
|
|
1860
|
+
<VueProgressRing :value="65" variant="primary" label="Primary" />
|
|
1861
|
+
<VueProgressRing :value="80" variant="success" label="Success" />
|
|
1862
|
+
<VueProgressRing :value="45" variant="warning" label="Warning" />
|
|
1863
|
+
<VueProgressRing :value="30" variant="danger" label="Danger" />
|
|
1864
|
+
<VueProgressRing :value="55" variant="info" label="Info" />
|
|
1865
|
+
</div>
|
|
1866
|
+
\`,
|
|
1867
|
+
}),
|
|
1868
|
+
};
|
|
1869
|
+
export const Sizes: Story = {
|
|
1870
|
+
render: () => ({
|
|
1871
|
+
components: { VueProgressRing },
|
|
1872
|
+
template: \`
|
|
1873
|
+
<div style="display:flex;gap:1.5rem;align-items:center;flex-wrap:wrap">
|
|
1874
|
+
<VueProgressRing :value="65" size="small" label="Small" />
|
|
1875
|
+
<VueProgressRing :value="65" size="medium" label="Medium" />
|
|
1876
|
+
<VueProgressRing :value="65" size="large" label="Large" />
|
|
1877
|
+
</div>
|
|
1878
|
+
\`,
|
|
1879
|
+
}),
|
|
1880
|
+
};
|
|
1881
|
+
`,
|
|
1882
|
+
Tabs: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1883
|
+
import { VueTabs, VueTab, VueTabPanel } from './index';
|
|
1884
|
+
|
|
1885
|
+
const meta = {
|
|
1886
|
+
title: 'AgnosticUI/Tabs',
|
|
1887
|
+
component: VueTabs,
|
|
1888
|
+
tags: ['autodocs'],
|
|
1889
|
+
argTypes: {
|
|
1890
|
+
activation: {
|
|
1891
|
+
control: 'select',
|
|
1892
|
+
options: ['manual', 'automatic'],
|
|
1893
|
+
},
|
|
1894
|
+
orientation: {
|
|
1895
|
+
control: 'select',
|
|
1896
|
+
options: ['horizontal', 'vertical'],
|
|
1897
|
+
},
|
|
1898
|
+
},
|
|
1899
|
+
args: { activation: 'manual', ariaLabel: 'Example tabs' },
|
|
1900
|
+
render: (args: any) => ({
|
|
1901
|
+
components: { VueTabs, VueTab, VueTabPanel },
|
|
1902
|
+
setup() { return { args }; },
|
|
1903
|
+
template: \`
|
|
1904
|
+
<VueTabs v-bind="args">
|
|
1905
|
+
<VueTab panel="panel-1">Tab 1</VueTab>
|
|
1906
|
+
<VueTab panel="panel-2">Tab 2</VueTab>
|
|
1907
|
+
<VueTab panel="panel-3">Tab 3</VueTab>
|
|
1908
|
+
<VueTabPanel panel="panel-1">Content for Tab 1</VueTabPanel>
|
|
1909
|
+
<VueTabPanel panel="panel-2">Content for Tab 2</VueTabPanel>
|
|
1910
|
+
<VueTabPanel panel="panel-3">Content for Tab 3</VueTabPanel>
|
|
1911
|
+
</VueTabs>
|
|
1912
|
+
\`,
|
|
1913
|
+
}),
|
|
1914
|
+
} satisfies Meta<typeof VueTabs>;
|
|
1915
|
+
|
|
1916
|
+
export default meta;
|
|
1917
|
+
type Story = StoryObj<typeof meta>;
|
|
1918
|
+
|
|
1919
|
+
export const Default: Story = {};
|
|
1920
|
+
export const Vertical: Story = { args: { orientation: 'vertical', ariaLabel: 'Vertical tabs' } };
|
|
1921
|
+
`,
|
|
1922
|
+
Timeline: `import type { Meta, StoryObj } from '@storybook/vue3';
|
|
1923
|
+
import { VueTimeline, VueTimelineItem } from './index';
|
|
1924
|
+
|
|
1925
|
+
const meta = {
|
|
1926
|
+
title: 'AgnosticUI/Timeline',
|
|
1927
|
+
component: VueTimeline,
|
|
1928
|
+
tags: ['autodocs'],
|
|
1929
|
+
argTypes: {
|
|
1930
|
+
orientation: {
|
|
1931
|
+
control: 'select',
|
|
1932
|
+
options: ['vertical', 'horizontal'],
|
|
1933
|
+
},
|
|
1934
|
+
variant: {
|
|
1935
|
+
control: 'select',
|
|
1936
|
+
options: ['', 'primary', 'success', 'warning', 'danger', 'monochrome'],
|
|
1937
|
+
},
|
|
1938
|
+
compact: { control: 'boolean' },
|
|
1939
|
+
},
|
|
1940
|
+
args: { orientation: 'horizontal', variant: '' },
|
|
1941
|
+
render: (args: any) => ({
|
|
1942
|
+
components: { VueTimeline, VueTimelineItem },
|
|
1943
|
+
setup() { return { args }; },
|
|
1944
|
+
template: \`
|
|
1945
|
+
<VueTimeline v-bind="args">
|
|
1946
|
+
<VueTimelineItem>
|
|
1947
|
+
<template #ag-start>Jan 2024</template>
|
|
1948
|
+
<template #ag-end>Project kickoff</template>
|
|
1949
|
+
</VueTimelineItem>
|
|
1950
|
+
<VueTimelineItem>
|
|
1951
|
+
<template #ag-start>Mar 2024</template>
|
|
1952
|
+
<template #ag-end>Design phase</template>
|
|
1953
|
+
</VueTimelineItem>
|
|
1954
|
+
<VueTimelineItem>
|
|
1955
|
+
<template #ag-start>Jun 2024</template>
|
|
1956
|
+
<template #ag-end>Development complete</template>
|
|
1957
|
+
</VueTimelineItem>
|
|
1958
|
+
</VueTimeline>
|
|
1959
|
+
\`,
|
|
1960
|
+
}),
|
|
1961
|
+
} satisfies Meta<typeof VueTimeline>;
|
|
1962
|
+
|
|
1963
|
+
export default meta;
|
|
1964
|
+
type Story = StoryObj<typeof meta>;
|
|
1965
|
+
|
|
1966
|
+
export const Default: Story = {};
|
|
1967
|
+
export const Vertical: Story = { args: { orientation: 'vertical' } };
|
|
1968
|
+
export const Primary: Story = { args: { variant: 'primary' } };
|
|
1969
|
+
`,
|
|
1970
|
+
};
|
|
1971
|
+
// ---------------------------------------------------------------------------
|
|
1972
|
+
// Vue story generator
|
|
1973
|
+
// ---------------------------------------------------------------------------
|
|
1974
|
+
export function generateVueStory(name) {
|
|
1975
|
+
// Return full override if one exists for this component
|
|
1976
|
+
if (VUE_STORY_OVERRIDES[name]) {
|
|
1977
|
+
return STORY_FILE_HEADER + VUE_STORY_OVERRIDES[name];
|
|
1978
|
+
}
|
|
1979
|
+
// Flex override: Vue component is VueFlexRow in VueFlexRow.vue
|
|
1980
|
+
const vueComponentName = name === 'Flex' ? 'VueFlexRow' : `Vue${name}`;
|
|
1981
|
+
const vueFileName = name === 'Flex' ? 'VueFlexRow' : `Vue${name}`;
|
|
1982
|
+
const lines = [];
|
|
1983
|
+
// Imports
|
|
1984
|
+
lines.push(`import type { Meta, StoryObj } from '@storybook/vue3';`);
|
|
1985
|
+
if (OPEN_CONTROLLED_COMPONENTS.has(name)) {
|
|
1986
|
+
lines.push(`import { ref } from 'vue';`);
|
|
1987
|
+
}
|
|
1988
|
+
if (name === 'Dialog') {
|
|
1989
|
+
lines.push(`import Vue${name} from './Vue${name}.vue';`);
|
|
1990
|
+
lines.push(`import VueButton from '../../Button/vue/VueButton.vue';`);
|
|
1991
|
+
}
|
|
1992
|
+
else if (name === 'Drawer') {
|
|
1993
|
+
lines.push(`import Vue${name} from './Vue${name}.vue';`);
|
|
1994
|
+
lines.push(`import VueButton from '../../Button/vue/VueButton.vue';`);
|
|
1995
|
+
}
|
|
1996
|
+
else if (name === 'Toast') {
|
|
1997
|
+
lines.push(`import Vue${name} from './Vue${name}.vue';`);
|
|
1998
|
+
lines.push(`import VueButton from '../../Button/vue/VueButton.vue';`);
|
|
1999
|
+
}
|
|
2000
|
+
else if (name === 'Collapsible') {
|
|
2001
|
+
lines.push(`import Vue${name} from './Vue${name}.vue';`);
|
|
2002
|
+
}
|
|
2003
|
+
else if (REQUIRED_ARRAY_COMPONENTS.has(name)) {
|
|
2004
|
+
lines.push(`import ${vueComponentName} from './${vueFileName}.vue';`);
|
|
2005
|
+
lines.push(`import type { ComboboxOption } from '../core/${name}';`);
|
|
2006
|
+
}
|
|
2007
|
+
else if (VUE_INDEX_EXPORTS[name]) {
|
|
2008
|
+
// Component uses non-standard file name — import via named export from index
|
|
2009
|
+
lines.push(`import { ${VUE_INDEX_EXPORTS[name]} } from './index';`);
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
lines.push(`import ${vueComponentName} from './${vueFileName}.vue';`);
|
|
2013
|
+
}
|
|
2014
|
+
lines.push('');
|
|
2015
|
+
// Sample data for Combobox
|
|
2016
|
+
if (name === 'Combobox') {
|
|
2017
|
+
lines.push(`const FRUITS: ComboboxOption[] = [`);
|
|
2018
|
+
lines.push(` { value: 'apple', label: 'Apple' },`);
|
|
2019
|
+
lines.push(` { value: 'banana', label: 'Banana' },`);
|
|
2020
|
+
lines.push(` { value: 'cherry', label: 'Cherry' },`);
|
|
2021
|
+
lines.push(` { value: 'grape', label: 'Grape' },`);
|
|
2022
|
+
lines.push(` { value: 'mango', label: 'Mango' },`);
|
|
2023
|
+
lines.push(` { value: 'orange', label: 'Orange' },`);
|
|
2024
|
+
lines.push(`];`);
|
|
2025
|
+
lines.push('');
|
|
2026
|
+
}
|
|
2027
|
+
const argTypes = ARGTYPES[name] ?? '';
|
|
2028
|
+
// Build meta args for Vue
|
|
2029
|
+
let metaArgs = '';
|
|
2030
|
+
if (name === 'Dialog') {
|
|
2031
|
+
metaArgs = ` args: {
|
|
2032
|
+
heading: 'Dialog title',
|
|
2033
|
+
description: 'Supporting description text goes here.',
|
|
2034
|
+
showCloseButton: true,
|
|
2035
|
+
},`;
|
|
2036
|
+
}
|
|
2037
|
+
else if (name === 'Drawer') {
|
|
2038
|
+
metaArgs = ` args: {
|
|
2039
|
+
heading: 'Drawer',
|
|
2040
|
+
showCloseButton: true,
|
|
2041
|
+
},`;
|
|
2042
|
+
}
|
|
2043
|
+
else if (name === 'Combobox') {
|
|
2044
|
+
metaArgs = ` args: {
|
|
2045
|
+
options: FRUITS,
|
|
2046
|
+
label: 'Select a fruit',
|
|
2047
|
+
placeholder: 'Choose...',
|
|
2048
|
+
id: 'combobox-default',
|
|
2049
|
+
},`;
|
|
2050
|
+
}
|
|
2051
|
+
else if (FX_COMPONENTS.has(name)) {
|
|
2052
|
+
const defaultLabel = name === 'BadgeFx' ? 'BadgeFx' : name === 'ButtonFx' ? 'Click me' : name;
|
|
2053
|
+
metaArgs = ` args: {
|
|
2054
|
+
fx: 'bounce',
|
|
2055
|
+
default: '${defaultLabel}',
|
|
2056
|
+
},`;
|
|
2057
|
+
}
|
|
2058
|
+
else if (TEXT_CHILD_COMPONENTS.has(name)) {
|
|
2059
|
+
// Use args.default for slot text
|
|
2060
|
+
metaArgs = ` args: {
|
|
2061
|
+
default: '${name}',
|
|
2062
|
+
},`;
|
|
2063
|
+
}
|
|
2064
|
+
// Build render for meta (only for non-open-controlled, non-collapsible)
|
|
2065
|
+
let metaRender = '';
|
|
2066
|
+
if (!OPEN_CONTROLLED_COMPONENTS.has(name)) {
|
|
2067
|
+
if (TEXT_CHILD_COMPONENTS.has(name) || FX_COMPONENTS.has(name)) {
|
|
2068
|
+
metaRender = ` render: (args: any) => ({
|
|
2069
|
+
components: { ${vueComponentName} },
|
|
2070
|
+
setup() { return { args }; },
|
|
2071
|
+
template: '<${vueComponentName} v-bind="args">{{ args.default }}</${vueComponentName}>',
|
|
2072
|
+
}),`;
|
|
2073
|
+
}
|
|
2074
|
+
else {
|
|
2075
|
+
metaRender = ` render: (args: any) => ({
|
|
2076
|
+
components: { ${vueComponentName} },
|
|
2077
|
+
setup() { return { args }; },
|
|
2078
|
+
template: '<${vueComponentName} v-bind="args" />',
|
|
2079
|
+
}),`;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
const metaParts = [];
|
|
2083
|
+
metaParts.push(` title: 'AgnosticUI/${name}',`);
|
|
2084
|
+
metaParts.push(` component: ${vueComponentName},`);
|
|
2085
|
+
metaParts.push(` tags: ['autodocs'],`);
|
|
2086
|
+
if (argTypes) {
|
|
2087
|
+
metaParts.push(argTypes);
|
|
2088
|
+
}
|
|
2089
|
+
if (metaArgs) {
|
|
2090
|
+
metaParts.push(metaArgs);
|
|
2091
|
+
}
|
|
2092
|
+
if (metaRender) {
|
|
2093
|
+
metaParts.push(metaRender);
|
|
2094
|
+
}
|
|
2095
|
+
lines.push(`const meta = {`);
|
|
2096
|
+
lines.push(metaParts.join('\n'));
|
|
2097
|
+
lines.push(`} satisfies Meta<typeof ${vueComponentName}>;`);
|
|
2098
|
+
lines.push('');
|
|
2099
|
+
lines.push(`export default meta;`);
|
|
2100
|
+
lines.push(`type Story = StoryObj<typeof meta>;`);
|
|
2101
|
+
lines.push('');
|
|
2102
|
+
// Default story
|
|
2103
|
+
if (name === 'Dialog') {
|
|
2104
|
+
lines.push(`export const Default: Story = {`);
|
|
2105
|
+
lines.push(` render: (args: any) => ({`);
|
|
2106
|
+
lines.push(` components: { VueDialog, VueButton },`);
|
|
2107
|
+
lines.push(` setup() {`);
|
|
2108
|
+
lines.push(` const open = ref(false);`);
|
|
2109
|
+
lines.push(` return { args, open };`);
|
|
2110
|
+
lines.push(` },`);
|
|
2111
|
+
lines.push(` template: \``);
|
|
2112
|
+
lines.push(` <div>`);
|
|
2113
|
+
lines.push(` <VueButton variant="primary" @click="open = true">Open Dialog</VueButton>`);
|
|
2114
|
+
lines.push(` <VueDialog v-bind="args" v-model:open="open">`);
|
|
2115
|
+
lines.push(` <p>Dialog content goes here.</p>`);
|
|
2116
|
+
lines.push(` </VueDialog>`);
|
|
2117
|
+
lines.push(` </div>`);
|
|
2118
|
+
lines.push(` \`,`);
|
|
2119
|
+
lines.push(` }),`);
|
|
2120
|
+
lines.push(`};`);
|
|
2121
|
+
}
|
|
2122
|
+
else if (name === 'Drawer') {
|
|
2123
|
+
lines.push(`export const Default: Story = {`);
|
|
2124
|
+
lines.push(` render: (args: any) => ({`);
|
|
2125
|
+
lines.push(` components: { VueDrawer, VueButton },`);
|
|
2126
|
+
lines.push(` setup() {`);
|
|
2127
|
+
lines.push(` const open = ref(false);`);
|
|
2128
|
+
lines.push(` return { args, open };`);
|
|
2129
|
+
lines.push(` },`);
|
|
2130
|
+
lines.push(` template: \``);
|
|
2131
|
+
lines.push(` <div>`);
|
|
2132
|
+
lines.push(` <VueButton variant="primary" @click="open = true">Open Drawer</VueButton>`);
|
|
2133
|
+
lines.push(` <VueDrawer v-bind="args" v-model:open="open">`);
|
|
2134
|
+
lines.push(` <div style="padding: 1rem">Drawer content goes here.</div>`);
|
|
2135
|
+
lines.push(` </VueDrawer>`);
|
|
2136
|
+
lines.push(` </div>`);
|
|
2137
|
+
lines.push(` \`,`);
|
|
2138
|
+
lines.push(` }),`);
|
|
2139
|
+
lines.push(`};`);
|
|
2140
|
+
}
|
|
2141
|
+
else if (name === 'Toast') {
|
|
2142
|
+
lines.push(`export const Default: Story = {`);
|
|
2143
|
+
lines.push(` render: (args: any) => ({`);
|
|
2144
|
+
lines.push(` components: { VueToast, VueButton },`);
|
|
2145
|
+
lines.push(` setup() {`);
|
|
2146
|
+
lines.push(` const open = ref(false);`);
|
|
2147
|
+
lines.push(` return { args, open };`);
|
|
2148
|
+
lines.push(` },`);
|
|
2149
|
+
lines.push(` template: \``);
|
|
2150
|
+
lines.push(` <div>`);
|
|
2151
|
+
lines.push(` <VueButton variant="primary" @click="open = true">Show Toast</VueButton>`);
|
|
2152
|
+
lines.push(` <VueToast v-bind="args" v-model:open="open">`);
|
|
2153
|
+
lines.push(` <span>Toast notification message</span>`);
|
|
2154
|
+
lines.push(` </VueToast>`);
|
|
2155
|
+
lines.push(` </div>`);
|
|
2156
|
+
lines.push(` \`,`);
|
|
2157
|
+
lines.push(` }),`);
|
|
2158
|
+
lines.push(`};`);
|
|
2159
|
+
}
|
|
2160
|
+
else if (name === 'Collapsible') {
|
|
2161
|
+
lines.push(`export const Default: Story = {`);
|
|
2162
|
+
lines.push(` render: () => ({`);
|
|
2163
|
+
lines.push(` components: { VueCollapsible },`);
|
|
2164
|
+
lines.push(` template: \``);
|
|
2165
|
+
lines.push(` <VueCollapsible>`);
|
|
2166
|
+
lines.push(` <template #summary>Toggle details</template>`);
|
|
2167
|
+
lines.push(` <div>Collapsible content goes here.</div>`);
|
|
2168
|
+
lines.push(` </VueCollapsible>`);
|
|
2169
|
+
lines.push(` \`,`);
|
|
2170
|
+
lines.push(` }),`);
|
|
2171
|
+
lines.push(`};`);
|
|
2172
|
+
}
|
|
2173
|
+
else if (name === 'Button') {
|
|
2174
|
+
lines.push(`export const Default: Story = {};`);
|
|
2175
|
+
lines.push('');
|
|
2176
|
+
lines.push(`export const Variants: Story = {`);
|
|
2177
|
+
lines.push(` render: () => ({`);
|
|
2178
|
+
lines.push(` components: { VueButton },`);
|
|
2179
|
+
lines.push(` template: \``);
|
|
2180
|
+
lines.push(` <div style="display:flex;gap:8px;flex-wrap:wrap">`);
|
|
2181
|
+
lines.push(` <VueButton>Default</VueButton>`);
|
|
2182
|
+
lines.push(` <VueButton variant="primary">Primary</VueButton>`);
|
|
2183
|
+
lines.push(` <VueButton variant="secondary">Secondary</VueButton>`);
|
|
2184
|
+
lines.push(` <VueButton variant="success">Success</VueButton>`);
|
|
2185
|
+
lines.push(` <VueButton variant="warning">Warning</VueButton>`);
|
|
2186
|
+
lines.push(` <VueButton variant="danger">Danger</VueButton>`);
|
|
2187
|
+
lines.push(` <VueButton variant="monochrome">Monochrome</VueButton>`);
|
|
2188
|
+
lines.push(` </div>`);
|
|
2189
|
+
lines.push(` \`,`);
|
|
2190
|
+
lines.push(` }),`);
|
|
2191
|
+
lines.push(`};`);
|
|
2192
|
+
lines.push('');
|
|
2193
|
+
lines.push(`export const Sizes: Story = {`);
|
|
2194
|
+
lines.push(` render: () => ({`);
|
|
2195
|
+
lines.push(` components: { VueButton },`);
|
|
2196
|
+
lines.push(` template: \``);
|
|
2197
|
+
lines.push(` <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">`);
|
|
2198
|
+
lines.push(` <VueButton size="x-sm">x-sm</VueButton>`);
|
|
2199
|
+
lines.push(` <VueButton size="sm">sm</VueButton>`);
|
|
2200
|
+
lines.push(` <VueButton size="md">md</VueButton>`);
|
|
2201
|
+
lines.push(` <VueButton size="lg">lg</VueButton>`);
|
|
2202
|
+
lines.push(` <VueButton size="xl">xl</VueButton>`);
|
|
2203
|
+
lines.push(` </div>`);
|
|
2204
|
+
lines.push(` \`,`);
|
|
2205
|
+
lines.push(` }),`);
|
|
2206
|
+
lines.push(`};`);
|
|
2207
|
+
lines.push('');
|
|
2208
|
+
lines.push(`export const Bordered: Story = {`);
|
|
2209
|
+
lines.push(` render: () => ({`);
|
|
2210
|
+
lines.push(` components: { VueButton },`);
|
|
2211
|
+
lines.push(` template: \``);
|
|
2212
|
+
lines.push(` <div style="display:flex;gap:8px;flex-wrap:wrap">`);
|
|
2213
|
+
lines.push(` <VueButton bordered>Default</VueButton>`);
|
|
2214
|
+
lines.push(` <VueButton variant="primary" bordered>Primary</VueButton>`);
|
|
2215
|
+
lines.push(` <VueButton variant="success" bordered>Success</VueButton>`);
|
|
2216
|
+
lines.push(` <VueButton variant="danger" bordered>Danger</VueButton>`);
|
|
2217
|
+
lines.push(` </div>`);
|
|
2218
|
+
lines.push(` \`,`);
|
|
2219
|
+
lines.push(` }),`);
|
|
2220
|
+
lines.push(`};`);
|
|
2221
|
+
lines.push('');
|
|
2222
|
+
lines.push(`export const Disabled: Story = { args: { variant: 'primary', disabled: true } };`);
|
|
2223
|
+
lines.push(`export const Loading: Story = { args: { variant: 'primary', loading: true } };`);
|
|
2224
|
+
}
|
|
2225
|
+
else {
|
|
2226
|
+
lines.push(`export const Default: Story = {};`);
|
|
2227
|
+
}
|
|
2228
|
+
return STORY_FILE_HEADER + lines.join('\n') + '\n';
|
|
2229
|
+
}
|
|
2230
|
+
// ---------------------------------------------------------------------------
|
|
2231
|
+
// Lit (web-components) story generator
|
|
2232
|
+
// ---------------------------------------------------------------------------
|
|
2233
|
+
function toKebabCase(str) {
|
|
2234
|
+
return str.replace(/([A-Z])/g, (_m, p1, offset) => (offset > 0 ? '-' : '') + p1.toLowerCase());
|
|
2235
|
+
}
|
|
2236
|
+
function getAgTagName(name) {
|
|
2237
|
+
// Special overrides
|
|
2238
|
+
if (name === 'Flex')
|
|
2239
|
+
return 'ag-flex-row';
|
|
2240
|
+
return `ag-${toKebabCase(name)}`;
|
|
2241
|
+
}
|
|
2242
|
+
// ---------------------------------------------------------------------------
|
|
2243
|
+
// Lit story overrides — components that need custom slot content or imports
|
|
2244
|
+
// ---------------------------------------------------------------------------
|
|
2245
|
+
const LIT_STORY_OVERRIDES = {
|
|
2246
|
+
Flex: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2247
|
+
import { html } from 'lit';
|
|
2248
|
+
import './index';
|
|
2249
|
+
|
|
2250
|
+
const meta: Meta = {
|
|
2251
|
+
title: 'AgnosticUI/Flex',
|
|
2252
|
+
component: 'ag-flex-row',
|
|
2253
|
+
tags: ['autodocs'],
|
|
2254
|
+
render: () => html\`
|
|
2255
|
+
<ag-flex-row style="--flex-gap: 1rem">
|
|
2256
|
+
<div style="padding: 1rem; background: #e0e7ff; border: 1px solid #6366f1;">Item 1</div>
|
|
2257
|
+
<div style="padding: 1rem; background: #dbeafe; border: 1px solid #3b82f6;">Item 2</div>
|
|
2258
|
+
<div style="padding: 1rem; background: #ddd6fe; border: 1px solid #8b5cf6;">Item 3</div>
|
|
2259
|
+
</ag-flex-row>
|
|
2260
|
+
\`,
|
|
2261
|
+
} satisfies Meta;
|
|
2262
|
+
|
|
2263
|
+
export default meta;
|
|
2264
|
+
type Story = StoryObj;
|
|
2265
|
+
|
|
2266
|
+
export const Default: Story = {};
|
|
2267
|
+
|
|
2268
|
+
export const Column: Story = {
|
|
2269
|
+
render: () => html\`
|
|
2270
|
+
<ag-flex-col style="--flex-gap: 1rem">
|
|
2271
|
+
<div style="padding: 1rem; background: #fef3c7; border: 1px solid #f59e0b;">Row 1</div>
|
|
2272
|
+
<div style="padding: 1rem; background: #fed7aa; border: 1px solid #ea580c;">Row 2</div>
|
|
2273
|
+
<div style="padding: 1rem; background: #fecaca; border: 1px solid #ef4444;">Row 3</div>
|
|
2274
|
+
</ag-flex-col>
|
|
2275
|
+
\`,
|
|
2276
|
+
};
|
|
2277
|
+
`,
|
|
2278
|
+
Header: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2279
|
+
import { html } from 'lit';
|
|
2280
|
+
import './Header';
|
|
2281
|
+
|
|
2282
|
+
const meta: Meta = {
|
|
2283
|
+
title: 'AgnosticUI/Header',
|
|
2284
|
+
component: 'ag-header',
|
|
2285
|
+
tags: ['autodocs'],
|
|
2286
|
+
render: () => html\`
|
|
2287
|
+
<ag-header>
|
|
2288
|
+
<a href="#" slot="logo" style="text-decoration: none; color: inherit; font-weight: 700; font-size: 1.25rem;">
|
|
2289
|
+
AgnosticUI
|
|
2290
|
+
</a>
|
|
2291
|
+
<nav>
|
|
2292
|
+
<ul style="display: flex; gap: 2rem; list-style: none; margin: 0; padding: 0;">
|
|
2293
|
+
<li><a href="#home" style="text-decoration: none; color: inherit;">Home</a></li>
|
|
2294
|
+
<li><a href="#about" style="text-decoration: none; color: inherit;">About</a></li>
|
|
2295
|
+
<li><a href="#docs" style="text-decoration: none; color: inherit;">Docs</a></li>
|
|
2296
|
+
</ul>
|
|
2297
|
+
</nav>
|
|
2298
|
+
</ag-header>
|
|
2299
|
+
\`,
|
|
2300
|
+
} satisfies Meta;
|
|
2301
|
+
|
|
2302
|
+
export default meta;
|
|
2303
|
+
type Story = StoryObj;
|
|
2304
|
+
|
|
2305
|
+
export const Default: Story = {};
|
|
2306
|
+
|
|
2307
|
+
export const WithLogo: Story = {
|
|
2308
|
+
render: () => html\`
|
|
2309
|
+
<ag-header>
|
|
2310
|
+
<a href="#" slot="logo" style="display: flex; align-items: center; gap: 0.5rem; text-decoration: none; color: inherit;">
|
|
2311
|
+
<svg width="28" height="28" viewBox="0 0 32 32">
|
|
2312
|
+
<circle cx="16" cy="16" r="14" fill="var(--ag-primary, #6366f1)" />
|
|
2313
|
+
<text x="16" y="22" text-anchor="middle" fill="white" font-size="16" font-weight="bold">A</text>
|
|
2314
|
+
</svg>
|
|
2315
|
+
<span style="font-weight: 700; font-size: 1.25rem;">MyApp</span>
|
|
2316
|
+
</a>
|
|
2317
|
+
<nav>
|
|
2318
|
+
<ul style="display: flex; gap: 2rem; list-style: none; margin: 0; padding: 0;">
|
|
2319
|
+
<li><a href="#home" style="text-decoration: none; color: inherit;">Home</a></li>
|
|
2320
|
+
<li><a href="#features" style="text-decoration: none; color: inherit;">Features</a></li>
|
|
2321
|
+
<li><a href="#pricing" style="text-decoration: none; color: inherit;">Pricing</a></li>
|
|
2322
|
+
</ul>
|
|
2323
|
+
</nav>
|
|
2324
|
+
</ag-header>
|
|
2325
|
+
\`,
|
|
2326
|
+
};
|
|
2327
|
+
`,
|
|
2328
|
+
Image: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2329
|
+
import { html } from 'lit';
|
|
2330
|
+
import './Image';
|
|
2331
|
+
|
|
2332
|
+
const meta: Meta = {
|
|
2333
|
+
title: 'AgnosticUI/Image',
|
|
2334
|
+
component: 'ag-image',
|
|
2335
|
+
tags: ['autodocs'],
|
|
2336
|
+
render: () => html\`
|
|
2337
|
+
<ag-image
|
|
2338
|
+
.src=\${'https://picsum.photos/800/400'}
|
|
2339
|
+
.alt=\${'Sample landscape photo'}
|
|
2340
|
+
.width=\${800}
|
|
2341
|
+
.height=\${400}
|
|
2342
|
+
></ag-image>
|
|
2343
|
+
\`,
|
|
2344
|
+
} satisfies Meta;
|
|
2345
|
+
|
|
2346
|
+
export default meta;
|
|
2347
|
+
type Story = StoryObj;
|
|
2348
|
+
|
|
2349
|
+
export const Default: Story = {};
|
|
2350
|
+
|
|
2351
|
+
export const WithFade: Story = {
|
|
2352
|
+
render: () => html\`
|
|
2353
|
+
<ag-image
|
|
2354
|
+
.src=\${'https://picsum.photos/600/400?random=2'}
|
|
2355
|
+
.alt=\${'Fading image'}
|
|
2356
|
+
.width=\${600}
|
|
2357
|
+
.height=\${400}
|
|
2358
|
+
.fade=\${true}
|
|
2359
|
+
.duration=\${500}
|
|
2360
|
+
></ag-image>
|
|
2361
|
+
\`,
|
|
2362
|
+
};
|
|
2363
|
+
|
|
2364
|
+
export const WithAspectRatio: Story = {
|
|
2365
|
+
render: () => html\`
|
|
2366
|
+
<ag-image
|
|
2367
|
+
.src=\${'https://picsum.photos/1200/675?random=3'}
|
|
2368
|
+
.alt=\${'Wide image'}
|
|
2369
|
+
.aspectRatio=\${'16/9'}
|
|
2370
|
+
.fit=\${'cover'}
|
|
2371
|
+
style="width: 100%; max-width: 800px; display: block;"
|
|
2372
|
+
></ag-image>
|
|
2373
|
+
\`,
|
|
2374
|
+
};
|
|
2375
|
+
`,
|
|
2376
|
+
Select: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2377
|
+
import { html } from 'lit';
|
|
2378
|
+
import './Select';
|
|
2379
|
+
|
|
2380
|
+
const meta: Meta = {
|
|
2381
|
+
title: 'AgnosticUI/Select',
|
|
2382
|
+
component: 'ag-select',
|
|
2383
|
+
tags: ['autodocs'],
|
|
2384
|
+
argTypes: {
|
|
2385
|
+
size: { control: 'select', options: ['', 'small', 'large'] },
|
|
2386
|
+
disabled: { control: 'boolean' },
|
|
2387
|
+
multiple: { control: 'boolean' },
|
|
2388
|
+
invalid: { control: 'boolean' },
|
|
2389
|
+
required: { control: 'boolean' },
|
|
2390
|
+
},
|
|
2391
|
+
render: (args: any) => html\`
|
|
2392
|
+
<div style="max-width: 300px;">
|
|
2393
|
+
<label for="player-select" style="display: block; margin-bottom: 0.5rem; font-weight: 600;">
|
|
2394
|
+
Greatest Tennis Player
|
|
2395
|
+
</label>
|
|
2396
|
+
<ag-select
|
|
2397
|
+
id="player-select"
|
|
2398
|
+
name="tennis"
|
|
2399
|
+
.size=\${args.size || ''}
|
|
2400
|
+
?disabled=\${args.disabled}
|
|
2401
|
+
?multiple=\${args.multiple}
|
|
2402
|
+
?invalid=\${args.invalid}
|
|
2403
|
+
?required=\${args.required}
|
|
2404
|
+
>
|
|
2405
|
+
<option value="">- Select a player -</option>
|
|
2406
|
+
<option value="federer">Roger Federer</option>
|
|
2407
|
+
<option value="nadal">Rafael Nadal</option>
|
|
2408
|
+
<option value="djokovic">Novak Djokovic</option>
|
|
2409
|
+
<option value="agassi">Andre Agassi</option>
|
|
2410
|
+
<option value="sampras">Pete Sampras</option>
|
|
2411
|
+
</ag-select>
|
|
2412
|
+
</div>
|
|
2413
|
+
\`,
|
|
2414
|
+
} satisfies Meta;
|
|
2415
|
+
|
|
2416
|
+
export default meta;
|
|
2417
|
+
type Story = StoryObj;
|
|
2418
|
+
|
|
2419
|
+
export const Default: Story = {};
|
|
2420
|
+
|
|
2421
|
+
export const Multiple: Story = {
|
|
2422
|
+
render: () => html\`
|
|
2423
|
+
<div style="max-width: 300px;">
|
|
2424
|
+
<label style="display: block; margin-bottom: 0.5rem; font-weight: 600;">Multiple Select</label>
|
|
2425
|
+
<ag-select name="players" .multiple=\${true} .multipleSize=\${4}>
|
|
2426
|
+
<option value="federer">Roger Federer</option>
|
|
2427
|
+
<option value="nadal">Rafael Nadal</option>
|
|
2428
|
+
<option value="djokovic">Novak Djokovic</option>
|
|
2429
|
+
<option value="agassi">Andre Agassi</option>
|
|
2430
|
+
</ag-select>
|
|
2431
|
+
</div>
|
|
2432
|
+
\`,
|
|
2433
|
+
};
|
|
2434
|
+
`,
|
|
2435
|
+
Progress: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2436
|
+
import { html } from 'lit';
|
|
2437
|
+
import './Progress';
|
|
2438
|
+
|
|
2439
|
+
const meta: Meta = {
|
|
2440
|
+
title: 'AgnosticUI/Progress',
|
|
2441
|
+
component: 'ag-progress',
|
|
2442
|
+
tags: ['autodocs'],
|
|
2443
|
+
argTypes: {
|
|
2444
|
+
value: { control: { type: 'number', min: 0, max: 100 } },
|
|
2445
|
+
max: { control: { type: 'number' } },
|
|
2446
|
+
label: { control: 'text' },
|
|
2447
|
+
},
|
|
2448
|
+
args: {
|
|
2449
|
+
value: 60,
|
|
2450
|
+
max: 100,
|
|
2451
|
+
label: 'Loading progress',
|
|
2452
|
+
},
|
|
2453
|
+
render: (args: any) => html\`
|
|
2454
|
+
<ag-progress
|
|
2455
|
+
.value=\${args.value}
|
|
2456
|
+
.max=\${args.max}
|
|
2457
|
+
.label=\${args.label}
|
|
2458
|
+
></ag-progress>
|
|
2459
|
+
\`,
|
|
2460
|
+
} satisfies Meta;
|
|
2461
|
+
|
|
2462
|
+
export default meta;
|
|
2463
|
+
type Story = StoryObj;
|
|
2464
|
+
|
|
2465
|
+
export const Default: Story = {};
|
|
2466
|
+
|
|
2467
|
+
export const Variants: Story = {
|
|
2468
|
+
render: () => html\`
|
|
2469
|
+
<div style="display: flex; flex-direction: column; gap: 1.5rem; max-width: 500px;">
|
|
2470
|
+
<ag-progress .value=\${25} .max=\${100} .label=\${'25%'}></ag-progress>
|
|
2471
|
+
<ag-progress .value=\${50} .max=\${100} .label=\${'50%'}></ag-progress>
|
|
2472
|
+
<ag-progress .value=\${75} .max=\${100} .label=\${'75%'}></ag-progress>
|
|
2473
|
+
<ag-progress .value=\${100} .max=\${100} .label=\${'Complete'}></ag-progress>
|
|
2474
|
+
</div>
|
|
2475
|
+
\`,
|
|
2476
|
+
};
|
|
2477
|
+
`,
|
|
2478
|
+
ProgressRing: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2479
|
+
import { html } from 'lit';
|
|
2480
|
+
import './ProgressRing';
|
|
2481
|
+
|
|
2482
|
+
const meta: Meta = {
|
|
2483
|
+
title: 'AgnosticUI/ProgressRing',
|
|
2484
|
+
component: 'ag-progress-ring',
|
|
2485
|
+
tags: ['autodocs'],
|
|
2486
|
+
argTypes: {
|
|
2487
|
+
value: { control: { type: 'range', min: 0, max: 100, step: 1 } },
|
|
2488
|
+
size: { control: 'select', options: ['small', 'medium', 'large'] },
|
|
2489
|
+
variant: { control: 'select', options: ['primary', 'success', 'warning', 'danger', 'info'] },
|
|
2490
|
+
label: { control: 'text' },
|
|
2491
|
+
},
|
|
2492
|
+
args: {
|
|
2493
|
+
value: 65,
|
|
2494
|
+
size: 'medium',
|
|
2495
|
+
variant: 'primary',
|
|
2496
|
+
label: 'Progress',
|
|
2497
|
+
},
|
|
2498
|
+
render: (args: any) => html\`
|
|
2499
|
+
<ag-progress-ring
|
|
2500
|
+
.value=\${args.value}
|
|
2501
|
+
.size=\${args.size}
|
|
2502
|
+
.variant=\${args.variant}
|
|
2503
|
+
.label=\${args.label}
|
|
2504
|
+
></ag-progress-ring>
|
|
2505
|
+
\`,
|
|
2506
|
+
} satisfies Meta;
|
|
2507
|
+
|
|
2508
|
+
export default meta;
|
|
2509
|
+
type Story = StoryObj;
|
|
2510
|
+
|
|
2511
|
+
export const Default: Story = {};
|
|
2512
|
+
|
|
2513
|
+
export const Variants: Story = {
|
|
2514
|
+
render: () => html\`
|
|
2515
|
+
<div style="display: flex; gap: 2rem; flex-wrap: wrap; align-items: center;">
|
|
2516
|
+
<ag-progress-ring .value=\${65} size="medium" variant="primary" label="Primary"></ag-progress-ring>
|
|
2517
|
+
<ag-progress-ring .value=\${80} size="medium" variant="success" label="Success"></ag-progress-ring>
|
|
2518
|
+
<ag-progress-ring .value=\${45} size="medium" variant="warning" label="Warning"></ag-progress-ring>
|
|
2519
|
+
<ag-progress-ring .value=\${30} size="medium" variant="danger" label="Danger"></ag-progress-ring>
|
|
2520
|
+
</div>
|
|
2521
|
+
\`,
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2524
|
+
export const Sizes: Story = {
|
|
2525
|
+
render: () => html\`
|
|
2526
|
+
<div style="display: flex; gap: 2rem; align-items: center;">
|
|
2527
|
+
<ag-progress-ring .value=\${65} size="small" variant="primary" label="Small"></ag-progress-ring>
|
|
2528
|
+
<ag-progress-ring .value=\${65} size="medium" variant="primary" label="Medium"></ag-progress-ring>
|
|
2529
|
+
<ag-progress-ring .value=\${65} size="large" variant="primary" label="Large"></ag-progress-ring>
|
|
2530
|
+
</div>
|
|
2531
|
+
\`,
|
|
2532
|
+
};
|
|
2533
|
+
`,
|
|
2534
|
+
Accordion: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2535
|
+
import { html } from 'lit';
|
|
2536
|
+
import './Accordion';
|
|
2537
|
+
|
|
2538
|
+
const meta: Meta = {
|
|
2539
|
+
title: 'AgnosticUI/Accordion',
|
|
2540
|
+
component: 'ag-accordion',
|
|
2541
|
+
tags: ['autodocs'],
|
|
2542
|
+
render: () => html\`
|
|
2543
|
+
<ag-accordion style="max-width: 600px;">
|
|
2544
|
+
<ag-accordion-item .open=\${true}>
|
|
2545
|
+
<span slot="header">Getting Started</span>
|
|
2546
|
+
<div slot="content">
|
|
2547
|
+
<p>AgnosticUI is a component library that works across React, Vue, and Lit. Install it with the CLI and add components to your project.</p>
|
|
2548
|
+
</div>
|
|
2549
|
+
</ag-accordion-item>
|
|
2550
|
+
<ag-accordion-item>
|
|
2551
|
+
<span slot="header">Configuration</span>
|
|
2552
|
+
<div slot="content">
|
|
2553
|
+
<p>Configure your project by editing the agnosticui.config.json file in your project root. Add components as needed.</p>
|
|
2554
|
+
</div>
|
|
2555
|
+
</ag-accordion-item>
|
|
2556
|
+
<ag-accordion-item>
|
|
2557
|
+
<span slot="header">Theming</span>
|
|
2558
|
+
<div slot="content">
|
|
2559
|
+
<p>Customize the appearance using CSS custom properties. Override tokens in your stylesheet to match your brand.</p>
|
|
2560
|
+
</div>
|
|
2561
|
+
</ag-accordion-item>
|
|
2562
|
+
</ag-accordion>
|
|
2563
|
+
\`,
|
|
2564
|
+
} satisfies Meta;
|
|
2565
|
+
|
|
2566
|
+
export default meta;
|
|
2567
|
+
type Story = StoryObj;
|
|
2568
|
+
|
|
2569
|
+
export const Default: Story = {};
|
|
2570
|
+
|
|
2571
|
+
export const Bordered: Story = {
|
|
2572
|
+
render: () => html\`
|
|
2573
|
+
<ag-accordion style="max-width: 600px;">
|
|
2574
|
+
<ag-accordion-item .open=\${true} .bordered=\${true}>
|
|
2575
|
+
<span slot="header">Section One</span>
|
|
2576
|
+
<div slot="content"><p>Content for section one goes here.</p></div>
|
|
2577
|
+
</ag-accordion-item>
|
|
2578
|
+
<ag-accordion-item .bordered=\${true}>
|
|
2579
|
+
<span slot="header">Section Two</span>
|
|
2580
|
+
<div slot="content"><p>Content for section two goes here.</p></div>
|
|
2581
|
+
</ag-accordion-item>
|
|
2582
|
+
</ag-accordion>
|
|
2583
|
+
\`,
|
|
2584
|
+
};
|
|
2585
|
+
`,
|
|
2586
|
+
Breadcrumb: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2587
|
+
import { html } from 'lit';
|
|
2588
|
+
import './Breadcrumb';
|
|
2589
|
+
|
|
2590
|
+
const ITEMS = [
|
|
2591
|
+
{ label: 'Home', href: '#' },
|
|
2592
|
+
{ label: 'Products', href: '#products' },
|
|
2593
|
+
{ label: 'Electronics', href: '#electronics' },
|
|
2594
|
+
{ label: 'Laptops', current: true },
|
|
2595
|
+
];
|
|
2596
|
+
|
|
2597
|
+
const meta: Meta = {
|
|
2598
|
+
title: 'AgnosticUI/Breadcrumb',
|
|
2599
|
+
component: 'ag-breadcrumb',
|
|
2600
|
+
tags: ['autodocs'],
|
|
2601
|
+
argTypes: {
|
|
2602
|
+
type: { control: 'select', options: ['default', 'slash', 'bullet', 'arrow'] },
|
|
2603
|
+
primary: { control: 'boolean' },
|
|
2604
|
+
},
|
|
2605
|
+
args: {
|
|
2606
|
+
type: 'default',
|
|
2607
|
+
primary: false,
|
|
2608
|
+
},
|
|
2609
|
+
render: (args: any) => html\`
|
|
2610
|
+
<ag-breadcrumb
|
|
2611
|
+
.items=\${ITEMS}
|
|
2612
|
+
.type=\${args.type}
|
|
2613
|
+
?primary=\${args.primary}
|
|
2614
|
+
></ag-breadcrumb>
|
|
2615
|
+
\`,
|
|
2616
|
+
} satisfies Meta;
|
|
2617
|
+
|
|
2618
|
+
export default meta;
|
|
2619
|
+
type Story = StoryObj;
|
|
2620
|
+
|
|
2621
|
+
export const Default: Story = {};
|
|
2622
|
+
|
|
2623
|
+
export const Slash: Story = { args: { type: 'slash' } };
|
|
2624
|
+
export const Arrow: Story = { args: { type: 'arrow' } };
|
|
2625
|
+
export const Bullet: Story = { args: { type: 'bullet' } };
|
|
2626
|
+
`,
|
|
2627
|
+
Icon: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2628
|
+
import { html } from 'lit';
|
|
2629
|
+
import './Icon';
|
|
2630
|
+
|
|
2631
|
+
const STAR_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>\`;
|
|
2632
|
+
const INFO_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>\`;
|
|
2633
|
+
const CHECK_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>\`;
|
|
2634
|
+
|
|
2635
|
+
const meta: Meta = {
|
|
2636
|
+
title: 'AgnosticUI/Icon',
|
|
2637
|
+
component: 'ag-icon',
|
|
2638
|
+
tags: ['autodocs'],
|
|
2639
|
+
argTypes: {
|
|
2640
|
+
size: { control: 'select', options: ['14', '16', '18', '20', '24', '32', '36', '40', '48'] },
|
|
2641
|
+
type: { control: 'select', options: ['', 'info', 'action', 'success', 'warning', 'error'] },
|
|
2642
|
+
},
|
|
2643
|
+
args: { size: '24', type: '' },
|
|
2644
|
+
render: (args: any) => html\`
|
|
2645
|
+
<ag-icon .size=\${args.size} .type=\${args.type} no-fill>
|
|
2646
|
+
\${STAR_SVG}
|
|
2647
|
+
</ag-icon>
|
|
2648
|
+
\`,
|
|
2649
|
+
} satisfies Meta;
|
|
2650
|
+
|
|
2651
|
+
export default meta;
|
|
2652
|
+
type Story = StoryObj;
|
|
2653
|
+
|
|
2654
|
+
export const Default: Story = {};
|
|
2655
|
+
|
|
2656
|
+
export const Types: Story = {
|
|
2657
|
+
render: () => html\`
|
|
2658
|
+
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
2659
|
+
<ag-icon size="24" no-fill>\${STAR_SVG}</ag-icon>
|
|
2660
|
+
<ag-icon size="24" type="info" no-fill>\${INFO_SVG}</ag-icon>
|
|
2661
|
+
<ag-icon size="24" type="success" no-fill>\${CHECK_SVG}</ag-icon>
|
|
2662
|
+
</div>
|
|
2663
|
+
\`,
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2666
|
+
export const Sizes: Story = {
|
|
2667
|
+
render: () => html\`
|
|
2668
|
+
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
2669
|
+
<ag-icon size="14" no-fill>\${STAR_SVG}</ag-icon>
|
|
2670
|
+
<ag-icon size="18" no-fill>\${STAR_SVG}</ag-icon>
|
|
2671
|
+
<ag-icon size="24" no-fill>\${STAR_SVG}</ag-icon>
|
|
2672
|
+
<ag-icon size="32" no-fill>\${STAR_SVG}</ag-icon>
|
|
2673
|
+
<ag-icon size="48" no-fill>\${STAR_SVG}</ag-icon>
|
|
2674
|
+
</div>
|
|
2675
|
+
\`,
|
|
2676
|
+
};
|
|
2677
|
+
`,
|
|
2678
|
+
IconButton: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2679
|
+
import { html } from 'lit';
|
|
2680
|
+
import './IconButton';
|
|
2681
|
+
|
|
2682
|
+
const CLOSE_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>\`;
|
|
2683
|
+
const SETTINGS_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>\`;
|
|
2684
|
+
const MENU_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>\`;
|
|
2685
|
+
|
|
2686
|
+
const meta: Meta = {
|
|
2687
|
+
title: 'AgnosticUI/IconButton',
|
|
2688
|
+
component: 'ag-icon-button',
|
|
2689
|
+
tags: ['autodocs'],
|
|
2690
|
+
argTypes: {
|
|
2691
|
+
label: { control: 'text' },
|
|
2692
|
+
size: { control: 'select', options: ['xs', 'sm', 'md', 'lg', 'xl'] },
|
|
2693
|
+
variant: { control: 'select', options: ['', 'primary', 'secondary', 'ghost', 'danger'] },
|
|
2694
|
+
disabled: { control: 'boolean' },
|
|
2695
|
+
loading: { control: 'boolean' },
|
|
2696
|
+
},
|
|
2697
|
+
args: { label: 'Close', size: 'md', variant: '' },
|
|
2698
|
+
render: (args: any) => html\`
|
|
2699
|
+
<ag-icon-button
|
|
2700
|
+
.label=\${args.label}
|
|
2701
|
+
.size=\${args.size}
|
|
2702
|
+
.variant=\${args.variant}
|
|
2703
|
+
?disabled=\${args.disabled}
|
|
2704
|
+
?loading=\${args.loading}
|
|
2705
|
+
>
|
|
2706
|
+
\${CLOSE_SVG}
|
|
2707
|
+
</ag-icon-button>
|
|
2708
|
+
\`,
|
|
2709
|
+
} satisfies Meta;
|
|
2710
|
+
|
|
2711
|
+
export default meta;
|
|
2712
|
+
type Story = StoryObj;
|
|
2713
|
+
|
|
2714
|
+
export const Default: Story = {};
|
|
2715
|
+
|
|
2716
|
+
export const Variants: Story = {
|
|
2717
|
+
render: () => html\`
|
|
2718
|
+
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
2719
|
+
<ag-icon-button label="Default">\${CLOSE_SVG}</ag-icon-button>
|
|
2720
|
+
<ag-icon-button label="Settings" variant="primary">\${SETTINGS_SVG}</ag-icon-button>
|
|
2721
|
+
<ag-icon-button label="Menu" variant="secondary">\${MENU_SVG}</ag-icon-button>
|
|
2722
|
+
<ag-icon-button label="Ghost" variant="ghost">\${CLOSE_SVG}</ag-icon-button>
|
|
2723
|
+
</div>
|
|
2724
|
+
\`,
|
|
2725
|
+
};
|
|
2726
|
+
|
|
2727
|
+
export const Sizes: Story = {
|
|
2728
|
+
render: () => html\`
|
|
2729
|
+
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
2730
|
+
<ag-icon-button label="XS" size="xs">\${CLOSE_SVG}</ag-icon-button>
|
|
2731
|
+
<ag-icon-button label="SM" size="sm">\${CLOSE_SVG}</ag-icon-button>
|
|
2732
|
+
<ag-icon-button label="MD" size="md">\${CLOSE_SVG}</ag-icon-button>
|
|
2733
|
+
<ag-icon-button label="LG" size="lg">\${CLOSE_SVG}</ag-icon-button>
|
|
2734
|
+
<ag-icon-button label="XL" size="xl">\${CLOSE_SVG}</ag-icon-button>
|
|
2735
|
+
</div>
|
|
2736
|
+
\`,
|
|
2737
|
+
};
|
|
2738
|
+
`,
|
|
2739
|
+
IconButtonFx: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2740
|
+
import { html } from 'lit';
|
|
2741
|
+
import './IconButtonFx';
|
|
2742
|
+
|
|
2743
|
+
const HEART_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>\`;
|
|
2744
|
+
const STAR_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>\`;
|
|
2745
|
+
|
|
2746
|
+
const meta: Meta = {
|
|
2747
|
+
title: 'AgnosticUI/IconButtonFx',
|
|
2748
|
+
component: 'ag-icon-button-fx',
|
|
2749
|
+
tags: ['autodocs'],
|
|
2750
|
+
argTypes: {
|
|
2751
|
+
label: { control: 'text' },
|
|
2752
|
+
fx: { control: 'select', options: ['', 'pulse', 'bounce', 'jelly', 'press-pop', 'slide-in', 'grow', 'shrink', 'push', 'shake', 'wobble'] },
|
|
2753
|
+
size: { control: 'select', options: ['xs', 'sm', 'md', 'lg', 'xl'] },
|
|
2754
|
+
disabled: { control: 'boolean' },
|
|
2755
|
+
},
|
|
2756
|
+
args: { label: 'Like', fx: 'bounce', size: 'md' },
|
|
2757
|
+
render: (args: any) => html\`
|
|
2758
|
+
<ag-icon-button-fx
|
|
2759
|
+
.label=\${args.label}
|
|
2760
|
+
.fx=\${args.fx}
|
|
2761
|
+
.size=\${args.size}
|
|
2762
|
+
?disabled=\${args.disabled}
|
|
2763
|
+
>
|
|
2764
|
+
\${HEART_SVG}
|
|
2765
|
+
</ag-icon-button-fx>
|
|
2766
|
+
\`,
|
|
2767
|
+
} satisfies Meta;
|
|
2768
|
+
|
|
2769
|
+
export default meta;
|
|
2770
|
+
type Story = StoryObj;
|
|
2771
|
+
|
|
2772
|
+
export const Default: Story = {};
|
|
2773
|
+
|
|
2774
|
+
export const FxVariants: Story = {
|
|
2775
|
+
render: () => html\`
|
|
2776
|
+
<div style="display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: center;">
|
|
2777
|
+
<ag-icon-button-fx label="Bounce" fx="bounce">\${HEART_SVG}</ag-icon-button-fx>
|
|
2778
|
+
<ag-icon-button-fx label="Pulse" fx="pulse">\${STAR_SVG}</ag-icon-button-fx>
|
|
2779
|
+
<ag-icon-button-fx label="Jelly" fx="jelly">\${HEART_SVG}</ag-icon-button-fx>
|
|
2780
|
+
<ag-icon-button-fx label="Shake" fx="shake">\${STAR_SVG}</ag-icon-button-fx>
|
|
2781
|
+
</div>
|
|
2782
|
+
\`,
|
|
2783
|
+
};
|
|
2784
|
+
`,
|
|
2785
|
+
Menu: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2786
|
+
import { html } from 'lit';
|
|
2787
|
+
import './Menu';
|
|
2788
|
+
|
|
2789
|
+
const meta: Meta = {
|
|
2790
|
+
title: 'AgnosticUI/Menu',
|
|
2791
|
+
component: 'ag-menu-button',
|
|
2792
|
+
tags: ['autodocs'],
|
|
2793
|
+
argTypes: {
|
|
2794
|
+
label: { control: 'text' },
|
|
2795
|
+
menuVariant: { control: 'select', options: ['chevron', 'button', 'icon'] },
|
|
2796
|
+
size: { control: 'select', options: ['x-sm', 'sm', 'md', 'lg', 'xl'] },
|
|
2797
|
+
disabled: { control: 'boolean' },
|
|
2798
|
+
},
|
|
2799
|
+
args: { label: 'Actions', menuVariant: 'chevron', size: 'md' },
|
|
2800
|
+
render: (args: any) => html\`
|
|
2801
|
+
<div style="padding: 3rem;">
|
|
2802
|
+
<ag-menu-button
|
|
2803
|
+
.menuVariant=\${args.menuVariant}
|
|
2804
|
+
.size=\${args.size}
|
|
2805
|
+
?disabled=\${args.disabled}
|
|
2806
|
+
>
|
|
2807
|
+
\${args.label}
|
|
2808
|
+
<ag-menu slot="menu" .ariaLabel=\${'Menu options'}>
|
|
2809
|
+
<ag-menu-item .value=\${'edit'}>Edit</ag-menu-item>
|
|
2810
|
+
<ag-menu-item .value=\${'duplicate'}>Duplicate</ag-menu-item>
|
|
2811
|
+
<ag-menu-separator></ag-menu-separator>
|
|
2812
|
+
<ag-menu-item .value=\${'delete'}>Delete</ag-menu-item>
|
|
2813
|
+
</ag-menu>
|
|
2814
|
+
</ag-menu-button>
|
|
2815
|
+
</div>
|
|
2816
|
+
\`,
|
|
2817
|
+
} satisfies Meta;
|
|
2818
|
+
|
|
2819
|
+
export default meta;
|
|
2820
|
+
type Story = StoryObj;
|
|
2821
|
+
|
|
2822
|
+
export const Default: Story = {};
|
|
2823
|
+
|
|
2824
|
+
export const Variants: Story = {
|
|
2825
|
+
render: () => html\`
|
|
2826
|
+
<div style="padding: 3rem; display: flex; gap: 2rem; flex-wrap: wrap;">
|
|
2827
|
+
<ag-menu-button .menuVariant=\${'chevron'}>
|
|
2828
|
+
Chevron
|
|
2829
|
+
<ag-menu slot="menu" .ariaLabel=\${'Options'}>
|
|
2830
|
+
<ag-menu-item .value=\${'a'}>Option A</ag-menu-item>
|
|
2831
|
+
<ag-menu-item .value=\${'b'}>Option B</ag-menu-item>
|
|
2832
|
+
<ag-menu-item .value=\${'c'}>Option C</ag-menu-item>
|
|
2833
|
+
</ag-menu>
|
|
2834
|
+
</ag-menu-button>
|
|
2835
|
+
<ag-menu-button .menuVariant=\${'button'}>
|
|
2836
|
+
Button
|
|
2837
|
+
<ag-menu slot="menu" .ariaLabel=\${'Options'}>
|
|
2838
|
+
<ag-menu-item .value=\${'a'}>Option A</ag-menu-item>
|
|
2839
|
+
<ag-menu-item .value=\${'b'}>Option B</ag-menu-item>
|
|
2840
|
+
</ag-menu>
|
|
2841
|
+
</ag-menu-button>
|
|
2842
|
+
<ag-menu-button .menuVariant=\${'icon'}>
|
|
2843
|
+
Menu
|
|
2844
|
+
<ag-menu slot="menu" .ariaLabel=\${'Options'}>
|
|
2845
|
+
<ag-menu-item .value=\${'a'}>Option A</ag-menu-item>
|
|
2846
|
+
<ag-menu-item .value=\${'b'}>Option B</ag-menu-item>
|
|
2847
|
+
</ag-menu>
|
|
2848
|
+
</ag-menu-button>
|
|
2849
|
+
</div>
|
|
2850
|
+
\`,
|
|
2851
|
+
};
|
|
2852
|
+
`,
|
|
2853
|
+
Fieldset: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2854
|
+
import { html } from 'lit';
|
|
2855
|
+
import './Fieldset';
|
|
2856
|
+
import '../../Input/core/Input';
|
|
2857
|
+
|
|
2858
|
+
const meta: Meta = {
|
|
2859
|
+
title: 'AgnosticUI/Fieldset',
|
|
2860
|
+
component: 'ag-fieldset',
|
|
2861
|
+
tags: ['autodocs'],
|
|
2862
|
+
argTypes: {
|
|
2863
|
+
legend: { control: 'text' },
|
|
2864
|
+
bordered: { control: 'boolean' },
|
|
2865
|
+
layout: { control: 'select', options: ['vertical', 'horizontal'] },
|
|
2866
|
+
},
|
|
2867
|
+
args: { legend: 'Personal Information', bordered: false, layout: 'vertical' },
|
|
2868
|
+
render: (args: any) => html\`
|
|
2869
|
+
<ag-fieldset
|
|
2870
|
+
.legend=\${args.legend}
|
|
2871
|
+
?bordered=\${args.bordered}
|
|
2872
|
+
.layout=\${args.layout}
|
|
2873
|
+
>
|
|
2874
|
+
<ag-input id="first-name" name="first-name" label="First Name" placeholder="Jane"></ag-input>
|
|
2875
|
+
<ag-input id="last-name" name="last-name" label="Last Name" placeholder="Doe"></ag-input>
|
|
2876
|
+
<ag-input id="email" name="email" label="Email" type="email" placeholder="jane@example.com"></ag-input>
|
|
2877
|
+
</ag-fieldset>
|
|
2878
|
+
\`,
|
|
2879
|
+
} satisfies Meta;
|
|
2880
|
+
|
|
2881
|
+
export default meta;
|
|
2882
|
+
type Story = StoryObj;
|
|
2883
|
+
|
|
2884
|
+
export const Default: Story = {};
|
|
2885
|
+
|
|
2886
|
+
export const Bordered: Story = { args: { bordered: true } };
|
|
2887
|
+
`,
|
|
2888
|
+
IntlFormatter: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2889
|
+
import { html } from 'lit';
|
|
2890
|
+
import './IntlFormatter';
|
|
2891
|
+
|
|
2892
|
+
const meta: Meta = {
|
|
2893
|
+
title: 'AgnosticUI/IntlFormatter',
|
|
2894
|
+
component: 'ag-intl-formatter',
|
|
2895
|
+
tags: ['autodocs'],
|
|
2896
|
+
argTypes: {
|
|
2897
|
+
type: { control: 'select', options: ['number', 'date', 'currency', 'percent'] },
|
|
2898
|
+
lang: { control: 'text' },
|
|
2899
|
+
value: { control: 'text' },
|
|
2900
|
+
},
|
|
2901
|
+
args: { type: 'number', value: '1234567.89', lang: 'en-US' },
|
|
2902
|
+
render: (args: any) => html\`
|
|
2903
|
+
<ag-intl-formatter
|
|
2904
|
+
.type=\${args.type}
|
|
2905
|
+
.value=\${args.value}
|
|
2906
|
+
.lang=\${args.lang}
|
|
2907
|
+
></ag-intl-formatter>
|
|
2908
|
+
\`,
|
|
2909
|
+
} satisfies Meta;
|
|
2910
|
+
|
|
2911
|
+
export default meta;
|
|
2912
|
+
type Story = StoryObj;
|
|
2913
|
+
|
|
2914
|
+
export const Default: Story = {};
|
|
2915
|
+
|
|
2916
|
+
export const Currency: Story = {
|
|
2917
|
+
render: () => html\`
|
|
2918
|
+
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
|
2919
|
+
<ag-intl-formatter .type=\${'currency'} .value=\${'1234.56'} .lang=\${'en-US'} .currency=\${'USD'}></ag-intl-formatter>
|
|
2920
|
+
<ag-intl-formatter .type=\${'currency'} .value=\${'1234.56'} .lang=\${'de-DE'} .currency=\${'EUR'}></ag-intl-formatter>
|
|
2921
|
+
</div>
|
|
2922
|
+
\`,
|
|
2923
|
+
};
|
|
2924
|
+
|
|
2925
|
+
export const DateFormat: Story = {
|
|
2926
|
+
render: () => html\`
|
|
2927
|
+
<ag-intl-formatter .type=\${'date'} .date=\${new Date().toISOString()} .lang=\${'en-US'} .dateStyle=\${'long'}></ag-intl-formatter>
|
|
2928
|
+
\`,
|
|
2929
|
+
};
|
|
2930
|
+
`,
|
|
2931
|
+
Popover: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2932
|
+
import { html } from 'lit';
|
|
2933
|
+
import './Popover';
|
|
2934
|
+
import '../../Button/core/Button';
|
|
2935
|
+
|
|
2936
|
+
const meta: Meta = {
|
|
2937
|
+
title: 'AgnosticUI/Popover',
|
|
2938
|
+
component: 'ag-popover',
|
|
2939
|
+
tags: ['autodocs'],
|
|
2940
|
+
argTypes: {
|
|
2941
|
+
placement: { control: 'select', options: ['top', 'bottom', 'left', 'right', 'top-start', 'top-end', 'bottom-start', 'bottom-end'] },
|
|
2942
|
+
arrow: { control: 'boolean' },
|
|
2943
|
+
triggerType: { control: 'select', options: ['click', 'hover', 'focus'] },
|
|
2944
|
+
showCloseButton: { control: 'boolean' },
|
|
2945
|
+
},
|
|
2946
|
+
args: { placement: 'bottom', arrow: true, triggerType: 'click', showCloseButton: true },
|
|
2947
|
+
render: (args: any) => html\`
|
|
2948
|
+
<div style="padding: 4rem; display: flex; justify-content: center;">
|
|
2949
|
+
<ag-popover
|
|
2950
|
+
.placement=\${args.placement}
|
|
2951
|
+
?arrow=\${args.arrow}
|
|
2952
|
+
.triggerType=\${args.triggerType}
|
|
2953
|
+
?showCloseButton=\${args.showCloseButton}
|
|
2954
|
+
.closeLabel=\${'Close'}
|
|
2955
|
+
>
|
|
2956
|
+
<ag-button slot="trigger">Open Popover</ag-button>
|
|
2957
|
+
<span slot="title">Popover Title</span>
|
|
2958
|
+
<div slot="content" style="padding: 0.5rem 0;">
|
|
2959
|
+
<p>This is the popover content. It can contain any HTML.</p>
|
|
2960
|
+
</div>
|
|
2961
|
+
</ag-popover>
|
|
2962
|
+
</div>
|
|
2963
|
+
\`,
|
|
2964
|
+
} satisfies Meta;
|
|
2965
|
+
|
|
2966
|
+
export default meta;
|
|
2967
|
+
type Story = StoryObj;
|
|
2968
|
+
|
|
2969
|
+
export const Default: Story = {};
|
|
2970
|
+
|
|
2971
|
+
export const HoverTrigger: Story = {
|
|
2972
|
+
args: { triggerType: 'hover', placement: 'top', arrow: true },
|
|
2973
|
+
};
|
|
2974
|
+
`,
|
|
2975
|
+
Tabs: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
2976
|
+
import { html } from 'lit';
|
|
2977
|
+
import './Tabs';
|
|
2978
|
+
|
|
2979
|
+
const meta: Meta = {
|
|
2980
|
+
title: 'AgnosticUI/Tabs',
|
|
2981
|
+
component: 'ag-tabs',
|
|
2982
|
+
tags: ['autodocs'],
|
|
2983
|
+
argTypes: {
|
|
2984
|
+
orientation: { control: 'select', options: ['horizontal', 'vertical'] },
|
|
2985
|
+
activation: { control: 'select', options: ['manual', 'automatic'] },
|
|
2986
|
+
},
|
|
2987
|
+
args: { orientation: 'horizontal', activation: 'manual' },
|
|
2988
|
+
render: (args: any) => html\`
|
|
2989
|
+
<ag-tabs
|
|
2990
|
+
aria-label="Example Tabs"
|
|
2991
|
+
.orientation=\${args.orientation}
|
|
2992
|
+
.activation=\${args.activation}
|
|
2993
|
+
>
|
|
2994
|
+
<ag-tab slot="tab" panel="p1">Overview</ag-tab>
|
|
2995
|
+
<ag-tab slot="tab" panel="p2">Features</ag-tab>
|
|
2996
|
+
<ag-tab slot="tab" panel="p3">Pricing</ag-tab>
|
|
2997
|
+
<ag-tab-panel slot="panel" panel="p1">
|
|
2998
|
+
<div style="padding: 1rem;">
|
|
2999
|
+
<h3>Overview</h3>
|
|
3000
|
+
<p>AgnosticUI is a framework-agnostic component library that works with React, Vue, and Lit.</p>
|
|
3001
|
+
</div>
|
|
3002
|
+
</ag-tab-panel>
|
|
3003
|
+
<ag-tab-panel slot="panel" panel="p2">
|
|
3004
|
+
<div style="padding: 1rem;">
|
|
3005
|
+
<h3>Features</h3>
|
|
3006
|
+
<ul><li>Framework agnostic</li><li>Accessible by default</li><li>Themeable via CSS tokens</li></ul>
|
|
3007
|
+
</div>
|
|
3008
|
+
</ag-tab-panel>
|
|
3009
|
+
<ag-tab-panel slot="panel" panel="p3">
|
|
3010
|
+
<div style="padding: 1rem;">
|
|
3011
|
+
<h3>Pricing</h3>
|
|
3012
|
+
<p>Free and open source under the Apache 2.0 license.</p>
|
|
3013
|
+
</div>
|
|
3014
|
+
</ag-tab-panel>
|
|
3015
|
+
</ag-tabs>
|
|
3016
|
+
\`,
|
|
3017
|
+
} satisfies Meta;
|
|
3018
|
+
|
|
3019
|
+
export default meta;
|
|
3020
|
+
type Story = StoryObj;
|
|
3021
|
+
|
|
3022
|
+
export const Default: Story = {};
|
|
3023
|
+
|
|
3024
|
+
export const Vertical: Story = { args: { orientation: 'vertical' } };
|
|
3025
|
+
`,
|
|
3026
|
+
Timeline: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
3027
|
+
import { html } from 'lit';
|
|
3028
|
+
import './Timeline';
|
|
3029
|
+
import '../../Icon/core/Icon';
|
|
3030
|
+
|
|
3031
|
+
const CHECK_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>\`;
|
|
3032
|
+
const INFO_SVG = html\`<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>\`;
|
|
3033
|
+
|
|
3034
|
+
const meta: Meta = {
|
|
3035
|
+
title: 'AgnosticUI/Timeline',
|
|
3036
|
+
component: 'ag-timeline',
|
|
3037
|
+
tags: ['autodocs'],
|
|
3038
|
+
argTypes: {
|
|
3039
|
+
orientation: { control: 'select', options: ['horizontal', 'vertical'] },
|
|
3040
|
+
variant: { control: 'select', options: ['', 'primary', 'success', 'warning', 'danger'] },
|
|
3041
|
+
compact: { control: 'boolean' },
|
|
3042
|
+
},
|
|
3043
|
+
args: { orientation: 'horizontal', variant: '', compact: false },
|
|
3044
|
+
render: (args: any) => html\`
|
|
3045
|
+
<ag-timeline
|
|
3046
|
+
orientation=\${args.orientation}
|
|
3047
|
+
variant=\${args.variant}
|
|
3048
|
+
?compact=\${args.compact}
|
|
3049
|
+
>
|
|
3050
|
+
<ag-timeline-item>
|
|
3051
|
+
<div slot="ag-start">Jan 2024</div>
|
|
3052
|
+
<div slot="ag-marker">
|
|
3053
|
+
<ag-icon type="success" size="18">\${CHECK_SVG}</ag-icon>
|
|
3054
|
+
</div>
|
|
3055
|
+
<div slot="ag-end">Project Kickoff</div>
|
|
3056
|
+
</ag-timeline-item>
|
|
3057
|
+
<ag-timeline-item>
|
|
3058
|
+
<div slot="ag-start">Mar 2024</div>
|
|
3059
|
+
<div slot="ag-marker">
|
|
3060
|
+
<ag-icon type="info" size="18">\${INFO_SVG}</ag-icon>
|
|
3061
|
+
</div>
|
|
3062
|
+
<div slot="ag-end">Beta Release</div>
|
|
3063
|
+
</ag-timeline-item>
|
|
3064
|
+
<ag-timeline-item>
|
|
3065
|
+
<div slot="ag-start">Jun 2024</div>
|
|
3066
|
+
<div slot="ag-marker">
|
|
3067
|
+
<ag-icon type="success" size="18">\${CHECK_SVG}</ag-icon>
|
|
3068
|
+
</div>
|
|
3069
|
+
<div slot="ag-end">v1.0 Launch</div>
|
|
3070
|
+
</ag-timeline-item>
|
|
3071
|
+
</ag-timeline>
|
|
3072
|
+
\`,
|
|
3073
|
+
} satisfies Meta;
|
|
3074
|
+
|
|
3075
|
+
export default meta;
|
|
3076
|
+
type Story = StoryObj;
|
|
3077
|
+
|
|
3078
|
+
export const Default: Story = {};
|
|
3079
|
+
|
|
3080
|
+
export const Vertical: Story = { args: { orientation: 'vertical' } };
|
|
3081
|
+
`,
|
|
3082
|
+
Sidebar: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
3083
|
+
import { html } from 'lit';
|
|
3084
|
+
import './Sidebar';
|
|
3085
|
+
import '../../SidebarNav/core/SidebarNav';
|
|
3086
|
+
import '../../Popover/core/Popover';
|
|
3087
|
+
|
|
3088
|
+
const HOME_SVG = \`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>\`;
|
|
3089
|
+
const FOLDER_SVG = \`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>\`;
|
|
3090
|
+
const USER_SVG = \`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>\`;
|
|
3091
|
+
const SETTINGS_SVG = \`<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>\`;
|
|
3092
|
+
const CHEVRON_RIGHT_SVG = \`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"/></svg>\`;
|
|
3093
|
+
const PANEL_TOGGLE_SVG = \`<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/></svg>\`;
|
|
3094
|
+
|
|
3095
|
+
const NAV_STYLES = \`
|
|
3096
|
+
.nav-button {
|
|
3097
|
+
display: flex;
|
|
3098
|
+
align-items: center;
|
|
3099
|
+
gap: var(--ag-space-3);
|
|
3100
|
+
position: relative;
|
|
3101
|
+
padding: var(--ag-space-2) var(--ag-space-3);
|
|
3102
|
+
margin-block-end: var(--ag-space-1);
|
|
3103
|
+
border: none;
|
|
3104
|
+
background: none;
|
|
3105
|
+
cursor: pointer;
|
|
3106
|
+
width: 100%;
|
|
3107
|
+
text-align: left;
|
|
3108
|
+
border-radius: var(--ag-radius-sm);
|
|
3109
|
+
transition: background var(--ag-fx-duration-sm);
|
|
3110
|
+
color: inherit;
|
|
3111
|
+
}
|
|
3112
|
+
.nav-button svg { flex-shrink: 0; }
|
|
3113
|
+
.nav-button:hover { background: var(--ag-background-secondary); }
|
|
3114
|
+
.nav-button.active {
|
|
3115
|
+
background: var(--ag-primary-background);
|
|
3116
|
+
color: var(--ag-primary-text);
|
|
3117
|
+
font-weight: 500;
|
|
3118
|
+
}
|
|
3119
|
+
.nav-label {
|
|
3120
|
+
flex-grow: 1;
|
|
3121
|
+
overflow: hidden;
|
|
3122
|
+
text-overflow: ellipsis;
|
|
3123
|
+
white-space: nowrap;
|
|
3124
|
+
transition: opacity var(--ag-sidebar-transition-duration) var(--ag-sidebar-transition-easing);
|
|
3125
|
+
}
|
|
3126
|
+
.chevron {
|
|
3127
|
+
display: flex;
|
|
3128
|
+
align-items: center;
|
|
3129
|
+
transition: transform var(--ag-fx-duration-md), opacity var(--ag-fx-duration-sm);
|
|
3130
|
+
margin-left: auto;
|
|
3131
|
+
}
|
|
3132
|
+
.nav-button[aria-expanded="true"] .chevron { transform: rotate(90deg); }
|
|
3133
|
+
ag-sidebar[collapsed] .nav-label,
|
|
3134
|
+
ag-sidebar[collapsed] .chevron {
|
|
3135
|
+
opacity: 0;
|
|
3136
|
+
pointer-events: none;
|
|
3137
|
+
display: none;
|
|
3138
|
+
}
|
|
3139
|
+
ag-sidebar[collapsed] .nav-button {
|
|
3140
|
+
width: auto;
|
|
3141
|
+
padding: var(--ag-space-2);
|
|
3142
|
+
}
|
|
3143
|
+
ag-sidebar[collapsed] ag-sidebar-nav-submenu:not(.popover-submenu),
|
|
3144
|
+
ag-sidebar:not([collapsed]) ag-popover,
|
|
3145
|
+
ag-sidebar[collapsed] .nav-button-expanded,
|
|
3146
|
+
ag-sidebar:not([collapsed]) .nav-button-collapsed {
|
|
3147
|
+
display: none !important;
|
|
3148
|
+
}
|
|
3149
|
+
ag-sidebar[collapsed] ag-popover.nav-button-collapsed {
|
|
3150
|
+
display: block !important;
|
|
3151
|
+
}
|
|
3152
|
+
ag-sidebar-nav-submenu {
|
|
3153
|
+
display: none;
|
|
3154
|
+
overflow: hidden;
|
|
3155
|
+
}
|
|
3156
|
+
ag-sidebar-nav-submenu[open] {
|
|
3157
|
+
display: block;
|
|
3158
|
+
}
|
|
3159
|
+
.nav-sublink {
|
|
3160
|
+
display: block;
|
|
3161
|
+
padding: var(--ag-space-2) var(--ag-space-3);
|
|
3162
|
+
margin-block-end: var(--ag-space-1);
|
|
3163
|
+
color: inherit;
|
|
3164
|
+
text-decoration: none;
|
|
3165
|
+
border-radius: var(--ag-radius-sm);
|
|
3166
|
+
transition: background var(--ag-fx-duration-sm);
|
|
3167
|
+
}
|
|
3168
|
+
.nav-sublink:hover { background: var(--ag-background-secondary); }
|
|
3169
|
+
.nav-button-collapsed::part(ag-popover-body) { padding: var(--ag-space-1); }
|
|
3170
|
+
\`;
|
|
3171
|
+
|
|
3172
|
+
const handleSubmenuToggle = (e: Event) => {
|
|
3173
|
+
e.preventDefault();
|
|
3174
|
+
e.stopPropagation();
|
|
3175
|
+
const button = e.currentTarget as HTMLElement;
|
|
3176
|
+
const navItem = button.closest('ag-sidebar-nav-item');
|
|
3177
|
+
if (!navItem) return;
|
|
3178
|
+
const submenu = navItem.querySelector('ag-sidebar-nav-submenu');
|
|
3179
|
+
if (!submenu) return;
|
|
3180
|
+
const isExpanded = button.getAttribute('aria-expanded') === 'true';
|
|
3181
|
+
button.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
|
|
3182
|
+
if (isExpanded) submenu.removeAttribute('open');
|
|
3183
|
+
else submenu.setAttribute('open', '');
|
|
3184
|
+
};
|
|
3185
|
+
|
|
3186
|
+
const toggleCollapse = (e: Event) => {
|
|
3187
|
+
const sidebar = (e.target as HTMLElement).closest('ag-sidebar') as any;
|
|
3188
|
+
if (sidebar && sidebar.toggleCollapse) sidebar.toggleCollapse();
|
|
3189
|
+
};
|
|
3190
|
+
|
|
3191
|
+
const meta: Meta = {
|
|
3192
|
+
title: 'AgnosticUI/Sidebar',
|
|
3193
|
+
component: 'ag-sidebar',
|
|
3194
|
+
tags: ['autodocs'],
|
|
3195
|
+
parameters: { layout: 'fullscreen' },
|
|
3196
|
+
argTypes: {
|
|
3197
|
+
collapsed: { control: 'boolean' },
|
|
3198
|
+
variant: { control: 'select', options: ['default', 'bordered', 'elevated'] },
|
|
3199
|
+
},
|
|
3200
|
+
args: { collapsed: false, variant: 'default' },
|
|
3201
|
+
render: (args: any) => html\`
|
|
3202
|
+
<style>\${NAV_STYLES}</style>
|
|
3203
|
+
<div style="position: relative; display: flex; height: 500px; border: 1px solid var(--ag-border-color); border-radius: 0.5rem; overflow: hidden; contain: layout;">
|
|
3204
|
+
<ag-sidebar
|
|
3205
|
+
?collapsed=\${args.collapsed}
|
|
3206
|
+
.variant=\${args.variant}
|
|
3207
|
+
aria-label="Main navigation"
|
|
3208
|
+
show-mobile-toggle
|
|
3209
|
+
>
|
|
3210
|
+
<h2 slot="ag-header-start" style="margin: 0; font-size: 1.125rem; font-weight: 600;">Dashboard</h2>
|
|
3211
|
+
<button
|
|
3212
|
+
type="button"
|
|
3213
|
+
slot="ag-header-toggle"
|
|
3214
|
+
aria-label="Toggle sidebar"
|
|
3215
|
+
style="background: none; border: none; padding: 8px 0; cursor: pointer; display: flex; align-items: center; color: inherit;"
|
|
3216
|
+
@click=\${toggleCollapse}
|
|
3217
|
+
>
|
|
3218
|
+
<span .innerHTML=\${PANEL_TOGGLE_SVG}></span>
|
|
3219
|
+
</button>
|
|
3220
|
+
|
|
3221
|
+
<ag-sidebar-nav>
|
|
3222
|
+
<ag-sidebar-nav-item>
|
|
3223
|
+
<button type="button" class="nav-button active" aria-current="page">
|
|
3224
|
+
<span .innerHTML=\${HOME_SVG}></span>
|
|
3225
|
+
<span class="nav-label">Dashboard</span>
|
|
3226
|
+
</button>
|
|
3227
|
+
</ag-sidebar-nav-item>
|
|
3228
|
+
|
|
3229
|
+
<ag-sidebar-nav-item>
|
|
3230
|
+
<button type="button" class="nav-button nav-button-expanded" aria-expanded="false" @click=\${handleSubmenuToggle}>
|
|
3231
|
+
<span .innerHTML=\${FOLDER_SVG}></span>
|
|
3232
|
+
<span class="nav-label">Projects</span>
|
|
3233
|
+
<span class="chevron"><span .innerHTML=\${CHEVRON_RIGHT_SVG}></span></span>
|
|
3234
|
+
</button>
|
|
3235
|
+
<ag-popover class="nav-button-collapsed" placement="right-start" trigger-type="click" distance="8" arrow>
|
|
3236
|
+
<button slot="trigger" type="button" class="nav-button">
|
|
3237
|
+
<span .innerHTML=\${FOLDER_SVG}></span>
|
|
3238
|
+
</button>
|
|
3239
|
+
<ag-sidebar-nav-popover-submenu slot="content">
|
|
3240
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Project Alpha</a>
|
|
3241
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Project Beta</a>
|
|
3242
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Project Gamma</a>
|
|
3243
|
+
</ag-sidebar-nav-popover-submenu>
|
|
3244
|
+
</ag-popover>
|
|
3245
|
+
<ag-sidebar-nav-submenu>
|
|
3246
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Project Alpha</a>
|
|
3247
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Project Beta</a>
|
|
3248
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Project Gamma</a>
|
|
3249
|
+
</ag-sidebar-nav-submenu>
|
|
3250
|
+
</ag-sidebar-nav-item>
|
|
3251
|
+
|
|
3252
|
+
<ag-sidebar-nav-item>
|
|
3253
|
+
<button type="button" class="nav-button">
|
|
3254
|
+
<span .innerHTML=\${USER_SVG}></span>
|
|
3255
|
+
<span class="nav-label">Team</span>
|
|
3256
|
+
</button>
|
|
3257
|
+
</ag-sidebar-nav-item>
|
|
3258
|
+
|
|
3259
|
+
<ag-sidebar-nav-item>
|
|
3260
|
+
<button type="button" class="nav-button nav-button-expanded" aria-expanded="false" @click=\${handleSubmenuToggle}>
|
|
3261
|
+
<span .innerHTML=\${SETTINGS_SVG}></span>
|
|
3262
|
+
<span class="nav-label">Settings</span>
|
|
3263
|
+
<span class="chevron"><span .innerHTML=\${CHEVRON_RIGHT_SVG}></span></span>
|
|
3264
|
+
</button>
|
|
3265
|
+
<ag-popover class="nav-button-collapsed" placement="right-start" trigger-type="click" distance="8" arrow>
|
|
3266
|
+
<button slot="trigger" type="button" class="nav-button">
|
|
3267
|
+
<span .innerHTML=\${SETTINGS_SVG}></span>
|
|
3268
|
+
</button>
|
|
3269
|
+
<ag-sidebar-nav-popover-submenu slot="content">
|
|
3270
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Profile</a>
|
|
3271
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Billing</a>
|
|
3272
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Security</a>
|
|
3273
|
+
</ag-sidebar-nav-popover-submenu>
|
|
3274
|
+
</ag-popover>
|
|
3275
|
+
<ag-sidebar-nav-submenu>
|
|
3276
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Profile</a>
|
|
3277
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Billing</a>
|
|
3278
|
+
<a href="#" class="nav-sublink" @click=\${(e: Event) => e.preventDefault()}>Security</a>
|
|
3279
|
+
</ag-sidebar-nav-submenu>
|
|
3280
|
+
</ag-sidebar-nav-item>
|
|
3281
|
+
</ag-sidebar-nav>
|
|
3282
|
+
|
|
3283
|
+
<div slot="ag-footer" style="font-size: 0.875rem; color: var(--ag-text-secondary);">
|
|
3284
|
+
© 2024 Company
|
|
3285
|
+
</div>
|
|
3286
|
+
</ag-sidebar>
|
|
3287
|
+
|
|
3288
|
+
<main style="flex: 1; padding: 2rem; overflow: auto; background: var(--ag-background);">
|
|
3289
|
+
<h1 style="margin-top: 0;">Main Content</h1>
|
|
3290
|
+
<p>Click the panel toggle button to collapse the sidebar into rail mode.</p>
|
|
3291
|
+
<p>When collapsed, click icon-only items with submenus to see them in popovers.</p>
|
|
3292
|
+
</main>
|
|
3293
|
+
</div>
|
|
3294
|
+
\`,
|
|
3295
|
+
} satisfies Meta;
|
|
3296
|
+
|
|
3297
|
+
export default meta;
|
|
3298
|
+
type Story = StoryObj;
|
|
3299
|
+
|
|
3300
|
+
export const Default: Story = {};
|
|
3301
|
+
|
|
3302
|
+
export const Collapsed: Story = { args: { collapsed: true } };
|
|
3303
|
+
|
|
3304
|
+
export const Elevated: Story = { args: { variant: 'elevated' } };
|
|
3305
|
+
`,
|
|
3306
|
+
ScrollToButton: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
3307
|
+
import { html } from 'lit';
|
|
3308
|
+
import './ScrollToButton';
|
|
3309
|
+
|
|
3310
|
+
const meta: Meta = {
|
|
3311
|
+
title: 'AgnosticUI/ScrollToButton',
|
|
3312
|
+
component: 'ag-scroll-to-button',
|
|
3313
|
+
tags: ['autodocs'],
|
|
3314
|
+
argTypes: {
|
|
3315
|
+
label: { control: 'text' },
|
|
3316
|
+
target: { control: 'select', options: ['top', 'bottom'] },
|
|
3317
|
+
scrollThreshold: { control: 'number' },
|
|
3318
|
+
smoothScroll: { control: 'boolean' },
|
|
3319
|
+
showLabel: { control: 'boolean' },
|
|
3320
|
+
},
|
|
3321
|
+
args: { label: 'Back to Top', target: 'top', scrollThreshold: 400, smoothScroll: true, showLabel: false },
|
|
3322
|
+
} satisfies Meta;
|
|
3323
|
+
|
|
3324
|
+
export default meta;
|
|
3325
|
+
type Story = StoryObj;
|
|
3326
|
+
|
|
3327
|
+
export const Default: Story = {
|
|
3328
|
+
render: (args: any) => html\`
|
|
3329
|
+
<div style="min-height: 200vh; padding: 2rem; position: relative;">
|
|
3330
|
+
<h2>Scroll Down to See the Button</h2>
|
|
3331
|
+
<p>The scroll-to-top button appears after scrolling \${args.scrollThreshold}px.</p>
|
|
3332
|
+
<div style="margin-top: 1rem; padding: 1rem; background: var(--ag-background-secondary, #f9fafb); border-radius: 0.5rem;">
|
|
3333
|
+
Scroll down this panel to trigger the floating button.
|
|
3334
|
+
</div>
|
|
3335
|
+
\${Array.from({ length: 20 }, (_, i) => html\`<p style="margin: 1.5rem 0;">Paragraph \${i + 1}: Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>\`)}
|
|
3336
|
+
<ag-scroll-to-button
|
|
3337
|
+
.label=\${args.label}
|
|
3338
|
+
.target=\${args.target}
|
|
3339
|
+
.scrollThreshold=\${args.scrollThreshold}
|
|
3340
|
+
?smoothScroll=\${args.smoothScroll}
|
|
3341
|
+
?showLabel=\${args.showLabel}
|
|
3342
|
+
></ag-scroll-to-button>
|
|
3343
|
+
</div>
|
|
3344
|
+
\`,
|
|
3345
|
+
};
|
|
3346
|
+
`,
|
|
3347
|
+
ScrollProgress: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
3348
|
+
import { html } from 'lit';
|
|
3349
|
+
import './ScrollProgress';
|
|
3350
|
+
|
|
3351
|
+
const LOREM = Array.from({ length: 40 }, (_, i) =>
|
|
3352
|
+
\`<p style="margin: 1rem 0; line-height: 1.6; max-width: 700px;">Paragraph \${i + 1}: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p>\`
|
|
3353
|
+
).join('');
|
|
3354
|
+
|
|
3355
|
+
const meta: Meta = {
|
|
3356
|
+
title: 'AgnosticUI/ScrollProgress',
|
|
3357
|
+
component: 'ag-scroll-progress',
|
|
3358
|
+
tags: ['autodocs'],
|
|
3359
|
+
parameters: { layout: 'fullscreen' },
|
|
3360
|
+
argTypes: {
|
|
3361
|
+
mode: { control: 'select', options: ['bar', 'dot-trail', 'badge', 'ring'] },
|
|
3362
|
+
orientation: { control: 'select', options: ['top', 'bottom'] },
|
|
3363
|
+
},
|
|
3364
|
+
args: { mode: 'bar', orientation: 'top' },
|
|
3365
|
+
} satisfies Meta;
|
|
3366
|
+
|
|
3367
|
+
export default meta;
|
|
3368
|
+
type Story = StoryObj;
|
|
3369
|
+
|
|
3370
|
+
export const BarTop: Story = {
|
|
3371
|
+
parameters: { layout: 'fullscreen' },
|
|
3372
|
+
render: () => html\`
|
|
3373
|
+
<ag-scroll-progress .mode=\${'bar'} .orientation=\${'top'}></ag-scroll-progress>
|
|
3374
|
+
<div style="padding: 2rem;">
|
|
3375
|
+
<h2>Bar — Top</h2>
|
|
3376
|
+
<p>Scroll down. The progress bar fills along the <strong>top</strong> of the page.</p>
|
|
3377
|
+
<div .innerHTML=\${LOREM}></div>
|
|
3378
|
+
</div>
|
|
3379
|
+
\`,
|
|
3380
|
+
};
|
|
3381
|
+
|
|
3382
|
+
export const BarBottom: Story = {
|
|
3383
|
+
parameters: { layout: 'fullscreen' },
|
|
3384
|
+
render: () => html\`
|
|
3385
|
+
<ag-scroll-progress .mode=\${'bar'} .orientation=\${'bottom'}></ag-scroll-progress>
|
|
3386
|
+
<div style="padding: 2rem;">
|
|
3387
|
+
<h2>Bar — Bottom</h2>
|
|
3388
|
+
<p>Scroll down. The progress bar fills along the <strong>bottom</strong> of the page.</p>
|
|
3389
|
+
<div .innerHTML=\${LOREM}></div>
|
|
3390
|
+
</div>
|
|
3391
|
+
\`,
|
|
3392
|
+
};
|
|
3393
|
+
|
|
3394
|
+
export const DotTrail: Story = {
|
|
3395
|
+
parameters: { layout: 'fullscreen' },
|
|
3396
|
+
render: () => html\`
|
|
3397
|
+
<div style="position: fixed; bottom: 1.5rem; left: 50%; transform: translateX(-50%); z-index: 100;
|
|
3398
|
+
background: var(--ag-background, white); border-radius: 2rem; padding: 0.5rem 1rem;
|
|
3399
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);">
|
|
3400
|
+
<ag-scroll-progress .mode=\${'dot-trail'} .dots=\${7}></ag-scroll-progress>
|
|
3401
|
+
</div>
|
|
3402
|
+
<div style="padding: 2rem;">
|
|
3403
|
+
<h2>Dot Trail</h2>
|
|
3404
|
+
<p>Scroll down. The dots at the bottom of the page fill in to show progress.</p>
|
|
3405
|
+
<div .innerHTML=\${LOREM}></div>
|
|
3406
|
+
</div>
|
|
3407
|
+
\`,
|
|
3408
|
+
};
|
|
3409
|
+
|
|
3410
|
+
export const Badge: Story = {
|
|
3411
|
+
parameters: { layout: 'fullscreen' },
|
|
3412
|
+
render: () => html\`
|
|
3413
|
+
<div style="position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 100;">
|
|
3414
|
+
<ag-scroll-progress .mode=\${'badge'} .badgeVariant=\${'info'}></ag-scroll-progress>
|
|
3415
|
+
</div>
|
|
3416
|
+
<div style="padding: 2rem;">
|
|
3417
|
+
<h2>Badge</h2>
|
|
3418
|
+
<p>Scroll down. The percentage badge in the bottom-right updates as you scroll.</p>
|
|
3419
|
+
<div .innerHTML=\${LOREM}></div>
|
|
3420
|
+
</div>
|
|
3421
|
+
\`,
|
|
3422
|
+
};
|
|
3423
|
+
|
|
3424
|
+
export const Ring: Story = {
|
|
3425
|
+
parameters: { layout: 'fullscreen' },
|
|
3426
|
+
render: () => html\`
|
|
3427
|
+
<div style="position: fixed; bottom: 1.5rem; right: 1.5rem; z-index: 100;">
|
|
3428
|
+
<ag-scroll-progress .mode=\${'ring'} .ringSize=\${48} .ringVariant=\${'primary'}></ag-scroll-progress>
|
|
3429
|
+
</div>
|
|
3430
|
+
<div style="padding: 2rem;">
|
|
3431
|
+
<h2>Ring</h2>
|
|
3432
|
+
<p>Scroll down. The progress ring in the bottom-right fills as you scroll.</p>
|
|
3433
|
+
<div .innerHTML=\${LOREM}></div>
|
|
3434
|
+
</div>
|
|
3435
|
+
\`,
|
|
3436
|
+
};
|
|
3437
|
+
`,
|
|
3438
|
+
Toast: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
3439
|
+
import { html } from 'lit';
|
|
3440
|
+
import './Toast';
|
|
3441
|
+
import '../../Button/core/Button';
|
|
3442
|
+
|
|
3443
|
+
const meta: Meta = {
|
|
3444
|
+
title: 'AgnosticUI/Toast',
|
|
3445
|
+
component: 'ag-toast',
|
|
3446
|
+
tags: ['autodocs'],
|
|
3447
|
+
argTypes: {
|
|
3448
|
+
type: { control: 'select', options: ['info', 'success', 'warning', 'danger', 'error'] },
|
|
3449
|
+
position: { control: 'select', options: ['top-end', 'top', 'top-start', 'bottom-end', 'bottom', 'bottom-start'] },
|
|
3450
|
+
autoDismiss: { control: 'boolean' },
|
|
3451
|
+
showCloseButton: { control: 'boolean' },
|
|
3452
|
+
bordered: { control: 'boolean' },
|
|
3453
|
+
rounded: { control: 'boolean' },
|
|
3454
|
+
},
|
|
3455
|
+
args: {
|
|
3456
|
+
type: 'info',
|
|
3457
|
+
position: 'top-end',
|
|
3458
|
+
autoDismiss: false,
|
|
3459
|
+
showCloseButton: true,
|
|
3460
|
+
bordered: false,
|
|
3461
|
+
rounded: true,
|
|
3462
|
+
},
|
|
3463
|
+
} satisfies Meta;
|
|
3464
|
+
|
|
3465
|
+
export default meta;
|
|
3466
|
+
type Story = StoryObj;
|
|
3467
|
+
|
|
3468
|
+
export const Default: Story = {
|
|
3469
|
+
render: (args: any) => html\`
|
|
3470
|
+
<div style="min-height: 200px; padding: 1rem;">
|
|
3471
|
+
<ag-button
|
|
3472
|
+
@click=\${(e: Event) => {
|
|
3473
|
+
const toast = (e.target as HTMLElement).parentElement?.querySelector('ag-toast') as any;
|
|
3474
|
+
if (toast) { toast.open = false; requestAnimationFrame(() => { toast.open = true; }); }
|
|
3475
|
+
}}
|
|
3476
|
+
>Show Toast</ag-button>
|
|
3477
|
+
<ag-toast
|
|
3478
|
+
.open=\${args.open ?? false}
|
|
3479
|
+
.type=\${args.type}
|
|
3480
|
+
.position=\${args.position}
|
|
3481
|
+
.autoDismiss=\${args.autoDismiss}
|
|
3482
|
+
.showCloseButton=\${args.showCloseButton}
|
|
3483
|
+
.bordered=\${args.bordered}
|
|
3484
|
+
.rounded=\${args.rounded}
|
|
3485
|
+
@toast-close=\${(e: Event) => { (e.target as any).open = false; }}
|
|
3486
|
+
>
|
|
3487
|
+
Toast notification message here.
|
|
3488
|
+
</ag-toast>
|
|
3489
|
+
</div>
|
|
3490
|
+
\`,
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3493
|
+
export const TopStart: Story = {
|
|
3494
|
+
render: () => html\`
|
|
3495
|
+
<div style="min-height: 200px; padding: 1rem;">
|
|
3496
|
+
<ag-toast .open=\${true} type="success" .position=\${'top-start'} .showCloseButton=\${true}>
|
|
3497
|
+
Saved successfully!
|
|
3498
|
+
</ag-toast>
|
|
3499
|
+
<p>Toast anchored to top-start.</p>
|
|
3500
|
+
</div>
|
|
3501
|
+
\`,
|
|
3502
|
+
};
|
|
3503
|
+
|
|
3504
|
+
export const BottomEnd: Story = {
|
|
3505
|
+
render: () => html\`
|
|
3506
|
+
<div style="min-height: 200px; padding: 1rem;">
|
|
3507
|
+
<ag-toast .open=\${true} type="warning" .position=\${'bottom-end'} .showCloseButton=\${true}>
|
|
3508
|
+
Warning: low disk space.
|
|
3509
|
+
</ag-toast>
|
|
3510
|
+
<p>Toast anchored to bottom-end.</p>
|
|
3511
|
+
</div>
|
|
3512
|
+
\`,
|
|
3513
|
+
};
|
|
3514
|
+
|
|
3515
|
+
export const BottomCenter: Story = {
|
|
3516
|
+
render: () => html\`
|
|
3517
|
+
<div style="min-height: 200px; padding: 1rem;">
|
|
3518
|
+
<ag-toast .open=\${true} type="danger" .position=\${'bottom'} .showCloseButton=\${true}>
|
|
3519
|
+
Connection lost. Retrying...
|
|
3520
|
+
</ag-toast>
|
|
3521
|
+
<p>Toast anchored to bottom-center.</p>
|
|
3522
|
+
</div>
|
|
3523
|
+
\`,
|
|
3524
|
+
};
|
|
3525
|
+
`,
|
|
3526
|
+
Tooltip: `import type { Meta, StoryObj } from '@storybook/web-components';
|
|
3527
|
+
import { html } from 'lit';
|
|
3528
|
+
import './Tooltip';
|
|
3529
|
+
|
|
3530
|
+
const meta: Meta = {
|
|
3531
|
+
title: 'AgnosticUI/Tooltip',
|
|
3532
|
+
component: 'ag-tooltip',
|
|
3533
|
+
tags: ['autodocs'],
|
|
3534
|
+
argTypes: {
|
|
3535
|
+
content: { control: 'text' },
|
|
3536
|
+
placement: { control: 'select', options: ['top', 'bottom', 'left', 'right'] },
|
|
3537
|
+
disabled: { control: 'boolean' },
|
|
3538
|
+
},
|
|
3539
|
+
args: { content: 'This is a helpful tooltip', placement: 'top', disabled: false },
|
|
3540
|
+
render: (args: any) => html\`
|
|
3541
|
+
<div style="display: flex; justify-content: center; padding: 4rem;">
|
|
3542
|
+
<ag-tooltip
|
|
3543
|
+
.content=\${args.content}
|
|
3544
|
+
.placement=\${args.placement}
|
|
3545
|
+
?disabled=\${args.disabled}
|
|
3546
|
+
>
|
|
3547
|
+
<button>Hover over me</button>
|
|
3548
|
+
</ag-tooltip>
|
|
3549
|
+
</div>
|
|
3550
|
+
\`,
|
|
3551
|
+
} satisfies Meta;
|
|
3552
|
+
|
|
3553
|
+
export default meta;
|
|
3554
|
+
type Story = StoryObj;
|
|
3555
|
+
|
|
3556
|
+
export const Default: Story = {};
|
|
3557
|
+
|
|
3558
|
+
export const Placements: Story = {
|
|
3559
|
+
render: () => html\`
|
|
3560
|
+
<div style="display: flex; gap: 2rem; justify-content: center; align-items: center; padding: 5rem 2rem;">
|
|
3561
|
+
<ag-tooltip content="Top tooltip" placement="top">
|
|
3562
|
+
<button>Top</button>
|
|
3563
|
+
</ag-tooltip>
|
|
3564
|
+
<ag-tooltip content="Bottom tooltip" placement="bottom">
|
|
3565
|
+
<button>Bottom</button>
|
|
3566
|
+
</ag-tooltip>
|
|
3567
|
+
<ag-tooltip content="Left tooltip" placement="left">
|
|
3568
|
+
<button>Left</button>
|
|
3569
|
+
</ag-tooltip>
|
|
3570
|
+
<ag-tooltip content="Right tooltip" placement="right">
|
|
3571
|
+
<button>Right</button>
|
|
3572
|
+
</ag-tooltip>
|
|
3573
|
+
</div>
|
|
3574
|
+
\`,
|
|
3575
|
+
};
|
|
3576
|
+
`,
|
|
3577
|
+
};
|
|
3578
|
+
export function generateLitStory(name) {
|
|
3579
|
+
// Check for override first
|
|
3580
|
+
if (LIT_STORY_OVERRIDES[name]) {
|
|
3581
|
+
return STORY_FILE_HEADER + LIT_STORY_OVERRIDES[name];
|
|
3582
|
+
}
|
|
3583
|
+
const tag = getAgTagName(name);
|
|
3584
|
+
// For import path, the side-effect import uses the PascalCase name
|
|
3585
|
+
// e.g. import './Button' or import './SkeletonLoader'
|
|
3586
|
+
const lines = [];
|
|
3587
|
+
// Imports
|
|
3588
|
+
lines.push(`import type { Meta, StoryObj } from '@storybook/web-components';`);
|
|
3589
|
+
lines.push(`import { html } from 'lit';`);
|
|
3590
|
+
// Side-effect import to register custom element
|
|
3591
|
+
if (OPEN_CONTROLLED_COMPONENTS.has(name) && name !== 'Collapsible') {
|
|
3592
|
+
lines.push(`import './${name}';`);
|
|
3593
|
+
lines.push(`import '../../Button/core/Button';`);
|
|
3594
|
+
}
|
|
3595
|
+
else {
|
|
3596
|
+
lines.push(`import './${name}';`);
|
|
3597
|
+
}
|
|
3598
|
+
lines.push('');
|
|
3599
|
+
// Sample data for Combobox
|
|
3600
|
+
if (name === 'Combobox') {
|
|
3601
|
+
lines.push(`const FRUITS = [`);
|
|
3602
|
+
lines.push(` { value: 'apple', label: 'Apple' },`);
|
|
3603
|
+
lines.push(` { value: 'banana', label: 'Banana' },`);
|
|
3604
|
+
lines.push(` { value: 'cherry', label: 'Cherry' },`);
|
|
3605
|
+
lines.push(` { value: 'grape', label: 'Grape' },`);
|
|
3606
|
+
lines.push(` { value: 'mango', label: 'Mango' },`);
|
|
3607
|
+
lines.push(` { value: 'orange', label: 'Orange' },`);
|
|
3608
|
+
lines.push(`];`);
|
|
3609
|
+
lines.push('');
|
|
3610
|
+
}
|
|
3611
|
+
const argTypes = ARGTYPES[name] ?? '';
|
|
3612
|
+
// Build default args for Lit
|
|
3613
|
+
let litArgs = '';
|
|
3614
|
+
if (name === 'Button') {
|
|
3615
|
+
litArgs = ` args: {
|
|
3616
|
+
variant: '',
|
|
3617
|
+
size: 'md',
|
|
3618
|
+
shape: '',
|
|
3619
|
+
bordered: false,
|
|
3620
|
+
ghost: false,
|
|
3621
|
+
disabled: false,
|
|
3622
|
+
loading: false,
|
|
3623
|
+
label: 'Button',
|
|
3624
|
+
},`;
|
|
3625
|
+
}
|
|
3626
|
+
else if (name === 'Dialog') {
|
|
3627
|
+
litArgs = ` args: {
|
|
3628
|
+
open: false,
|
|
3629
|
+
heading: 'Dialog title',
|
|
3630
|
+
description: 'Supporting description text goes here.',
|
|
3631
|
+
showCloseButton: true,
|
|
3632
|
+
},`;
|
|
3633
|
+
}
|
|
3634
|
+
else if (name === 'Drawer') {
|
|
3635
|
+
litArgs = ` args: {
|
|
3636
|
+
open: false,
|
|
3637
|
+
heading: 'Drawer',
|
|
3638
|
+
showCloseButton: true,
|
|
3639
|
+
},`;
|
|
3640
|
+
}
|
|
3641
|
+
else if (name === 'Toast') {
|
|
3642
|
+
litArgs = ` args: {
|
|
3643
|
+
open: false,
|
|
3644
|
+
type: 'info',
|
|
3645
|
+
position: 'top-right',
|
|
3646
|
+
showCloseButton: true,
|
|
3647
|
+
},`;
|
|
3648
|
+
}
|
|
3649
|
+
else if (name === 'Combobox') {
|
|
3650
|
+
litArgs = ` args: {
|
|
3651
|
+
options: FRUITS,
|
|
3652
|
+
label: 'Select a fruit',
|
|
3653
|
+
placeholder: 'Choose...',
|
|
3654
|
+
},`;
|
|
3655
|
+
}
|
|
3656
|
+
else if (FX_COMPONENTS.has(name)) {
|
|
3657
|
+
const defaultLabel = name === 'BadgeFx' ? 'BadgeFx' : name === 'ButtonFx' ? 'Click me' : name;
|
|
3658
|
+
litArgs = ` args: {
|
|
3659
|
+
fx: 'bounce',
|
|
3660
|
+
label: '${defaultLabel}',
|
|
3661
|
+
},`;
|
|
3662
|
+
}
|
|
3663
|
+
else if (TEXT_CHILD_COMPONENTS.has(name)) {
|
|
3664
|
+
litArgs = ` args: {
|
|
3665
|
+
label: '${name}',
|
|
3666
|
+
},`;
|
|
3667
|
+
}
|
|
3668
|
+
// Build render for meta level (only simple/text-child components, not open-controlled)
|
|
3669
|
+
let litRender = '';
|
|
3670
|
+
if (!OPEN_CONTROLLED_COMPONENTS.has(name) && name !== 'Button' && name !== 'Combobox') {
|
|
3671
|
+
if (TEXT_CHILD_COMPONENTS.has(name)) {
|
|
3672
|
+
litRender = ` render: (args: any) => html\`<${tag}>\${args.label}</${tag}>\`,`;
|
|
3673
|
+
}
|
|
3674
|
+
else if (FX_COMPONENTS.has(name)) {
|
|
3675
|
+
litRender = ` render: (args: any) => html\`<${tag} fx=\${args.fx}>\${args.label}</${tag}>\`,`;
|
|
3676
|
+
}
|
|
3677
|
+
else {
|
|
3678
|
+
litRender = ` render: (args: any) => html\`<${tag}></${tag}>\`,`;
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
else if (name === 'Combobox') {
|
|
3682
|
+
litRender = ` render: (args: any) => html\`
|
|
3683
|
+
<${tag}
|
|
3684
|
+
.options=\${args.options}
|
|
3685
|
+
label=\${args.label}
|
|
3686
|
+
placeholder=\${args.placeholder}
|
|
3687
|
+
?clearable=\${args.clearable}
|
|
3688
|
+
?disabled=\${args.disabled}
|
|
3689
|
+
?multiple=\${args.multiple}
|
|
3690
|
+
?invalid=\${args.invalid}
|
|
3691
|
+
></${tag}>
|
|
3692
|
+
\`,`;
|
|
3693
|
+
}
|
|
3694
|
+
else if (name === 'Button') {
|
|
3695
|
+
litRender = ` render: (args: any) => html\`
|
|
3696
|
+
<ag-button
|
|
3697
|
+
variant=\${args.variant}
|
|
3698
|
+
size=\${args.size}
|
|
3699
|
+
shape=\${args.shape}
|
|
3700
|
+
?bordered=\${args.bordered}
|
|
3701
|
+
?ghost=\${args.ghost}
|
|
3702
|
+
?disabled=\${args.disabled}
|
|
3703
|
+
?loading=\${args.loading}
|
|
3704
|
+
>\${args.label}</ag-button>
|
|
3705
|
+
\`,`;
|
|
3706
|
+
}
|
|
3707
|
+
const metaParts = [];
|
|
3708
|
+
metaParts.push(` title: 'AgnosticUI/${name}',`);
|
|
3709
|
+
metaParts.push(` component: '${tag}',`);
|
|
3710
|
+
metaParts.push(` tags: ['autodocs'],`);
|
|
3711
|
+
const needsLabelArgType = TEXT_CHILD_COMPONENTS.has(name) || FX_COMPONENTS.has(name);
|
|
3712
|
+
if (argTypes) {
|
|
3713
|
+
// For Lit, add a label argType for TEXT_CHILD and FX components
|
|
3714
|
+
if (needsLabelArgType) {
|
|
3715
|
+
// Insert label control before the closing ' },' of the argTypes block
|
|
3716
|
+
const closingIdx = argTypes.lastIndexOf('\n },');
|
|
3717
|
+
if (closingIdx !== -1) {
|
|
3718
|
+
const modified = argTypes.slice(0, closingIdx) + '\n label: { control: \'text\' },' + argTypes.slice(closingIdx);
|
|
3719
|
+
metaParts.push(modified);
|
|
3720
|
+
}
|
|
3721
|
+
else {
|
|
3722
|
+
metaParts.push(argTypes);
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
else {
|
|
3726
|
+
metaParts.push(argTypes);
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
else if (needsLabelArgType) {
|
|
3730
|
+
metaParts.push(` argTypes: {\n label: { control: 'text' },\n },`);
|
|
3731
|
+
}
|
|
3732
|
+
if (litArgs) {
|
|
3733
|
+
metaParts.push(litArgs);
|
|
3734
|
+
}
|
|
3735
|
+
if (litRender) {
|
|
3736
|
+
metaParts.push(litRender);
|
|
3737
|
+
}
|
|
3738
|
+
lines.push(`const meta: Meta = {`);
|
|
3739
|
+
lines.push(metaParts.join('\n'));
|
|
3740
|
+
lines.push(`} satisfies Meta;`);
|
|
3741
|
+
lines.push('');
|
|
3742
|
+
lines.push(`export default meta;`);
|
|
3743
|
+
lines.push(`type Story = StoryObj;`);
|
|
3744
|
+
lines.push('');
|
|
3745
|
+
// Default story
|
|
3746
|
+
if (name === 'Dialog') {
|
|
3747
|
+
lines.push(`export const Default: Story = {`);
|
|
3748
|
+
lines.push(` render: (args: any) => html\``);
|
|
3749
|
+
lines.push(` <div>`);
|
|
3750
|
+
lines.push(` <ag-button`);
|
|
3751
|
+
lines.push(` @click=\${(e: Event) => {`);
|
|
3752
|
+
lines.push(` const dialog = (e.target as HTMLElement).parentElement?.querySelector('${tag}') as any;`);
|
|
3753
|
+
lines.push(` if (dialog) dialog.open = true;`);
|
|
3754
|
+
lines.push(` }}`);
|
|
3755
|
+
lines.push(` >Open Dialog</ag-button>`);
|
|
3756
|
+
lines.push(` <${tag}`);
|
|
3757
|
+
lines.push(` .open=\${args.open}`);
|
|
3758
|
+
lines.push(` .heading=\${args.heading}`);
|
|
3759
|
+
lines.push(` .showCloseButton=\${args.showCloseButton}`);
|
|
3760
|
+
lines.push(` @dialog-close=\${(e: Event) => { (e.target as any).open = false; }}`);
|
|
3761
|
+
lines.push(` @dialog-cancel=\${(e: Event) => { (e.target as any).open = false; }}`);
|
|
3762
|
+
lines.push(` >`);
|
|
3763
|
+
lines.push(` <p>Dialog content goes here.</p>`);
|
|
3764
|
+
lines.push(` </${tag}>`);
|
|
3765
|
+
lines.push(` </div>`);
|
|
3766
|
+
lines.push(` \`,`);
|
|
3767
|
+
lines.push(`};`);
|
|
3768
|
+
}
|
|
3769
|
+
else if (name === 'Drawer') {
|
|
3770
|
+
lines.push(`export const Default: Story = {`);
|
|
3771
|
+
lines.push(` render: (args: any) => html\``);
|
|
3772
|
+
lines.push(` <div>`);
|
|
3773
|
+
lines.push(` <ag-button`);
|
|
3774
|
+
lines.push(` @click=\${(e: Event) => {`);
|
|
3775
|
+
lines.push(` const drawer = (e.target as HTMLElement).parentElement?.querySelector('${tag}') as any;`);
|
|
3776
|
+
lines.push(` if (drawer) drawer.open = true;`);
|
|
3777
|
+
lines.push(` }}`);
|
|
3778
|
+
lines.push(` >Open Drawer</ag-button>`);
|
|
3779
|
+
lines.push(` <${tag}`);
|
|
3780
|
+
lines.push(` .open=\${args.open}`);
|
|
3781
|
+
lines.push(` .heading=\${args.heading}`);
|
|
3782
|
+
lines.push(` .showCloseButton=\${args.showCloseButton}`);
|
|
3783
|
+
lines.push(` @drawer-close=\${(e: Event) => { (e.target as any).open = false; }}`);
|
|
3784
|
+
lines.push(` @drawer-cancel=\${(e: Event) => { (e.target as any).open = false; }}`);
|
|
3785
|
+
lines.push(` >`);
|
|
3786
|
+
lines.push(` <div style="padding: 1rem">Drawer content goes here.</div>`);
|
|
3787
|
+
lines.push(` </${tag}>`);
|
|
3788
|
+
lines.push(` </div>`);
|
|
3789
|
+
lines.push(` \`,`);
|
|
3790
|
+
lines.push(`};`);
|
|
3791
|
+
}
|
|
3792
|
+
else if (name === 'Toast') {
|
|
3793
|
+
lines.push(`export const Default: Story = {`);
|
|
3794
|
+
lines.push(` render: (args: any) => html\``);
|
|
3795
|
+
lines.push(` <div>`);
|
|
3796
|
+
lines.push(` <ag-button`);
|
|
3797
|
+
lines.push(` @click=\${(e: Event) => {`);
|
|
3798
|
+
lines.push(` const toast = (e.target as HTMLElement).parentElement?.querySelector('${tag}') as any;`);
|
|
3799
|
+
lines.push(` if (toast) toast.open = true;`);
|
|
3800
|
+
lines.push(` }}`);
|
|
3801
|
+
lines.push(` >Show Toast</ag-button>`);
|
|
3802
|
+
lines.push(` <${tag}`);
|
|
3803
|
+
lines.push(` .open=\${args.open}`);
|
|
3804
|
+
lines.push(` type=\${args.type}`);
|
|
3805
|
+
lines.push(` position=\${args.position}`);
|
|
3806
|
+
lines.push(` ?showCloseButton=\${args.showCloseButton}`);
|
|
3807
|
+
lines.push(` @toast-close=\${(e: Event) => { (e.target as any).open = false; }}`);
|
|
3808
|
+
lines.push(` >`);
|
|
3809
|
+
lines.push(` <span>Toast notification message</span>`);
|
|
3810
|
+
lines.push(` </${tag}>`);
|
|
3811
|
+
lines.push(` </div>`);
|
|
3812
|
+
lines.push(` \`,`);
|
|
3813
|
+
lines.push(`};`);
|
|
3814
|
+
}
|
|
3815
|
+
else if (name === 'Collapsible') {
|
|
3816
|
+
lines.push(`export const Default: Story = {`);
|
|
3817
|
+
lines.push(` render: () => html\``);
|
|
3818
|
+
lines.push(` <${tag}>`);
|
|
3819
|
+
lines.push(` <span slot="summary">Toggle details</span>`);
|
|
3820
|
+
lines.push(` <div>Collapsible content goes here.</div>`);
|
|
3821
|
+
lines.push(` </${tag}>`);
|
|
3822
|
+
lines.push(` \`,`);
|
|
3823
|
+
lines.push(`};`);
|
|
3824
|
+
}
|
|
3825
|
+
else if (name === 'Button') {
|
|
3826
|
+
lines.push(`export const Default: Story = {};`);
|
|
3827
|
+
lines.push('');
|
|
3828
|
+
lines.push(`export const Variants: Story = {`);
|
|
3829
|
+
lines.push(` render: () => html\``);
|
|
3830
|
+
lines.push(` <div style="display:flex;gap:8px;flex-wrap:wrap">`);
|
|
3831
|
+
lines.push(` <ag-button>Default</ag-button>`);
|
|
3832
|
+
lines.push(` <ag-button variant="primary">Primary</ag-button>`);
|
|
3833
|
+
lines.push(` <ag-button variant="secondary">Secondary</ag-button>`);
|
|
3834
|
+
lines.push(` <ag-button variant="success">Success</ag-button>`);
|
|
3835
|
+
lines.push(` <ag-button variant="warning">Warning</ag-button>`);
|
|
3836
|
+
lines.push(` <ag-button variant="danger">Danger</ag-button>`);
|
|
3837
|
+
lines.push(` <ag-button variant="monochrome">Monochrome</ag-button>`);
|
|
3838
|
+
lines.push(` </div>`);
|
|
3839
|
+
lines.push(` \`,`);
|
|
3840
|
+
lines.push(`};`);
|
|
3841
|
+
lines.push('');
|
|
3842
|
+
lines.push(`export const Sizes: Story = {`);
|
|
3843
|
+
lines.push(` render: () => html\``);
|
|
3844
|
+
lines.push(` <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">`);
|
|
3845
|
+
lines.push(` <ag-button size="x-sm">x-sm</ag-button>`);
|
|
3846
|
+
lines.push(` <ag-button size="sm">sm</ag-button>`);
|
|
3847
|
+
lines.push(` <ag-button size="md">md</ag-button>`);
|
|
3848
|
+
lines.push(` <ag-button size="lg">lg</ag-button>`);
|
|
3849
|
+
lines.push(` <ag-button size="xl">xl</ag-button>`);
|
|
3850
|
+
lines.push(` </div>`);
|
|
3851
|
+
lines.push(` \`,`);
|
|
3852
|
+
lines.push(`};`);
|
|
3853
|
+
lines.push('');
|
|
3854
|
+
lines.push(`export const Bordered: Story = {`);
|
|
3855
|
+
lines.push(` render: () => html\``);
|
|
3856
|
+
lines.push(` <div style="display:flex;gap:8px;flex-wrap:wrap">`);
|
|
3857
|
+
lines.push(` <ag-button bordered>Default</ag-button>`);
|
|
3858
|
+
lines.push(` <ag-button variant="primary" bordered>Primary</ag-button>`);
|
|
3859
|
+
lines.push(` <ag-button variant="success" bordered>Success</ag-button>`);
|
|
3860
|
+
lines.push(` <ag-button variant="danger" bordered>Danger</ag-button>`);
|
|
3861
|
+
lines.push(` </div>`);
|
|
3862
|
+
lines.push(` \`,`);
|
|
3863
|
+
lines.push(`};`);
|
|
3864
|
+
lines.push('');
|
|
3865
|
+
lines.push(`export const Disabled: Story = { args: { variant: 'primary', disabled: true } };`);
|
|
3866
|
+
lines.push(`export const Loading: Story = { args: { variant: 'primary', loading: true } };`);
|
|
3867
|
+
}
|
|
3868
|
+
else {
|
|
3869
|
+
lines.push(`export const Default: Story = {};`);
|
|
3870
|
+
}
|
|
3871
|
+
return STORY_FILE_HEADER + lines.join('\n') + '\n';
|
|
660
3872
|
}
|
|
661
3873
|
//# sourceMappingURL=stories.js.map
|