Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions src/Toolkit/kits/shadcn/button-group/EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Examples

## Default

```twig {"preview":true,"height":"220px"}
<twig:ButtonGroup>
<twig:ButtonGroup class="hidden sm:flex">
<twig:Button variant="outline" size="icon" aria-label="Go back">
<twig:ux:icon name="lucide:arrow-left" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">Archive</twig:Button>
<twig:Button variant="outline">Report</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">
<twig:ux:icon name="lucide:clock" class="size-4" />
Snooze
</twig:Button>
<twig:Button variant="outline" size="icon" aria-label="More options">
<twig:ux:icon name="lucide:more-horizontal" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
</twig:ButtonGroup>
```

## Orientation

```twig {"preview":true,"height":"200px"}
<twig:ButtonGroup orientation="vertical" aria-label="Media controls" class="h-fit">
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" class="size-4" />
</twig:Button>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:minus" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
```

## Size

```twig {"preview":true,"height":"420px"}
<div class="flex flex-col items-start gap-8">
<twig:ButtonGroup>
<twig:Button variant="outline" size="sm">Small</twig:Button>
<twig:Button variant="outline" size="sm">Button</twig:Button>
<twig:Button variant="outline" size="sm">Group</twig:Button>
<twig:Button variant="outline" size="icon-sm">
<twig:ux:icon name="lucide:plus" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">Default</twig:Button>
<twig:Button variant="outline">Button</twig:Button>
<twig:Button variant="outline">Group</twig:Button>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline" size="lg">Large</twig:Button>
<twig:Button variant="outline" size="lg">Button</twig:Button>
<twig:Button variant="outline" size="lg">Group</twig:Button>
<twig:Button variant="outline" size="icon-lg">
<twig:ux:icon name="lucide:plus" class="size-5" />
</twig:Button>
</twig:ButtonGroup>
</div>
```

## Input

```twig {"preview":true,"height":"200px"}
<twig:ButtonGroup class="max-w-md">
<twig:Input placeholder="Search..." />
<twig:Button size="icon-lg" variant="outline" aria-label="Search">
<twig:ux:icon name="lucide:search" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
```

## Nested

```twig {"preview":true,"height":"240px"}
<twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline" size="sm">1</twig:Button>
<twig:Button variant="outline" size="sm">2</twig:Button>
<twig:Button variant="outline" size="sm">3</twig:Button>
<twig:Button variant="outline" size="sm">4</twig:Button>
<twig:Button variant="outline" size="sm">5</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline" size="icon-sm" aria-label="Previous">
<twig:ux:icon name="lucide:arrow-left" class="size-4" />
</twig:Button>
<twig:Button variant="outline" size="icon-sm" aria-label="Next">
<twig:ux:icon name="lucide:arrow-right" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
</twig:ButtonGroup>
```

## Separator

```twig {"preview":true,"height":"200px"}
<twig:ButtonGroup>
<twig:Button variant="secondary" size="sm">Copy</twig:Button>
<twig:ButtonGroup:Separator />
<twig:Button variant="secondary" size="sm">Paste</twig:Button>
</twig:ButtonGroup>
```

## Split

