Skip to content
Draft
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
2 changes: 2 additions & 0 deletions src/Toolkit/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- [Toolkit] Add `avatar` recipe
- [Toolkit] Add `dropdown` recipe
- [Shadcn] Add `dropdown-menu` recipe
- [Shadcn] Add `sidebar` recipe

## 3.0.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
static targets = ['trigger', 'content'];

static values = {
open: { type: Boolean, default: false },
};

connect() {
this.updateState();
}

toggle(event) {
event?.preventDefault();
this.openValue = !this.openValue;
}

close() {
this.openValue = false;
}

openValueChanged() {
this.updateState();
}

handleOutsideClick(event) {
if (!this.openValue || this.element.contains(event.target)) {
return;
}
this.openValue = false;
}

handleEscape(event) {
if (!this.openValue || event.key !== 'Escape') {
return;
}
this.openValue = false;
this.triggerTargets[0]?.focus();
}

updateState() {
const open = this.openValue;
const state = open ? 'open' : 'closed';

this.element.dataset.state = state;

for (const trigger of this.triggerTargets) {
trigger.setAttribute('aria-expanded', String(open));
trigger.dataset.state = state;
}

for (const content of this.contentTargets) {
content.dataset.state = state;
content.setAttribute('aria-hidden', String(!open));
if (open) {
content.removeAttribute('hidden');
} else {
content.setAttribute('hidden', '');
}
}
}
}
29 changes: 29 additions & 0 deletions src/Toolkit/kits/shadcn/dropdown-menu/examples/Avatar.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<twig:DropdownMenu>
<twig:DropdownMenu:Trigger>
<twig:Button variant="ghost" size="icon" class="rounded-full" {{ ...dropdown_menu_trigger_attrs }}>
<twig:Avatar>
<twig:Avatar:Image src="https://github.com/shadcn.png" alt="shadcn" />
<twig:Avatar:Fallback>LR</twig:Avatar:Fallback>
</twig:Avatar>
</twig:Button>
</twig:DropdownMenu:Trigger>
<twig:DropdownMenu:Content class="end-0 start-auto w-48">
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:badge-check" class="me-2 size-4" />
Account
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:credit-card" class="me-2 size-4" />
Billing
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:bell" class="me-2 size-4" />
Notifications
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:log-out" class="me-2 size-4" />
Sign Out
</twig:DropdownMenu:Item>
</twig:DropdownMenu:Content>
</twig:DropdownMenu>
15 changes: 15 additions & 0 deletions src/Toolkit/kits/shadcn/dropdown-menu/examples/Basic.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<twig:DropdownMenu>
<twig:DropdownMenu:Trigger>
<twig:Button variant="outline" {{ ...dropdown_menu_trigger_attrs }}>Open</twig:Button>
</twig:DropdownMenu:Trigger>
<twig:DropdownMenu:Content>
<twig:DropdownMenu:Label>My Account</twig:DropdownMenu:Label>
<twig:DropdownMenu:Item>Profile</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Billing</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Settings</twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>GitHub</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Support</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item disabled>API</twig:DropdownMenu:Item>
</twig:DropdownMenu:Content>
</twig:DropdownMenu>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<twig:DropdownMenu>
<twig:DropdownMenu:Trigger>
<twig:Button variant="outline" {{ ...dropdown_menu_trigger_attrs }}>Notifications</twig:Button>
</twig:DropdownMenu:Trigger>
<twig:DropdownMenu:Content class="w-56">
<twig:DropdownMenu:Label>Notification Preferences</twig:DropdownMenu:Label>
{% set items = [
{name: 'email', icon: 'lucide:mail', label: 'Email notifications', checked: true},
{name: 'sms', icon: 'lucide:message-square', label: 'SMS notifications', checked: false},
{name: 'push', icon: 'lucide:bell', label: 'Push notifications', checked: true},
] %}
{% for it in items %}
<label class="flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground">
<input type="checkbox" class="peer sr-only" name="{{ it.name }}" {% if it.checked %}checked{% endif %}>
<twig:ux:icon name="{{ it.icon }}" class="me-2 size-4" />
{{ it.label }}
<span class="ms-auto flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
<twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
</span>
</label>
{% endfor %}
</twig:DropdownMenu:Content>
</twig:DropdownMenu>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<twig:DropdownMenu>
<twig:DropdownMenu:Trigger>
<twig:Button variant="outline" {{ ...dropdown_menu_trigger_attrs }}>Open</twig:Button>
</twig:DropdownMenu:Trigger>
<twig:DropdownMenu:Content class="w-40">
<twig:DropdownMenu:Label>Appearance</twig:DropdownMenu:Label>
<twig:DropdownMenu:CheckboxItem name="statusbar" checked>Status Bar</twig:DropdownMenu:CheckboxItem>
<twig:DropdownMenu:CheckboxItem name="activitybar" disabled>Activity Bar</twig:DropdownMenu:CheckboxItem>
<twig:DropdownMenu:CheckboxItem name="panel">Panel</twig:DropdownMenu:CheckboxItem>
</twig:DropdownMenu:Content>
</twig:DropdownMenu>
191 changes: 191 additions & 0 deletions src/Toolkit/kits/shadcn/dropdown-menu/examples/Complex.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<twig:DropdownMenu class="self-start mt-4">
<twig:DropdownMenu:Trigger>
<twig:Button variant="outline" {{ ...dropdown_menu_trigger_attrs }}>Complex Menu</twig:Button>
</twig:DropdownMenu:Trigger>
<twig:DropdownMenu:Content class="w-48">
<twig:DropdownMenu:Label>File</twig:DropdownMenu:Label>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:file" class="me-2 size-4" />
New File<twig:DropdownMenu:Shortcut>⌘N</twig:DropdownMenu:Shortcut>
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:folder" class="me-2 size-4" />
New Folder<twig:DropdownMenu:Shortcut>β‡§βŒ˜N</twig:DropdownMenu:Shortcut>
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Sub>
<twig:DropdownMenu:SubTrigger>
<twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...dropdown_menu_sub_trigger_attrs }}>
<twig:ux:icon name="lucide:folder-open" class="me-2 size-4" />
Open Recent
<twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
</twig:Button>
</twig:DropdownMenu:SubTrigger>
<twig:DropdownMenu:SubContent>
<twig:DropdownMenu:Label>Recent Projects</twig:DropdownMenu:Label>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:file-code" class="me-2 size-4" />
Project Alpha
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:file-code" class="me-2 size-4" />
Project Beta
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Sub>
<twig:DropdownMenu:SubTrigger>
<twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...dropdown_menu_sub_trigger_attrs }}>
<twig:ux:icon name="lucide:more-horizontal" class="me-2 size-4" />
More Projects
<twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
</twig:Button>
</twig:DropdownMenu:SubTrigger>
<twig:DropdownMenu:SubContent>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:file-code" class="me-2 size-4" />
Project Gamma
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:file-code" class="me-2 size-4" />
Project Delta
</twig:DropdownMenu:Item>
</twig:DropdownMenu:SubContent>
</twig:DropdownMenu:Sub>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:folder-search" class="me-2 size-4" />
Browse...
</twig:DropdownMenu:Item>
</twig:DropdownMenu:SubContent>
</twig:DropdownMenu:Sub>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:save" class="me-2 size-4" />
Save<twig:DropdownMenu:Shortcut>⌘S</twig:DropdownMenu:Shortcut>
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:download" class="me-2 size-4" />
Export<twig:DropdownMenu:Shortcut>β‡§βŒ˜E</twig:DropdownMenu:Shortcut>
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Label>View</twig:DropdownMenu:Label>
<label class="flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground">
<input type="checkbox" class="peer sr-only" name="sidebar" checked>
<twig:ux:icon name="lucide:eye" class="me-2 size-4" />
Show Sidebar
<span class="ms-auto flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
<twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
</span>
</label>
<label class="flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground">
<input type="checkbox" class="peer sr-only" name="statusbar">
<twig:ux:icon name="lucide:layout" class="me-2 size-4" />
Show Status Bar
<span class="ms-auto flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
<twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
</span>
</label>
<twig:DropdownMenu:Sub>
<twig:DropdownMenu:SubTrigger>
<twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...dropdown_menu_sub_trigger_attrs }}>
<twig:ux:icon name="lucide:palette" class="me-2 size-4" />
Theme
<twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
</twig:Button>
</twig:DropdownMenu:SubTrigger>
<twig:DropdownMenu:SubContent>
<twig:DropdownMenu:Label>Appearance</twig:DropdownMenu:Label>
{% set themes = [
{value: 'light', icon: 'lucide:sun', label: 'Light', checked: true},
{value: 'dark', icon: 'lucide:moon', label: 'Dark', checked: false},
{value: 'system', icon: 'lucide:monitor', label: 'System', checked: false},
] %}
{% for t in themes %}
<label class="flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground">
<input type="radio" class="peer sr-only" name="theme" value="{{ t.value }}" {% if t.checked %}checked{% endif %}>
<twig:ux:icon name="{{ t.icon }}" class="me-2 size-4" />
{{ t.label }}
<span class="ms-auto flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
<twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
</span>
</label>
{% endfor %}
</twig:DropdownMenu:SubContent>
</twig:DropdownMenu:Sub>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Label>Account</twig:DropdownMenu:Label>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:user" class="me-2 size-4" />
Profile<twig:DropdownMenu:Shortcut>β‡§βŒ˜P</twig:DropdownMenu:Shortcut>
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:credit-card" class="me-2 size-4" />
Billing
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Sub>
<twig:DropdownMenu:SubTrigger>
<twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...dropdown_menu_sub_trigger_attrs }}>
<twig:ux:icon name="lucide:settings" class="me-2 size-4" />
Settings
<twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
</twig:Button>
</twig:DropdownMenu:SubTrigger>
<twig:DropdownMenu:SubContent>
<twig:DropdownMenu:Label>Preferences</twig:DropdownMenu:Label>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:keyboard" class="me-2 size-4" />
Keyboard Shortcuts
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:languages" class="me-2 size-4" />
Language
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Sub>
<twig:DropdownMenu:SubTrigger>
<twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...dropdown_menu_sub_trigger_attrs }}>
<twig:ux:icon name="lucide:bell" class="me-2 size-4" />
Notifications
<twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
</twig:Button>
</twig:DropdownMenu:SubTrigger>
<twig:DropdownMenu:SubContent>
<twig:DropdownMenu:Label>Notification Types</twig:DropdownMenu:Label>
<label class="flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground">
<input type="checkbox" class="peer sr-only" name="push" checked>
<twig:ux:icon name="lucide:bell" class="me-2 size-4" />
Push Notifications
<span class="ms-auto flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
<twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
</span>
</label>
<label class="flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground">
<input type="checkbox" class="peer sr-only" name="emailnotif" checked>
<twig:ux:icon name="lucide:mail" class="me-2 size-4" />
Email Notifications
<span class="ms-auto flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
<twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
</span>
</label>
</twig:DropdownMenu:SubContent>
</twig:DropdownMenu:Sub>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:shield" class="me-2 size-4" />
Privacy &amp; Security
</twig:DropdownMenu:Item>
</twig:DropdownMenu:SubContent>
</twig:DropdownMenu:Sub>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:help-circle" class="me-2 size-4" />
Help &amp; Support
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>
<twig:ux:icon name="lucide:file-text" class="me-2 size-4" />
Documentation
</twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item class="text-destructive focus:text-destructive hover:text-destructive hover:bg-destructive/10">
<twig:ux:icon name="lucide:log-out" class="me-2 size-4" />
Sign Out<twig:DropdownMenu:Shortcut>β‡§βŒ˜Q</twig:DropdownMenu:Shortcut>
</twig:DropdownMenu:Item>
</twig:DropdownMenu:Content>
</twig:DropdownMenu>
34 changes: 34 additions & 0 deletions src/Toolkit/kits/shadcn/dropdown-menu/examples/Demo.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<twig:DropdownMenu>
<twig:DropdownMenu:Trigger>
<twig:Button variant="outline" {{ ...dropdown_menu_trigger_attrs }}>Open</twig:Button>
</twig:DropdownMenu:Trigger>
<twig:DropdownMenu:Content class="w-40">
<twig:DropdownMenu:Label>My Account</twig:DropdownMenu:Label>
<twig:DropdownMenu:Item>Profile<twig:DropdownMenu:Shortcut>β‡§βŒ˜P</twig:DropdownMenu:Shortcut></twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Billing<twig:DropdownMenu:Shortcut>⌘B</twig:DropdownMenu:Shortcut></twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Settings<twig:DropdownMenu:Shortcut>⌘S</twig:DropdownMenu:Shortcut></twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>Team</twig:DropdownMenu:Item>
<twig:DropdownMenu:Sub>
<twig:DropdownMenu:SubTrigger>
<twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...dropdown_menu_sub_trigger_attrs }}>
Invite users
<twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
</twig:Button>
</twig:DropdownMenu:SubTrigger>
<twig:DropdownMenu:SubContent>
<twig:DropdownMenu:Item>Email</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Message</twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>More...</twig:DropdownMenu:Item>
</twig:DropdownMenu:SubContent>
</twig:DropdownMenu:Sub>
<twig:DropdownMenu:Item>New Team<twig:DropdownMenu:Shortcut>⌘+T</twig:DropdownMenu:Shortcut></twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>GitHub</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item>Support</twig:DropdownMenu:Item>
<twig:DropdownMenu:Item disabled>API</twig:DropdownMenu:Item>
<twig:DropdownMenu:Separator />
<twig:DropdownMenu:Item>Log out<twig:DropdownMenu:Shortcut>β‡§βŒ˜Q</twig:DropdownMenu:Shortcut></twig:DropdownMenu:Item>
</twig:DropdownMenu:Content>
</twig:DropdownMenu>
Loading