diff --git a/apps/dev-tool/app/components/components/button-group-story.tsx b/apps/dev-tool/app/components/components/button-group-story.tsx new file mode 100644 index 000000000..b3670b300 --- /dev/null +++ b/apps/dev-tool/app/components/components/button-group-story.tsx @@ -0,0 +1,369 @@ +'use client'; + +import { useMemo } from 'react'; + +import { Filter, Plus, Settings } from 'lucide-react'; + +import { Button } from '@kit/ui/button'; +import { + ButtonGroup, + ButtonGroupSeparator, + ButtonGroupText, +} from '@kit/ui/button-group'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@kit/ui/card'; +import { Label } from '@kit/ui/label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@kit/ui/select'; +import { Separator } from '@kit/ui/separator'; +import { Switch } from '@kit/ui/switch'; +import { cn } from '@kit/ui/utils'; + +import { + formatCodeBlock, + generatePropsString, + useStoryControls, +} from '../lib/story-utils'; +import { ComponentStoryLayout } from './story-layout'; +import { SimpleStorySelect } from './story-select'; + +interface ButtonGroupControls { + orientation: 'horizontal' | 'vertical'; + size: 'sm' | 'default' | 'lg'; + withLabel: boolean; + withSeparator: boolean; + withFilter: boolean; + withPrimary: boolean; + withSelect: boolean; + fullWidth: boolean; +} + +const orientationOptions = [ + { + value: 'horizontal', + label: 'Horizontal', + description: 'Buttons arranged side-by-side', + }, + { + value: 'vertical', + label: 'Vertical', + description: 'Stack buttons vertically', + }, +] as const; + +const sizeOptions = [ + { + value: 'sm', + label: 'Small', + description: 'Compact 36px controls', + }, + { + value: 'default', + label: 'Default', + description: 'Standard 40px controls', + }, + { + value: 'lg', + label: 'Large', + description: 'Spacious 44px controls', + }, +] as const; + +export function ButtonGroupStory() { + const { controls, updateControl } = useStoryControls({ + orientation: 'horizontal', + size: 'sm', + withLabel: false, + withSeparator: false, + withFilter: false, + withPrimary: false, + withSelect: false, + fullWidth: false, + }); + + const buttonGroupPropsString = useMemo( + () => + generatePropsString( + { + orientation: controls.orientation, + className: controls.fullWidth ? 'w-full' : undefined, + }, + { + orientation: 'horizontal', + className: undefined, + }, + ), + [controls.fullWidth, controls.orientation], + ); + + const generatedCode = useMemo(() => { + const separatorOrientation = + controls.orientation === 'vertical' ? 'horizontal' : 'vertical'; + + const buttonSizeProp = + controls.size === 'default' ? '' : ` size="${controls.size}"`; + const selectTriggerClasses = [ + 'w-[140px] justify-between', + controls.size === 'sm' ? 'h-9 text-sm' : null, + controls.size === 'lg' ? 'h-11 text-base' : null, + ] + .filter(Boolean) + .join(' '); + const labelClasses = [ + 'min-w-[120px] justify-between', + controls.size === 'sm' ? 'text-sm' : null, + controls.size === 'lg' ? 'text-base' : null, + 'gap-2', + ] + .filter(Boolean) + .join(' '); + + let code = ``; + + if (controls.withLabel) { + code += `\n `; + code += `\n Views`; + code += `\n `; + code += `\n `; + } + + code += `\n `; + code += `\n `; + code += `\n `; + + if (controls.withSeparator) { + code += `\n `; + } + + if (controls.withFilter) { + code += `\n `; + } + + if (controls.withSelect) { + code += `\n `; + } + + if (controls.withPrimary) { + code += `\n `; + code += `\n `; + code += `\n New view`; + code += `\n `; + } + + code += `\n`; + + return formatCodeBlock(code, [ + "import { Filter, Plus, Settings } from 'lucide-react';", + "import { Button } from '@kit/ui/button';", + "import { ButtonGroup, ButtonGroupSeparator, ButtonGroupText } from '@kit/ui/button-group';", + "import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@kit/ui/select';", + ]); + }, [buttonGroupPropsString, controls]); + + const separatorOrientation = + controls.orientation === 'vertical' ? 'horizontal' : 'vertical'; + + const preview = ( +
+ + {controls.withLabel && ( + + Views + + + )} + + + + + + {controls.withSeparator && ( + + )} + + {controls.withFilter && ( + + )} + + {controls.withPrimary && ( + + )} + +
+ ); + + const controlsPanel = ( + <> +
+ + updateControl('orientation', value)} + options={orientationOptions} + /> +
+ +
+ + updateControl('size', value)} + options={sizeOptions} + /> +
+ + + +
+ + updateControl('withLabel', checked)} + /> +
+ +
+ + updateControl('withFilter', checked)} + /> +
+ +
+ + updateControl('withPrimary', checked)} + /> +
+ + ); + + const examples = ( + + + Button group sizes + + Mirror the documentation examples with small, default, and large + buttons. + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + return ( + + ); +} + +export default ButtonGroupStory; diff --git a/apps/dev-tool/app/components/components/docs-content.tsx b/apps/dev-tool/app/components/components/docs-content.tsx index af0504fac..ad8de9d0b 100644 --- a/apps/dev-tool/app/components/components/docs-content.tsx +++ b/apps/dev-tool/app/components/components/docs-content.tsx @@ -19,7 +19,7 @@ export function DocsContent({ selectedComponent }: DocsContentProps) { } return ( -
+
}>
diff --git a/apps/dev-tool/app/components/components/empty-state-story.tsx b/apps/dev-tool/app/components/components/empty-state-story.tsx index ddb97276b..8b056e0fa 100644 --- a/apps/dev-tool/app/components/components/empty-state-story.tsx +++ b/apps/dev-tool/app/components/components/empty-state-story.tsx @@ -11,6 +11,7 @@ import { CardTitle, } from '@kit/ui/card'; import { + EmptyMedia, EmptyState, EmptyStateButton, EmptyStateHeading, @@ -290,7 +291,9 @@ export function EmptyStateStory() { - + + + No products Add your first product to start selling. @@ -299,7 +302,9 @@ export function EmptyStateStory() { - + + + No documents Upload or create your first document. diff --git a/apps/dev-tool/app/components/components/field-story.tsx b/apps/dev-tool/app/components/components/field-story.tsx new file mode 100644 index 000000000..a71004ac3 --- /dev/null +++ b/apps/dev-tool/app/components/components/field-story.tsx @@ -0,0 +1,468 @@ +'use client'; + +import { useMemo } from 'react'; + +import { Button } from '@kit/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@kit/ui/card'; +import { + Field, + FieldContent, + FieldDescription, + FieldError, + FieldGroup, + FieldLabel, + FieldLegend, + FieldSeparator, + FieldSet, + FieldTitle, +} from '@kit/ui/field'; +import { Input } from '@kit/ui/input'; +import { Label } from '@kit/ui/label'; +import { RadioGroup, RadioGroupItem } from '@kit/ui/radio-group'; +import { Separator } from '@kit/ui/separator'; +import { Switch } from '@kit/ui/switch'; +import { Textarea } from '@kit/ui/textarea'; + +import { + formatCodeBlock, + generatePropsString, + useStoryControls, +} from '../lib/story-utils'; +import { ComponentStoryLayout } from './story-layout'; +import { SimpleStorySelect } from './story-select'; + +interface FieldControls { + orientation: 'vertical' | 'horizontal'; + showDescriptions: boolean; + showErrors: boolean; + useLegend: boolean; + includeSeparator: boolean; +} + +const orientationOptions = [ + { + value: 'vertical', + label: 'Vertical', + description: 'Label and controls stacked', + }, + { + value: 'horizontal', + label: 'Horizontal', + description: 'Label inline with controls', + }, +] as const; + +export function FieldStory() { + const { controls, updateControl } = useStoryControls({ + orientation: 'horizontal', + showDescriptions: true, + showErrors: false, + useLegend: true, + includeSeparator: true, + }); + + const fieldPropsString = useMemo( + () => + generatePropsString( + { + orientation: controls.orientation, + }, + { orientation: 'vertical' }, + ), + [controls.orientation], + ); + + const generatedCode = useMemo(() => { + const separatorLine = controls.includeSeparator + ? '\n Preferences' + : ''; + + const descriptionLine = controls.showDescriptions + ? '\n The name that will appear on invoices.' + : ''; + + const errorLine = controls.showErrors + ? '\n ' + : ''; + + const code = `
+ ${controls.useLegend ? 'Account Details\n ' : ''} + + Full name + + ${descriptionLine}${errorLine} + + ${separatorLine} + + Email address + + ${ + controls.showDescriptions + ? '\n Used for sign-in and notifications.' + : '' + } + + + + Bio + +