```twig {"preview":true,"height":"200px"}
<twig:ButtonGroup>
<twig:Button variant="secondary">Button</twig:Button>
<twig:ButtonGroup:Separator />
<twig:Button size="icon" variant="secondary">
<twig:ux:icon name="tabler:plus" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
```
13 changes: 13 additions & 0 deletions src/Toolkit/kits/shadcn/button-group/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "../../../schema-kit-recipe-v1.json",
"type": "component",
"name": "Button Group",
"description": "A layout helper for grouping buttons and related controls with shared borders and separators.",
"copy-files": {
"templates/": "templates/"
},
"dependencies": {
"composer": ["twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra:^1.0.0"],
"recipe": ["separator"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{# @prop orientation 'horizontal'|'vertical' The orientation, default to `horizontal` #}
{# @block content The default block #}
{%- props orientation = 'horizontal' -%}
{%- set style = html_cva(
base: 'flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*=\'w-\'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2',
variants: {
orientation: {
horizontal: '[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none',
vertical: 'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none',
},
},
default_variant: {
orientation: 'horizontal',
},
) -%}

<div
role="group"
data-slot="button-group"
data-orientation="{{ orientation }}"
class="{{ style.apply({orientation: orientation}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{# @prop orientation 'horizontal'|'vertical' The orientation of the separator, default to `vertical` #}
{# @block content The default block #}
{%- props orientation = 'vertical' -%}
<twig:Separator
orientation="{{ orientation }}"
data-slot="button-group-separator"
class="bg-input relative m-0! self-stretch {{ orientation == 'vertical' ? 'h-auto' }} {{ attributes.render('class')|tailwind_merge }}"
{{ ...attributes }}
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{# @prop as 'div' The HTML tag to use, default to `div` #}
{# @block content The default block #}
{%- props as = 'div' -%}
<{{ as }}
data-slot="button-group-text"
class="{{ 'bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 ' ~ attributes.render('class')|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</{{ as }}>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!--
- Kit: Shadcn UI
- Component: Button Group
- Code:
```twig
<twig:ButtonGroup>
<twig:ButtonGroup class="hidden sm:flex">
<twig:Button variant="outline" size="icon" aria-label="Go back">
<twig:ux:icon name="lucide:arrow-left" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">Archive</twig:Button>
<twig:Button variant="outline">Report</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">
<twig:ux:icon name="lucide:clock" class="size-4" />
Snooze
</twig:Button>
<twig:Button variant="outline" size="icon" aria-label="More options">
<twig:ux:icon name="lucide:more-horizontal" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
</twig:ButtonGroup>
```
- Rendered code (prettified for testing purposes, run "php vendor/bin/phpunit -d --update-snapshots" to update snapshots): -->
<div role="group" data-slot="button-group" data-orientation="horizontal" class="flex w-fit items-stretch [&amp;&gt;*]:focus-visible:z-10 [&amp;&gt;*]:focus-visible:relative [&amp;&gt;[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&amp;&gt;input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&amp;&gt;[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[&gt;[data-slot=button-group]]:gap-2 [&amp;&gt;*:not(:first-child)]:rounded-l-none [&amp;&gt;*:not(:first-child)]:border-l-0 [&amp;&gt;*:not(:last-child)]:rounded-r-none">
<div role="group" data-slot="button-group" data-orientation="horizontal" class="w-fit items-stretch [&amp;&gt;*]:focus-visible:z-10 [&amp;&gt;*]:focus-visible:relative [&amp;&gt;[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&amp;&gt;input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&amp;&gt;[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[&gt;[data-slot=button-group]]:gap-2 [&amp;&gt;*:not(:first-child)]:rounded-l-none [&amp;&gt;*:not(:first-child)]:border-l-0 [&amp;&gt;*:not(:last-child)]:rounded-r-none hidden sm:flex">
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 size-9" aria-label="Go back"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="currentColor" class="size-4" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m12 19l-7-7l7-7m7 7H5"></path></svg>
</button>
</div>
<div role="group" data-slot="button-group" data-orientation="horizontal" class="flex w-fit items-stretch [&amp;&gt;*]:focus-visible:z-10 [&amp;&gt;*]:focus-visible:relative [&amp;&gt;[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&amp;&gt;input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&amp;&gt;[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[&gt;[data-slot=button-group]]:gap-2 [&amp;&gt;*:not(:first-child)]:rounded-l-none [&amp;&gt;*:not(:first-child)]:border-l-0 [&amp;&gt;*:not(:last-child)]:rounded-r-none">
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[&gt;svg]:px-3">Archive</button>
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[&gt;svg]:px-3">Report</button>
</div>
<div role="group" data-slot="button-group" data-orientation="horizontal" class="flex w-fit items-stretch [&amp;&gt;*]:focus-visible:z-10 [&amp;&gt;*]:focus-visible:relative [&amp;&gt;[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&amp;&gt;input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&amp;&gt;[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[&gt;[data-slot=button-group]]:gap-2 [&amp;&gt;*:not(:first-child)]:rounded-l-none [&amp;&gt;*:not(:first-child)]:border-l-0 [&amp;&gt;*:not(:last-child)]:rounded-r-none">
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 h-9 px-4 py-2 has-[&gt;svg]:px-3"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="currentColor" class="size-4" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></g></svg>
Snooze
</button>
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 size-9" aria-label="More options"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="currentColor" class="size-4" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle></g></svg>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--
- Kit: Shadcn UI
- Component: Button Group
- Code:
```twig
<twig:ButtonGroup orientation="vertical" aria-label="Media controls" class="h-fit">
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" class="size-4" />
</twig:Button>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:minus" class="size-4" />
</twig:Button>
</twig:ButtonGroup>
```
- Rendered code (prettified for testing purposes, run "php vendor/bin/phpunit -d --update-snapshots" to update snapshots): -->
<div role="group" data-slot="button-group" data-orientation="vertical" class="flex w-fit items-stretch [&amp;&gt;*]:focus-visible:z-10 [&amp;&gt;*]:focus-visible:relative [&amp;&gt;[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&amp;&gt;input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&amp;&gt;[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[&gt;[data-slot=button-group]]:gap-2 flex-col [&amp;&gt;*:not(:first-child)]:rounded-t-none [&amp;&gt;*:not(:first-child)]:border-t-0 [&amp;&gt;*:not(:last-child)]:rounded-b-none h-fit" aria-label="Media controls">
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 size-9"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="currentColor" class="size-4" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14m-7-7v14"></path></svg>
</button>
<button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&amp;_svg]:pointer-events-none [&amp;_svg:not([class*='size-'])]:size-4 shrink-0 [&amp;_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 size-9"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" fill="currentColor" class="size-4" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14"></path></svg>
</button>
</div>
Loading
Loading