diff --git a/src/Toolkit/CHANGELOG.md b/src/Toolkit/CHANGELOG.md index 5ba248f5890..5306a502856 100644 --- a/src/Toolkit/CHANGELOG.md +++ b/src/Toolkit/CHANGELOG.md @@ -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 diff --git a/src/Toolkit/kits/shadcn/popover/assets/controllers/popover_controller.js b/src/Toolkit/kits/shadcn/popover/assets/controllers/popover_controller.js new file mode 100644 index 00000000000..fbb78f43ba1 --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/assets/controllers/popover_controller.js @@ -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', ''); + } + } + } +} diff --git a/src/Toolkit/kits/shadcn/popover/examples/Alignments.html.twig b/src/Toolkit/kits/shadcn/popover/examples/Alignments.html.twig new file mode 100644 index 00000000000..47943fdcf79 --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/examples/Alignments.html.twig @@ -0,0 +1,26 @@ +
+ + + Start + + + Aligned to start + + + + + Center + + + Aligned to center + + + + + End + + + Aligned to end + + +
diff --git a/src/Toolkit/kits/shadcn/popover/examples/Basic.html.twig b/src/Toolkit/kits/shadcn/popover/examples/Basic.html.twig new file mode 100644 index 00000000000..ea7475323fa --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/examples/Basic.html.twig @@ -0,0 +1,11 @@ + + + Open Popover + + +
+

Dimensions

+

Set the dimensions for the layer.

+
+
+
diff --git a/src/Toolkit/kits/shadcn/popover/examples/Demo.html.twig b/src/Toolkit/kits/shadcn/popover/examples/Demo.html.twig new file mode 100644 index 00000000000..7b106ae7f89 --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/examples/Demo.html.twig @@ -0,0 +1,31 @@ + + + Open Popover + + +
+
+

Dimensions

+

Set the dimensions for the layer.

+
+
+
+ Width + +
+
+ Max. width + +
+
+ Height + +
+
+ Max. height + +
+
+
+
+
diff --git a/src/Toolkit/kits/shadcn/popover/examples/Form.html.twig b/src/Toolkit/kits/shadcn/popover/examples/Form.html.twig new file mode 100644 index 00000000000..ebbff568849 --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/examples/Form.html.twig @@ -0,0 +1,23 @@ + + + Open Popover + + +
+
+

Dimensions

+

Set the dimensions for the layer.

+
+
+
+ Width + +
+
+ Height + +
+
+
+
+
diff --git a/src/Toolkit/kits/shadcn/popover/examples/RTL.html.twig b/src/Toolkit/kits/shadcn/popover/examples/RTL.html.twig new file mode 100644 index 00000000000..0994ccab395 --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/examples/RTL.html.twig @@ -0,0 +1,97 @@ +
+ {# Arabic #} +
+ + + يسار + + +
+

الأبعاد

+

تعيين الأبعاد للطبقة.

+
+
+
+ + + أعلى + + +
+

الأبعاد

+

تعيين الأبعاد للطبقة.

+
+
+
+ + + أسفل + + +
+

الأبعاد

+

تعيين الأبعاد للطبقة.

+
+
+
+ + + يمين + + +
+

الأبعاد

+

تعيين الأبعاد للطبقة.

+
+
+
+
+ + {# Hebrew #} +
+ + + שמאל + + +
+

מימדים

+

הגדר את המימדים לשכבה.

+
+
+
+ + + למעלה + + +
+

מימדים

+

הגדר את המימדים לשכבה.

+
+
+
+ + + למטה + + +
+

מימדים

+

הגדר את המימדים לשכבה.

+
+
+
+ + + ימין + + +
+

מימדים

+

הגדר את המימדים לשכבה.

+
+
+
+
+
diff --git a/src/Toolkit/kits/shadcn/popover/examples/Usage.html.twig b/src/Toolkit/kits/shadcn/popover/examples/Usage.html.twig new file mode 100644 index 00000000000..87298fae8af --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/examples/Usage.html.twig @@ -0,0 +1,8 @@ + + + Open popover + + + Popover content goes here. + + diff --git a/src/Toolkit/kits/shadcn/popover/manifest.json b/src/Toolkit/kits/shadcn/popover/manifest.json new file mode 100644 index 00000000000..927bc9317ce --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/manifest.json @@ -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"] + } +} diff --git a/src/Toolkit/kits/shadcn/popover/templates/components/Popover.html.twig b/src/Toolkit/kits/shadcn/popover/templates/components/Popover.html.twig new file mode 100644 index 00000000000..03ccb0ae36a --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/templates/components/Popover.html.twig @@ -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 -%} +
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 -%} +
diff --git a/src/Toolkit/kits/shadcn/popover/templates/components/Popover/Content.html.twig b/src/Toolkit/kits/shadcn/popover/templates/components/Popover/Content.html.twig new file mode 100644 index 00000000000..ee9361147ad --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/templates/components/Popover/Content.html.twig @@ -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 -%} + diff --git a/src/Toolkit/kits/shadcn/popover/templates/components/Popover/Trigger.html.twig b/src/Toolkit/kits/shadcn/popover/templates/components/Popover/Trigger.html.twig new file mode 100644 index 00000000000..420f6396f19 --- /dev/null +++ b/src/Toolkit/kits/shadcn/popover/templates/components/Popover/Trigger.html.twig @@ -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 -%} diff --git a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Alignments.html.twig__1.html b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Alignments.html.twig__1.html new file mode 100644 index 00000000000..879a37430b2 --- /dev/null +++ b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Alignments.html.twig__1.html @@ -0,0 +1,50 @@ + +
+
+ + +
+
+ + +
+
+ + +
+
\ No newline at end of file diff --git a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Basic.html.twig__1.html b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Basic.html.twig__1.html new file mode 100644 index 00000000000..1e8f19e45c9 --- /dev/null +++ b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Basic.html.twig__1.html @@ -0,0 +1,27 @@ + +
+ + +
\ No newline at end of file diff --git a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Demo.html.twig__1.html b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Demo.html.twig__1.html new file mode 100644 index 00000000000..6bd6761dc2c --- /dev/null +++ b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Demo.html.twig__1.html @@ -0,0 +1,67 @@ + +
+ + +
\ No newline at end of file diff --git a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Form.html.twig__1.html b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Form.html.twig__1.html new file mode 100644 index 00000000000..d760d3f793c --- /dev/null +++ b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file Form.html.twig__1.html @@ -0,0 +1,51 @@ + +
+ + +
\ No newline at end of file diff --git a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file RTL.html.twig__1.html b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file RTL.html.twig__1.html new file mode 100644 index 00000000000..5025c0e748f --- /dev/null +++ b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component popover, code file RTL.html.twig__1.html @@ -0,0 +1,183 @@ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
\ No newline at end of file