Skip to content
Open
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
1 change: 1 addition & 0 deletions src/Toolkit/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Flowbite] Add `avatar` recipe
- [Flowbite] Add `dropdown` recipe
- [Shadcn] Add `hover-card` recipe
- [Shadcn] Add `popover` recipe
- [Shadcn] Add `resizable` recipe

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

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

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

connect() {
this.updateState();
}

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

close() {
this.openValue = false;
}

openValueChanged() {
this.updateState();

if (this.openValue && this.nameValue) {
window.dispatchEvent(new CustomEvent('popover:open', {
detail: { name: this.nameValue, source: this.element },
}));
}
}

handleGroupOpen(event) {
if (!this.nameValue || !this.openValue) {
return;
}
if (event.detail.name !== this.nameValue || event.detail.source === this.element) {
return;
}
this.openValue = false;
}

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', '');
}
}
}
}
26 changes: 26 additions & 0 deletions src/Toolkit/kits/shadcn/popover/examples/Alignments.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div class="flex gap-6">
<twig:Popover name="alignments-demo">
<twig:Popover:Trigger>
<twig:Button variant="outline" size="sm" {{ ...popover_trigger_attrs }}>Start</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content align="start" class="w-40">
Aligned to start
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="alignments-demo">
<twig:Popover:Trigger>
<twig:Button variant="outline" size="sm" {{ ...popover_trigger_attrs }}>Center</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content align="center" class="w-40">
Aligned to center
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="alignments-demo">
<twig:Popover:Trigger>
<twig:Button variant="outline" size="sm" {{ ...popover_trigger_attrs }}>End</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content align="end" class="w-40">
Aligned to end
</twig:Popover:Content>
</twig:Popover>
</div>
11 changes: 11 additions & 0 deletions src/Toolkit/kits/shadcn/popover/examples/Basic.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<twig:Popover>
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>Open Popover</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content align="start">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">Dimensions</h4>
<p class="text-xs text-muted-foreground">Set the dimensions for the layer.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
31 changes: 31 additions & 0 deletions src/Toolkit/kits/shadcn/popover/examples/Demo.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<twig:Popover>
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>Open Popover</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content align="start" class="w-80">
<div class="grid gap-4">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">Dimensions</h4>
<p class="text-xs text-muted-foreground">Set the dimensions for the layer.</p>
</div>
<div class="grid gap-2">
<div class="grid grid-cols-3 items-center gap-4">
<twig:Label for="demo-width">Width</twig:Label>
<input id="demo-width" class="col-span-2 h-8 rounded-md border px-2 text-sm" value="100%">
</div>
<div class="grid grid-cols-3 items-center gap-4">
<twig:Label for="demo-max-width">Max. width</twig:Label>
<input id="demo-max-width" class="col-span-2 h-8 rounded-md border px-2 text-sm" value="300px">
</div>
<div class="grid grid-cols-3 items-center gap-4">
<twig:Label for="demo-height">Height</twig:Label>
<input id="demo-height" class="col-span-2 h-8 rounded-md border px-2 text-sm" value="25px">
</div>
<div class="grid grid-cols-3 items-center gap-4">
<twig:Label for="demo-max-height">Max. height</twig:Label>
<input id="demo-max-height" class="col-span-2 h-8 rounded-md border px-2 text-sm" value="none">
</div>
</div>
</div>
</twig:Popover:Content>
</twig:Popover>
23 changes: 23 additions & 0 deletions src/Toolkit/kits/shadcn/popover/examples/Form.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<twig:Popover>
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>Open Popover</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content align="start" class="w-64">
<div class="grid gap-4">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">Dimensions</h4>
<p class="text-xs text-muted-foreground">Set the dimensions for the layer.</p>
</div>
<div class="grid gap-3">
<div class="flex items-center gap-3">
<twig:Label for="form-width" class="w-1/2">Width</twig:Label>
<input id="form-width" class="h-8 w-full rounded-md border px-2 text-sm" value="100%">
</div>
<div class="flex items-center gap-3">
<twig:Label for="form-height" class="w-1/2">Height</twig:Label>
<input id="form-height" class="h-8 w-full rounded-md border px-2 text-sm" value="25px">
</div>
</div>
</div>
</twig:Popover:Content>
</twig:Popover>
97 changes: 97 additions & 0 deletions src/Toolkit/kits/shadcn/popover/examples/RTL.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<div class="flex flex-col items-center gap-12 py-12">
{# Arabic #}
<div dir="rtl" class="flex flex-wrap justify-center gap-2">
<twig:Popover name="rtl-ar">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>يسار</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="left">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">الأبعاد</h4>
<p class="text-xs text-muted-foreground">تعيين الأبعاد للطبقة.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="rtl-ar">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>أعلى</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="top">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">الأبعاد</h4>
<p class="text-xs text-muted-foreground">تعيين الأبعاد للطبقة.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="rtl-ar">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>أسفل</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="bottom">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">الأبعاد</h4>
<p class="text-xs text-muted-foreground">تعيين الأبعاد للطبقة.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="rtl-ar">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>يمين</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="right">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">الأبعاد</h4>
<p class="text-xs text-muted-foreground">تعيين الأبعاد للطبقة.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
</div>

{# Hebrew #}
<div dir="rtl" class="flex flex-wrap justify-center gap-2">
<twig:Popover name="rtl-he">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>שמאל</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="left">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">מימדים</h4>
<p class="text-xs text-muted-foreground">הגדר את המימדים לשכבה.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="rtl-he">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>למעלה</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="top">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">מימדים</h4>
<p class="text-xs text-muted-foreground">הגדר את המימדים לשכבה.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="rtl-he">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>למטה</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="bottom">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">מימדים</h4>
<p class="text-xs text-muted-foreground">הגדר את המימדים לשכבה.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
<twig:Popover name="rtl-he">
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>ימין</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content side="right">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">מימדים</h4>
<p class="text-xs text-muted-foreground">הגדר את המימדים לשכבה.</p>
</div>
</twig:Popover:Content>
</twig:Popover>
</div>
</div>
8 changes: 8 additions & 0 deletions src/Toolkit/kits/shadcn/popover/examples/Usage.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<twig:Popover>
<twig:Popover:Trigger>
<twig:Button variant="outline" {{ ...popover_trigger_attrs }}>Open popover</twig:Button>
</twig:Popover:Trigger>
<twig:Popover:Content>
Popover content goes here.
</twig:Popover:Content>
</twig:Popover>
13 changes: 13 additions & 0 deletions src/Toolkit/kits/shadcn/popover/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": "Popover",
"description": "A click-triggered popup that displays rich content, anchored to its trigger.",
"copy-files": {
"assets/": "assets/",
"templates/": "templates/"
},
"dependencies": {
"composer": ["tales-from-a-dev/twig-tailwind-extra:^1.0.0"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{# @prop name string Group name — popovers sharing the same `name` are mutually exclusive (only one open at a time). #}
{# @prop open boolean Whether the popover is open on initial render. Defaults to `false` #}
{# @block content A `Popover:Trigger` and a `Popover:Content` #}
{%- props name = null, open = false -%}
{%- set _popover_open = open -%}
<div
class="{{ ('relative inline-block ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({
'data-slot': 'popover',
'data-controller': 'popover',
'data-popover-open-value': open ? 'true' : 'false',
'data-action': [
'popover:open@window->popover#handleGroupOpen',
'click@window->popover#handleOutsideClick',
'keydown.esc@window->popover#handleEscape',
]|join(' ')|html_attr_type('sst'),
'data-state': open ? 'open' : 'closed',
...(name ? {'data-popover-name-value': name} : {}),
}) }}
>
{%- block content %}{% endblock -%}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{# @prop side 'top'|'right'|'bottom'|'left' Which side of the trigger the content appears on. Defaults to `bottom` #}
{# @prop align 'start'|'center'|'end' Alignment along the cross axis. Defaults to `center` #}
{# @block content The content revealed when the popover is open #}
{%- props side = 'bottom', align = 'center' -%}
{%- set _is_horizontal = side in ['left', 'right'] -%}
{%- set _pos = {
top: 'bottom-full mb-2',
bottom: 'top-full mt-2',
left: 'right-full top-1/2 -translate-y-1/2 mr-2',
right: 'left-full top-1/2 -translate-y-1/2 ml-2',
}[side] -%}
{%- if not _is_horizontal -%}
{%- if align == 'start' -%}{%- set _pos = _pos ~ ' start-0' -%}
{%- elseif align == 'center' -%}{%- set _pos = _pos ~ ' start-1/2 -translate-x-1/2 rtl:translate-x-1/2' -%}
{%- else -%}{%- set _pos = _pos ~ ' end-0' -%}
{%- endif -%}
{%- endif -%}
<div
role="dialog"
class="{{ ('absolute z-50 w-72 rounded-md border bg-popover p-4 text-sm text-popover-foreground shadow-md outline-none ' ~ _pos ~ ' ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({
'data-slot': 'popover-content',
'data-popover-target': 'content',
'data-side': side,
'data-align': align,
'aria-hidden': not _popover_open ? 'true' : 'false',
'data-state': _popover_open ? 'open' : 'closed',
...(not _popover_open ? {hidden: ''} : {}),
}) }}
>
{%- block content %}{% endblock -%}
</div>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By doing this, you are forcing the user to use a button like as a trigger, this is not our philosophy.

Please check the existing *Trigger.html.twig, they are nearly-empty templates that expose a Twig variable ..._trigger_attrs, e.g. with AlertDialog/Trigger.html.twig:

{# @block content The trigger element (e.g., a `Button`) that opens the dialog when clicked #}
{%- set alert_dialog_trigger_attrs = {
    'data-action': 'click->alert-dialog#open'|html_attr_type('sst'),
    'data-alert-dialog-target': 'trigger',
    'aria-haspopup': 'dialog',
    'aria-expanded': 'false',
} -%}
{%- block content %}{% endblock -%}

Usage:

<twig:AlertDialog id="delete_account">
    <twig:AlertDialog:Trigger>
        <twig:Button variant="outline" {{ ...alert_dialog_trigger_attrs }}>
            Show Dialog
        </twig:Button>
    </twig:AlertDialog:Trigger>
    <twig:AlertDialog:Content>...</twig:AlertDialog:Content>
</twig:AlertDialog>

It also means that you must not use details and summary HTML elements.

Thanks!

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{# @block content The clickable trigger (e.g., a `Button`) that opens the popover #}
{%- set popover_trigger_attrs = {
'data-slot': 'popover-trigger',
'data-popover-target': 'trigger',
'data-action': 'click->popover#toggle'|html_attr_type('sst'),
'aria-haspopup': 'dialog',
'aria-expanded': _popover_open ? 'true' : 'false',
'data-state': _popover_open ? 'open' : 'closed',
} -%}
{%- block content %}{% endblock -%}
Loading
Loading