diff --git a/src/Toolkit/kits/shadcn/button-group/EXAMPLES.md b/src/Toolkit/kits/shadcn/button-group/EXAMPLES.md index 2fcb6f20992..f9515136f38 100644 --- a/src/Toolkit/kits/shadcn/button-group/EXAMPLES.md +++ b/src/Toolkit/kits/shadcn/button-group/EXAMPLES.md @@ -2,7 +2,7 @@ ## Default -```twig {"preview":true,"height":"220px"} +```twig {"preview":true,"height":"320px"} ``` diff --git a/src/Toolkit/kits/shadcn/button-group/manifest.json b/src/Toolkit/kits/shadcn/button-group/manifest.json index 562ee0f7661..96ead31c4fb 100644 --- a/src/Toolkit/kits/shadcn/button-group/manifest.json +++ b/src/Toolkit/kits/shadcn/button-group/manifest.json @@ -8,6 +8,6 @@ }, "dependencies": { "composer": ["twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra:^1.0.0"], - "recipe": ["separator"] + "recipe": ["dropdown-menu", "separator"] } } diff --git a/src/Toolkit/kits/shadcn/button-group/templates/components/ButtonGroup.html.twig b/src/Toolkit/kits/shadcn/button-group/templates/components/ButtonGroup.html.twig index 4ff7ea3c364..cb3dcd445e9 100644 --- a/src/Toolkit/kits/shadcn/button-group/templates/components/ButtonGroup.html.twig +++ b/src/Toolkit/kits/shadcn/button-group/templates/components/ButtonGroup.html.twig @@ -1,12 +1,14 @@ {# @prop orientation 'horizontal'|'vertical' The orientation, default to `horizontal` #} {# @block content The default block #} {%- props orientation = 'horizontal' -%} +{%- set _dropdown_h = '[&>[data-controller=dropdown-menu]:not(:first-child)_button]:rounded-l-none [&>[data-controller=dropdown-menu]:not(:first-child)_button]:border-l-0 [&>[data-controller=dropdown-menu]:not(:last-child)_button]:rounded-r-none' -%} +{%- set _dropdown_v = '[&>[data-controller=dropdown-menu]:not(:first-child)_button]:rounded-t-none [&>[data-controller=dropdown-menu]:not(:first-child)_button]:border-t-0 [&>[data-controller=dropdown-menu]:not(:last-child)_button]:rounded-b-none' -%} {%- 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', + horizontal: '[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none ' ~ _dropdown_h, + vertical: 'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none ' ~ _dropdown_v, }, }, default_variant: { diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/EXAMPLES.md b/src/Toolkit/kits/shadcn/dropdown-menu/EXAMPLES.md new file mode 100644 index 00000000000..6911703b624 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/EXAMPLES.md @@ -0,0 +1,219 @@ +# Examples + +## Default + +```twig {"preview":true,"height":"360px"} + + + Open + + + My Account + + + + + Profile + ⇧⌘P + + + + Billing + ⌘B + + + + Settings + ⌘S + + + + Keyboard shortcuts + ⌘K + + + + + + + Team + + + + API + + + + + + Log out + ⇧⌘Q + + + +``` + +## Checkboxes + +```twig {"preview":true,"height":"320px"} + + + Open + + + Appearance + + + Status Bar + + + Activity Bar + + + Panel + + + Full Screen + + + +``` + +## Radio Group + +```twig {"preview":true,"height":"280px"} + + + Open + + + Panel Position + + + Top + Bottom + Right + + + +``` + +## Alignment + +```twig {"preview":true,"height":"360px"} +
+ + + Start + + + + + New File + + + + New Folder + + + + + + + Center + + + + + New File + + + + New Folder + + + + + + + End + + + + + New File + + + + New Folder + + + +
+``` + +## With Dialog + +```twig {"preview":true,"height":"500px"} + + + + Open + + + My Account + + + + + Profile + ⇧⌘P + + + + Billing + ⌘B + + + + Settings + ⌘S + + + + + + + + + Invite team members + + Invite your team members to collaborate. + + +
+
+ Email address + +
+
+ + + Cancel + + Send invite + +
+
+``` + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/assets/controllers/dropdown_menu_controller.js b/src/Toolkit/kits/shadcn/dropdown-menu/assets/controllers/dropdown_menu_controller.js new file mode 100644 index 00000000000..cd08c410046 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/assets/controllers/dropdown_menu_controller.js @@ -0,0 +1,400 @@ +import { Controller } from '@hotwired/stimulus'; +import { computePosition, flip, shift, offset, autoUpdate } from '@floating-ui/dom'; + +export default class extends Controller { + static targets = ['trigger', 'content', 'item', 'radioGroup']; + static values = { + open: Boolean, + side: { type: String, default: 'bottom' }, + align: { type: String, default: 'start' }, + sideOffset: { type: Number, default: 8 }, + alignOffset: { type: Number, default: 0 }, + }; + + _cleanupAutoUpdate = null; + + connect() { + this._boundOnDocumentPointerDown = this._onDocumentPointerDown.bind(this); + this._boundOnDocumentFocusIn = this._onDocumentFocusIn.bind(this); + this._boundOnDocumentKeyDown = this._onDocumentKeyDown.bind(this); + + this._setOpen(this.openValue ?? false, { focus: false }); + } + + disconnect() { + if (this._cleanupAutoUpdate) { + this._cleanupAutoUpdate(); + this._cleanupAutoUpdate = null; + } + this._removeGlobalListeners(); + } + + toggle(event) { + if (event) { + event.preventDefault(); + } + + if (this._isOpen()) { + this.close({ focus: true }); + } else { + this.open({ focus: false }); + } + } + + open({ focus } = { focus: true }) { + if (this._isOpen()) { + return; + } + + this._setOpen(true, { focus: false }); + + if (focus) { + this._focusFirstItem(); + } + } + + close({ focus } = { focus: true }) { + if (!this._isOpen()) { + return; + } + + this._setOpen(false, { focus: false }); + + if (focus && this.hasTriggerTarget) { + this.triggerTarget.focus(); + } + } + + closeFromItem(event) { + const item = event.currentTarget; + if (!item || item.getAttribute('aria-disabled') === 'true' || item.dataset.disabled === 'true') { + event.preventDefault(); + return; + } + + const closeOnSelect = item.dataset.closeOnSelect; + if (closeOnSelect === 'false') { + return; + } + + this.close({ focus: true }); + } + + toggleCheckbox(event) { + const item = event.currentTarget; + if (!item || item.getAttribute('aria-disabled') === 'true' || item.dataset.disabled === 'true') { + event.preventDefault(); + return; + } + + const isChecked = item.getAttribute('aria-checked') === 'true'; + const newChecked = !isChecked; + + item.setAttribute('aria-checked', newChecked ? 'true' : 'false'); + + const indicator = item.querySelector('[data-checkbox-indicator]'); + if (indicator) { + indicator.hidden = !newChecked; + } + + const closeOnSelect = item.dataset.closeOnSelect; + if (closeOnSelect === 'true') { + this.close({ focus: true }); + } + } + + selectRadio(event) { + const item = event.currentTarget; + if (!item || item.getAttribute('aria-disabled') === 'true' || item.dataset.disabled === 'true') { + event.preventDefault(); + return; + } + + const radioGroup = item.closest('[data-dropdown-menu-target="radioGroup"]'); + if (!radioGroup) { + return; + } + + const value = item.dataset.value; + radioGroup.dataset.value = value; + + const radioItems = radioGroup.querySelectorAll('[role="menuitemradio"]'); + radioItems.forEach((radioItem) => { + const isSelected = radioItem.dataset.value === value; + radioItem.setAttribute('aria-checked', isSelected ? 'true' : 'false'); + + const indicator = radioItem.querySelector('[data-radio-indicator]'); + if (indicator) { + indicator.hidden = !isSelected; + } + }); + + const closeOnSelect = item.dataset.closeOnSelect; + if (closeOnSelect === 'true') { + this.close({ focus: true }); + } + } + + onTriggerKeydown(event) { + const key = event.key; + + if (key === 'ArrowDown') { + event.preventDefault(); + this.open({ focus: false }); + this._focusFirstItem(); + return; + } + + if (key === 'ArrowUp') { + event.preventDefault(); + this.open({ focus: false }); + this._focusLastItem(); + return; + } + + if (key === 'Enter' || key === ' ') { + event.preventDefault(); + this.toggle(); + } + } + + onContentKeydown(event) { + if (!this._isOpen()) { + return; + } + + const key = event.key; + + if (key === 'Escape') { + event.preventDefault(); + this.close({ focus: true }); + return; + } + + if (key === 'ArrowDown') { + event.preventDefault(); + this._focusNextItem(); + return; + } + + if (key === 'ArrowUp') { + event.preventDefault(); + this._focusPrevItem(); + return; + } + + if (key === 'Home') { + event.preventDefault(); + this._focusFirstItem(); + return; + } + + if (key === 'End') { + event.preventDefault(); + this._focusLastItem(); + } + } + + _isOpen() { + return !this.contentTarget.hasAttribute('hidden'); + } + + _getPlacement() { + const side = this.sideValue; + const align = this.alignValue; + + if (align === 'center') { + return side; + } + return `${side}-${align}`; + } + + async _updatePosition() { + if (!this.hasTriggerTarget || !this.hasContentTarget) { + return; + } + + const placement = this._getPlacement(); + + const { x, y, placement: finalPlacement } = await computePosition( + this.triggerTarget, + this.contentTarget, + { + placement, + middleware: [ + offset({ + mainAxis: this.sideOffsetValue, + crossAxis: this.alignOffsetValue, + }), + flip({ + fallbackAxisSideDirection: 'start', + padding: 8, + }), + shift({ + padding: 8, + }), + ], + } + ); + + Object.assign(this.contentTarget.style, { + left: `${x}px`, + top: `${y}px`, + }); + + const [side, align = 'center'] = finalPlacement.split('-'); + this.contentTarget.dataset.side = side; + this.contentTarget.dataset.align = align; + } + + _setOpen(open, { focus } = { focus: false }) { + if (!this.hasContentTarget || !this.hasTriggerTarget) { + return; + } + + if (open) { + this.contentTarget.removeAttribute('hidden'); + this.triggerTarget.setAttribute('aria-expanded', 'true'); + this.element.dataset.state = 'open'; + this.triggerTarget.dataset.state = 'open'; + this.contentTarget.dataset.state = 'open'; + + this._cleanupAutoUpdate = autoUpdate( + this.triggerTarget, + this.contentTarget, + () => this._updatePosition(), + { + ancestorScroll: true, + ancestorResize: true, + elementResize: true, + layoutShift: true, + } + ); + + this._addGlobalListeners(); + } else { + if (this._cleanupAutoUpdate) { + this._cleanupAutoUpdate(); + this._cleanupAutoUpdate = null; + } + + this.contentTarget.style.left = ''; + this.contentTarget.style.top = ''; + + this.contentTarget.setAttribute('hidden', ''); + this.triggerTarget.setAttribute('aria-expanded', 'false'); + this.element.dataset.state = 'closed'; + this.triggerTarget.dataset.state = 'closed'; + this.contentTarget.dataset.state = 'closed'; + this._removeGlobalListeners(); + } + + if (focus) { + this._focusFirstItem(); + } + } + + _addGlobalListeners() { + document.addEventListener('pointerdown', this._boundOnDocumentPointerDown, true); + document.addEventListener('focusin', this._boundOnDocumentFocusIn, true); + document.addEventListener('keydown', this._boundOnDocumentKeyDown, true); + } + + _removeGlobalListeners() { + document.removeEventListener('pointerdown', this._boundOnDocumentPointerDown, true); + document.removeEventListener('focusin', this._boundOnDocumentFocusIn, true); + document.removeEventListener('keydown', this._boundOnDocumentKeyDown, true); + } + + _onDocumentPointerDown(event) { + if (!this._isOpen()) { + return; + } + + if (!this.element.contains(event.target)) { + this.close({ focus: false }); + } + } + + _onDocumentFocusIn(event) { + if (!this._isOpen()) { + return; + } + + if (!this.element.contains(event.target)) { + this.close({ focus: false }); + } + } + + _onDocumentKeyDown(event) { + if (!this._isOpen()) { + return; + } + + if (event.key !== 'Tab') { + return; + } + + this.close({ focus: false }); + } + + _enabledItems() { + return this.itemTargets.filter((item) => { + if (item.getAttribute('aria-disabled') === 'true') { + return false; + } + if (item.dataset.disabled === 'true') { + return false; + } + if (item.hasAttribute('disabled')) { + return false; + } + + return true; + }); + } + + _focusFirstItem() { + const items = this._enabledItems(); + if (items.length === 0) { + return; + } + + items[0].focus(); + } + + _focusLastItem() { + const items = this._enabledItems(); + if (items.length === 0) { + return; + } + + items[items.length - 1].focus(); + } + + _focusNextItem() { + const items = this._enabledItems(); + if (items.length === 0) { + return; + } + + const active = document.activeElement; + const index = items.indexOf(active); + const nextIndex = index === -1 ? 0 : (index + 1) % items.length; + + items[nextIndex].focus(); + } + + _focusPrevItem() { + const items = this._enabledItems(); + if (items.length === 0) { + return; + } + + const active = document.activeElement; + const index = items.indexOf(active); + const prevIndex = index === -1 ? items.length - 1 : (index - 1 + items.length) % items.length; + + items[prevIndex].focus(); + } +} diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/manifest.json b/src/Toolkit/kits/shadcn/dropdown-menu/manifest.json new file mode 100644 index 00000000000..de5da72e5db --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/manifest.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../schema-kit-recipe-v1.json", + "type": "component", + "name": "Dropdown Menu", + "description": "A menu triggered by a button, providing a list of actions or links.", + "copy-files": { + "assets/": "assets/", + "templates/": "templates/" + }, + "dependencies": { + "recipe": ["kbd"], + "composer": ["twig/extra-bundle", "twig/html-extra:^3.12.0", "tales-from-a-dev/twig-tailwind-extra:^1.0.0"], + "importmap": ["@floating-ui/dom"] + } +} diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu.html.twig new file mode 100644 index 00000000000..3c79ff91499 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu.html.twig @@ -0,0 +1,26 @@ +{# @prop id string Unique suffix identifier for generating DropdownMenu internal IDs #} +{# @prop open boolean Open (or not) the DropdownMenu at initial rendering, default to `false` #} +{# @prop side 'bottom'|'top'|'left'|'right' The preferred side of the trigger where the menu appears, default to `bottom` #} +{# @prop align 'start'|'center'|'end' How the menu aligns to the trigger, default to `start` #} +{# @prop sideOffset number The distance in pixels from the trigger, default to `8` #} +{# @prop alignOffset number The alignment offset in pixels, default to `0` #} +{# @block content The default block #} +{%- props id, open = false, side = 'bottom', align = 'start', sideOffset = 8, alignOffset = 0 -%} + +{%- set _dropdown_menu_id = 'dropdown-menu-' ~ id -%} +{%- set _dropdown_menu_trigger_id = _dropdown_menu_id ~ '-trigger' -%} +{%- set _dropdown_menu_content_id = _dropdown_menu_id ~ '-content' -%} + +
+ {% block content %}{% endblock %} +
diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/CheckboxItem.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/CheckboxItem.html.twig new file mode 100644 index 00000000000..470587d4594 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/CheckboxItem.html.twig @@ -0,0 +1,25 @@ +{# @prop checked boolean Whether the checkbox is checked, default to `false` #} +{# @prop disabled boolean Whether the item is disabled, default to `false` #} +{# @prop closeOnSelect boolean Whether selecting the item closes the menu, default to `false` #} +{# @block content The default block #} +{%- props checked = false, disabled = false, closeOnSelect = false -%} + + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Content.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Content.html.twig new file mode 100644 index 00000000000..90957c2038b --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Content.html.twig @@ -0,0 +1,24 @@ +{# @prop side 'bottom'|'top'|'left'|'right' The preferred side of the trigger where the menu appears, default to `bottom` #} +{# @prop align 'start'|'center'|'end' How the menu aligns to the trigger, default to `start` #} +{# @block content The default block #} +{%- props side = 'bottom', align = 'start' -%} +{%- set style = html_cva( + base: 'absolute z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none', +) -%} + + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Group.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Group.html.twig new file mode 100644 index 00000000000..4a2f3d96a7c --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Group.html.twig @@ -0,0 +1,4 @@ +{# @block content The default block #} +
+ {%- block content %}{% endblock -%} +
diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Item.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Item.html.twig new file mode 100644 index 00000000000..fc9a7a51dfa --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Item.html.twig @@ -0,0 +1,31 @@ +{# @prop as 'button'|'a'|'div' The HTML tag to use, default to `button` #} +{# @prop disabled boolean Whether the item is disabled, default to `false` #} +{# @prop closeOnSelect boolean Whether selecting the item closes the menu, default to `true` #} +{# @prop inset boolean Whether the item should be inset, default to `false` #} +{# @block content The default block #} +{%- props as = 'button', disabled = false, closeOnSelect = true, inset = false -%} +{%- set style = html_cva( + base: 'relative flex w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg:not([class*=size-])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0', + variants: { + inset: { + true: 'pl-8', + false: '', + }, + }, +) -%} + +<{{ as }} + role="menuitem" + tabindex="-1" + data-dropdown-menu-target="item" + data-action="click->dropdown-menu#closeFromItem" + data-close-on-select="{{ closeOnSelect ? 'true' : 'false' }}" + data-disabled="{{ disabled ? 'true' : 'false' }}" + aria-disabled="{{ disabled ? 'true' : 'false' }}" + {% if as == 'button' %}type="button"{% endif %} + {% if as == 'button' and disabled %}disabled{% endif %} + class="{{ style.apply({inset: inset ? 'true' : 'false'}, attributes.render('class'))|tailwind_merge }}" + {{ attributes.without('role', 'tabindex', 'type') }} +> + {%- block content %}{% endblock -%} + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Label.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Label.html.twig new file mode 100644 index 00000000000..c9bd2b3567c --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Label.html.twig @@ -0,0 +1,19 @@ +{# @prop inset boolean Whether the label should be inset, default to `false` #} +{# @block content The default block #} +{%- props inset = false -%} +{%- set style = html_cva( + base: 'px-2 py-1.5 text-sm font-semibold', + variants: { + inset: { + true: 'pl-8', + false: '', + }, + }, +) -%} + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/RadioGroup.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/RadioGroup.html.twig new file mode 100644 index 00000000000..9fd760e0f03 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/RadioGroup.html.twig @@ -0,0 +1,13 @@ +{# @prop value string|null The currently selected value, default to `null` #} +{# @block content The default block #} +{%- props value = null -%} + +
+ {%- block content %}{% endblock -%} +
diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/RadioItem.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/RadioItem.html.twig new file mode 100644 index 00000000000..4fb03ff9e20 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/RadioItem.html.twig @@ -0,0 +1,27 @@ +{# @prop value string The value of this radio item #} +{# @prop checked boolean Whether the item is checked, default to `false` #} +{# @prop disabled boolean Whether the item is disabled, default to `false` #} +{# @prop closeOnSelect boolean Whether selecting the item closes the menu, default to `false` #} +{# @block content The default block #} +{%- props value, checked = false, disabled = false, closeOnSelect = false -%} + + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Separator.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Separator.html.twig new file mode 100644 index 00000000000..4863634eab1 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Separator.html.twig @@ -0,0 +1,6 @@ + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Shortcut.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Shortcut.html.twig new file mode 100644 index 00000000000..af3aa18da5f --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Shortcut.html.twig @@ -0,0 +1,7 @@ +{# @block content The default block #} + + {%- block content %}{% endblock -%} + diff --git a/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Trigger.html.twig b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Trigger.html.twig new file mode 100644 index 00000000000..2f8bbd5b516 --- /dev/null +++ b/src/Toolkit/kits/shadcn/dropdown-menu/templates/components/DropdownMenu/Trigger.html.twig @@ -0,0 +1,11 @@ +{# @block content The default block #} +{%- set trigger_attrs = { + id: _dropdown_menu_trigger_id, + 'aria-haspopup': 'menu', + 'aria-controls': _dropdown_menu_content_id, + 'aria-expanded': 'false', + 'data-dropdown-menu-target': 'trigger', + 'data-action': 'click->dropdown-menu#toggle keydown->dropdown-menu#onTriggerKeydown', + 'data-state': 'closed', +} -%} +{%- block content %}{% endblock -%} diff --git a/src/Toolkit/kits/shadcn/kbd/templates/components/Kbd.html.twig b/src/Toolkit/kits/shadcn/kbd/templates/components/Kbd.html.twig index ef6451a97c2..56a6b5a35af 100644 --- a/src/Toolkit/kits/shadcn/kbd/templates/components/Kbd.html.twig +++ b/src/Toolkit/kits/shadcn/kbd/templates/components/Kbd.html.twig @@ -1,7 +1,7 @@ {# @block content The default block #} {%- block content %}{% endblock -%} diff --git a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component button-group, code 1__1.html b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component button-group, code 1__1.html index e91a4f59de5..ee16df6fdd9 100644 --- a/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component button-group, code 1__1.html +++ b/src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component button-group, code 1__1.html @@ -18,27 +18,85 @@ Snooze - - - + + + + + + + + + + Reply in thread + + + + Forward + + + + Add to starred + + + + + Mute + + + + Mark as unread + + + + + Delete + + + ``` - Rendered code (prettified for testing purposes, run "php vendor/bin/phpunit -d --update-snapshots" to update snapshots): --> -
-