shadcn_view_component 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +255 -0
- data/MIT-LICENSE +20 -0
- data/README.md +604 -0
- data/app/assets/stylesheets/shadcn-themes.css +1075 -0
- data/app/assets/stylesheets/shadcn.css +853 -0
- data/app/components/shadcn/accordion/component.rb +44 -0
- data/app/components/shadcn/accordion/content/component.rb +44 -0
- data/app/components/shadcn/accordion/item/component.rb +41 -0
- data/app/components/shadcn/accordion/preview.rb +16 -0
- data/app/components/shadcn/accordion/previews/default.html.erb +10 -0
- data/app/components/shadcn/accordion/previews/multiple.html.erb +13 -0
- data/app/components/shadcn/accordion/trigger/component.rb +52 -0
- data/app/components/shadcn/alert/component.rb +52 -0
- data/app/components/shadcn/alert/preview.rb +11 -0
- data/app/components/shadcn/alert/previews/default.html.erb +8 -0
- data/app/components/shadcn/alert.rb +17 -0
- data/app/components/shadcn/alert_dialog/action/component.rb +35 -0
- data/app/components/shadcn/alert_dialog/cancel/component.rb +16 -0
- data/app/components/shadcn/alert_dialog/component.rb +20 -0
- data/app/components/shadcn/alert_dialog/content/component.rb +55 -0
- data/app/components/shadcn/alert_dialog/header/component.rb +33 -0
- data/app/components/shadcn/alert_dialog/preview.rb +16 -0
- data/app/components/shadcn/alert_dialog/previews/default.html.erb +13 -0
- data/app/components/shadcn/alert_dialog/previews/small_with_media.html.erb +38 -0
- data/app/components/shadcn/alert_dialog.rb +32 -0
- data/app/components/shadcn/application_view_component.rb +225 -0
- data/app/components/shadcn/application_view_component_preview.rb +8 -0
- data/app/components/shadcn/aspect_ratio/component.rb +34 -0
- data/app/components/shadcn/aspect_ratio/preview.rb +16 -0
- data/app/components/shadcn/aspect_ratio/previews/default.html.erb +5 -0
- data/app/components/shadcn/aspect_ratio/previews/ratios.html.erb +12 -0
- data/app/components/shadcn/attachment/action/component.rb +19 -0
- data/app/components/shadcn/attachment/component.rb +76 -0
- data/app/components/shadcn/attachment/media/component.rb +56 -0
- data/app/components/shadcn/attachment/preview.rb +11 -0
- data/app/components/shadcn/attachment/previews/default.html.erb +80 -0
- data/app/components/shadcn/attachment/trigger/component.rb +31 -0
- data/app/components/shadcn/attachment.rb +42 -0
- data/app/components/shadcn/avatar/component.rb +43 -0
- data/app/components/shadcn/avatar/fallback/component.rb +23 -0
- data/app/components/shadcn/avatar/image/component.rb +20 -0
- data/app/components/shadcn/avatar/preview.rb +11 -0
- data/app/components/shadcn/avatar/previews/default.html.erb +13 -0
- data/app/components/shadcn/avatar.rb +32 -0
- data/app/components/shadcn/badge/component.rb +57 -0
- data/app/components/shadcn/badge/preview.rb +11 -0
- data/app/components/shadcn/badge/previews/default.html.erb +5 -0
- data/app/components/shadcn/breadcrumb/component.rb +15 -0
- data/app/components/shadcn/breadcrumb/ellipsis/component.rb +28 -0
- data/app/components/shadcn/breadcrumb/page/component.rb +25 -0
- data/app/components/shadcn/breadcrumb/preview.rb +16 -0
- data/app/components/shadcn/breadcrumb/previews/custom_separator.html.erb +17 -0
- data/app/components/shadcn/breadcrumb/previews/default.html.erb +15 -0
- data/app/components/shadcn/breadcrumb/separator/component.rb +31 -0
- data/app/components/shadcn/breadcrumb.rb +18 -0
- data/app/components/shadcn/bubble/component.rb +90 -0
- data/app/components/shadcn/bubble/preview.rb +16 -0
- data/app/components/shadcn/bubble/previews/alignment.html.erb +33 -0
- data/app/components/shadcn/bubble/previews/default.html.erb +22 -0
- data/app/components/shadcn/bubble/reactions/component.rb +54 -0
- data/app/components/shadcn/bubble.rb +24 -0
- data/app/components/shadcn/button/component.rb +85 -0
- data/app/components/shadcn/button/preview.rb +31 -0
- data/app/components/shadcn/button/previews/default.html.erb +8 -0
- data/app/components/shadcn/button/previews/sizes.html.erb +5 -0
- data/app/components/shadcn/button/previews/variants.html.erb +5 -0
- data/app/components/shadcn/button_group/component.rb +53 -0
- data/app/components/shadcn/button_group/preview.rb +15 -0
- data/app/components/shadcn/button_group/previews/default.html.erb +18 -0
- data/app/components/shadcn/button_group/previews/vertical.html.erb +5 -0
- data/app/components/shadcn/button_group/separator/component.rb +30 -0
- data/app/components/shadcn/button_group/text/component.rb +20 -0
- data/app/components/shadcn/calendar/component.rb +558 -0
- data/app/components/shadcn/calendar/month.rb +74 -0
- data/app/components/shadcn/calendar/preview.rb +64 -0
- data/app/components/shadcn/calendar/previews/booked.html.erb +18 -0
- data/app/components/shadcn/calendar/previews/custom_cell_size.html.erb +11 -0
- data/app/components/shadcn/calendar/previews/date_and_time.html.erb +31 -0
- data/app/components/shadcn/calendar/previews/default.html.erb +8 -0
- data/app/components/shadcn/calendar/previews/dropdown_caption.html.erb +11 -0
- data/app/components/shadcn/calendar/previews/in_a_form.html.erb +8 -0
- data/app/components/shadcn/calendar/previews/presets.html.erb +20 -0
- data/app/components/shadcn/calendar/previews/range.html.erb +10 -0
- data/app/components/shadcn/calendar/previews/week_numbers.html.erb +8 -0
- data/app/components/shadcn/card/component.rb +24 -0
- data/app/components/shadcn/card/header/component.rb +26 -0
- data/app/components/shadcn/card/preview.rb +16 -0
- data/app/components/shadcn/card/previews/default.html.erb +18 -0
- data/app/components/shadcn/card/previews/image.html.erb +37 -0
- data/app/components/shadcn/card.rb +21 -0
- data/app/components/shadcn/carousel/component.rb +61 -0
- data/app/components/shadcn/carousel/content/component.rb +86 -0
- data/app/components/shadcn/carousel/item/component.rb +42 -0
- data/app/components/shadcn/carousel/next/component.rb +59 -0
- data/app/components/shadcn/carousel/preview.rb +22 -0
- data/app/components/shadcn/carousel/previews/default.html.erb +22 -0
- data/app/components/shadcn/carousel/previews/sizes.html.erb +42 -0
- data/app/components/shadcn/carousel/previews/vertical.html.erb +27 -0
- data/app/components/shadcn/carousel/previous/component.rb +59 -0
- data/app/components/shadcn/chart/area/component.rb +43 -0
- data/app/components/shadcn/chart/bar/component.rb +75 -0
- data/app/components/shadcn/chart/cartesian/component.rb +152 -0
- data/app/components/shadcn/chart/component.rb +124 -0
- data/app/components/shadcn/chart/focusable.rb +38 -0
- data/app/components/shadcn/chart/legend/component.rb +55 -0
- data/app/components/shadcn/chart/line/component.rb +50 -0
- data/app/components/shadcn/chart/pie/component.rb +169 -0
- data/app/components/shadcn/chart/plot.rb +147 -0
- data/app/components/shadcn/chart/preview.rb +45 -0
- data/app/components/shadcn/chart/previews/area.html.erb +31 -0
- data/app/components/shadcn/chart/previews/bar.html.erb +32 -0
- data/app/components/shadcn/chart/previews/bar_stacked.html.erb +30 -0
- data/app/components/shadcn/chart/previews/default.html.erb +30 -0
- data/app/components/shadcn/chart/previews/donut.html.erb +16 -0
- data/app/components/shadcn/chart/previews/line.html.erb +30 -0
- data/app/components/shadcn/chart/previews/percentages.html.erb +17 -0
- data/app/components/shadcn/chart/table/component.rb +75 -0
- data/app/components/shadcn/chart/tooltip/component.rb +111 -0
- data/app/components/shadcn/checkbox/component.rb +109 -0
- data/app/components/shadcn/checkbox/preview.rb +16 -0
- data/app/components/shadcn/checkbox/previews/default.html.erb +14 -0
- data/app/components/shadcn/checkbox/previews/states.html.erb +23 -0
- data/app/components/shadcn/collapsible/component.rb +35 -0
- data/app/components/shadcn/collapsible/content/component.rb +20 -0
- data/app/components/shadcn/collapsible/preview.rb +11 -0
- data/app/components/shadcn/collapsible/previews/default.html.erb +9 -0
- data/app/components/shadcn/collapsible/trigger/component.rb +23 -0
- data/app/components/shadcn/combobox/chip/component.rb +60 -0
- data/app/components/shadcn/combobox/chip_input/component.rb +43 -0
- data/app/components/shadcn/combobox/chips/component.rb +54 -0
- data/app/components/shadcn/combobox/clear/component.rb +32 -0
- data/app/components/shadcn/combobox/component.rb +113 -0
- data/app/components/shadcn/combobox/content/component.rb +59 -0
- data/app/components/shadcn/combobox/empty/component.rb +23 -0
- data/app/components/shadcn/combobox/input/component.rb +85 -0
- data/app/components/shadcn/combobox/item/component.rb +69 -0
- data/app/components/shadcn/combobox/list/component.rb +27 -0
- data/app/components/shadcn/combobox/preview.rb +31 -0
- data/app/components/shadcn/combobox/previews/default.html.erb +20 -0
- data/app/components/shadcn/combobox/previews/grouped.html.erb +25 -0
- data/app/components/shadcn/combobox/previews/multiple.html.erb +28 -0
- data/app/components/shadcn/combobox/previews/with_clear.html.erb +16 -0
- data/app/components/shadcn/combobox/trigger/component.rb +54 -0
- data/app/components/shadcn/combobox.rb +16 -0
- data/app/components/shadcn/command/component.rb +67 -0
- data/app/components/shadcn/command/dialog/component.rb +72 -0
- data/app/components/shadcn/command/empty/component.rb +29 -0
- data/app/components/shadcn/command/group/component.rb +63 -0
- data/app/components/shadcn/command/input/component.rb +66 -0
- data/app/components/shadcn/command/item/component.rb +62 -0
- data/app/components/shadcn/command/list/component.rb +36 -0
- data/app/components/shadcn/command/preview.rb +26 -0
- data/app/components/shadcn/command/previews/default.html.erb +41 -0
- data/app/components/shadcn/command/previews/dialog.html.erb +33 -0
- data/app/components/shadcn/command/previews/ranking.html.erb +16 -0
- data/app/components/shadcn/command/separator/component.rb +33 -0
- data/app/components/shadcn/command.rb +14 -0
- data/app/components/shadcn/concerns/modal_root.rb +43 -0
- data/app/components/shadcn/concerns/submits_value.rb +81 -0
- data/app/components/shadcn/context_menu/component.rb +45 -0
- data/app/components/shadcn/context_menu/content/component.rb +40 -0
- data/app/components/shadcn/context_menu/preview.rb +16 -0
- data/app/components/shadcn/context_menu/previews/default.html.erb +34 -0
- data/app/components/shadcn/context_menu/previews/items.html.erb +35 -0
- data/app/components/shadcn/context_menu/sub/component.rb +47 -0
- data/app/components/shadcn/context_menu/sub_content/component.rb +31 -0
- data/app/components/shadcn/context_menu/trigger/component.rb +50 -0
- data/app/components/shadcn/context_menu.rb +43 -0
- data/app/components/shadcn/dialog/close/component.rb +20 -0
- data/app/components/shadcn/dialog/component.rb +20 -0
- data/app/components/shadcn/dialog/content/component.rb +78 -0
- data/app/components/shadcn/dialog/header/component.rb +23 -0
- data/app/components/shadcn/dialog/overlay/component.rb +28 -0
- data/app/components/shadcn/dialog/preview.rb +28 -0
- data/app/components/shadcn/dialog/previews/default.html.erb +17 -0
- data/app/components/shadcn/dialog/previews/inside_stacking_context.html.erb +25 -0
- data/app/components/shadcn/dialog/previews/no_close_button.html.erb +15 -0
- data/app/components/shadcn/dialog/previews/scrollable_content.html.erb +29 -0
- data/app/components/shadcn/dialog/trigger/component.rb +24 -0
- data/app/components/shadcn/dialog.rb +17 -0
- data/app/components/shadcn/drawer/component.rb +33 -0
- data/app/components/shadcn/drawer/content/component.rb +95 -0
- data/app/components/shadcn/drawer/header/component.rb +34 -0
- data/app/components/shadcn/drawer/preview.rb +29 -0
- data/app/components/shadcn/drawer/previews/default.html.erb +21 -0
- data/app/components/shadcn/drawer/previews/scrollable.html.erb +20 -0
- data/app/components/shadcn/drawer/previews/scrollable_side.html.erb +21 -0
- data/app/components/shadcn/drawer/previews/sides.html.erb +17 -0
- data/app/components/shadcn/drawer.rb +27 -0
- data/app/components/shadcn/dropdown_menu/checkbox_item/component.rb +61 -0
- data/app/components/shadcn/dropdown_menu/component.rb +48 -0
- data/app/components/shadcn/dropdown_menu/content/component.rb +49 -0
- data/app/components/shadcn/dropdown_menu/group/component.rb +16 -0
- data/app/components/shadcn/dropdown_menu/item/component.rb +50 -0
- data/app/components/shadcn/dropdown_menu/preview.rb +16 -0
- data/app/components/shadcn/dropdown_menu/previews/checkboxes_and_radio.html.erb +34 -0
- data/app/components/shadcn/dropdown_menu/previews/default.html.erb +23 -0
- data/app/components/shadcn/dropdown_menu/radio_group/component.rb +16 -0
- data/app/components/shadcn/dropdown_menu/radio_item/component.rb +63 -0
- data/app/components/shadcn/dropdown_menu/sub/component.rb +44 -0
- data/app/components/shadcn/dropdown_menu/sub_content/component.rb +45 -0
- data/app/components/shadcn/dropdown_menu/sub_trigger/component.rb +58 -0
- data/app/components/shadcn/dropdown_menu/trigger/component.rb +25 -0
- data/app/components/shadcn/dropdown_menu.rb +18 -0
- data/app/components/shadcn/empty/component.rb +17 -0
- data/app/components/shadcn/empty/media/component.rb +48 -0
- data/app/components/shadcn/empty/preview.rb +16 -0
- data/app/components/shadcn/empty/previews/default.html.erb +15 -0
- data/app/components/shadcn/empty/previews/media.html.erb +32 -0
- data/app/components/shadcn/empty.rb +26 -0
- data/app/components/shadcn/field/component.rb +61 -0
- data/app/components/shadcn/field/error/component.rb +49 -0
- data/app/components/shadcn/field/legend/component.rb +30 -0
- data/app/components/shadcn/field/preview.rb +17 -0
- data/app/components/shadcn/field/previews/default.html.erb +29 -0
- data/app/components/shadcn/field/previews/form_builder.html.erb +38 -0
- data/app/components/shadcn/field/separator/component.rb +35 -0
- data/app/components/shadcn/field.rb +44 -0
- data/app/components/shadcn/hover_card/component.rb +51 -0
- data/app/components/shadcn/hover_card/content/component.rb +45 -0
- data/app/components/shadcn/hover_card/preview.rb +16 -0
- data/app/components/shadcn/hover_card/previews/default.html.erb +36 -0
- data/app/components/shadcn/hover_card/previews/sides.html.erb +15 -0
- data/app/components/shadcn/hover_card/trigger/component.rb +33 -0
- data/app/components/shadcn/icon/component.rb +76 -0
- data/app/components/shadcn/icon.rb +43 -0
- data/app/components/shadcn/input/component.rb +26 -0
- data/app/components/shadcn/input/preview.rb +16 -0
- data/app/components/shadcn/input/previews/default.html.erb +5 -0
- data/app/components/shadcn/input/previews/states.html.erb +20 -0
- data/app/components/shadcn/input_group/addon/component.rb +65 -0
- data/app/components/shadcn/input_group/button/component.rb +40 -0
- data/app/components/shadcn/input_group/component.rb +43 -0
- data/app/components/shadcn/input_group/input/component.rb +25 -0
- data/app/components/shadcn/input_group/preview.rb +17 -0
- data/app/components/shadcn/input_group/previews/addons.html.erb +53 -0
- data/app/components/shadcn/input_group/previews/default.html.erb +25 -0
- data/app/components/shadcn/input_group/text/component.rb +20 -0
- data/app/components/shadcn/input_group/textarea/component.rb +21 -0
- data/app/components/shadcn/input_otp/component.rb +95 -0
- data/app/components/shadcn/input_otp/preview.rb +17 -0
- data/app/components/shadcn/input_otp/previews/default.html.erb +19 -0
- data/app/components/shadcn/input_otp/previews/variations.html.erb +33 -0
- data/app/components/shadcn/input_otp/separator/component.rb +21 -0
- data/app/components/shadcn/input_otp/slot/component.rb +60 -0
- data/app/components/shadcn/input_otp.rb +11 -0
- data/app/components/shadcn/item/component.rb +56 -0
- data/app/components/shadcn/item/group/component.rb +53 -0
- data/app/components/shadcn/item/media/component.rb +53 -0
- data/app/components/shadcn/item/preview.rb +20 -0
- data/app/components/shadcn/item/previews/default.html.erb +32 -0
- data/app/components/shadcn/item/previews/link.html.erb +39 -0
- data/app/components/shadcn/item/previews/outline.html.erb +8 -0
- data/app/components/shadcn/item/separator/component.rb +24 -0
- data/app/components/shadcn/item.rb +34 -0
- data/app/components/shadcn/kbd/component.rb +23 -0
- data/app/components/shadcn/kbd/group/component.rb +17 -0
- data/app/components/shadcn/kbd/preview.rb +11 -0
- data/app/components/shadcn/kbd/previews/default.html.erb +4 -0
- data/app/components/shadcn/label/component.rb +20 -0
- data/app/components/shadcn/label/preview.rb +11 -0
- data/app/components/shadcn/label/previews/default.html.erb +4 -0
- data/app/components/shadcn/marker/component.rb +46 -0
- data/app/components/shadcn/marker/icon/component.rb +23 -0
- data/app/components/shadcn/marker/preview.rb +17 -0
- data/app/components/shadcn/marker/previews/default.html.erb +18 -0
- data/app/components/shadcn/marker/previews/status.html.erb +32 -0
- data/app/components/shadcn/marker.rb +17 -0
- data/app/components/shadcn/menubar/component.rb +36 -0
- data/app/components/shadcn/menubar/content/component.rb +49 -0
- data/app/components/shadcn/menubar/menu/component.rb +39 -0
- data/app/components/shadcn/menubar/preview.rb +16 -0
- data/app/components/shadcn/menubar/previews/default.html.erb +52 -0
- data/app/components/shadcn/menubar/previews/items.html.erb +38 -0
- data/app/components/shadcn/menubar/sub/component.rb +43 -0
- data/app/components/shadcn/menubar/sub_content/component.rb +31 -0
- data/app/components/shadcn/menubar/sub_trigger/component.rb +31 -0
- data/app/components/shadcn/menubar/trigger/component.rb +41 -0
- data/app/components/shadcn/menubar.rb +47 -0
- data/app/components/shadcn/message/component.rb +33 -0
- data/app/components/shadcn/message/preview.rb +16 -0
- data/app/components/shadcn/message/previews/actions.html.erb +51 -0
- data/app/components/shadcn/message/previews/default.html.erb +35 -0
- data/app/components/shadcn/message.rb +35 -0
- data/app/components/shadcn/message_scroller/button/component.rb +78 -0
- data/app/components/shadcn/message_scroller/component.rb +59 -0
- data/app/components/shadcn/message_scroller/content/component.rb +50 -0
- data/app/components/shadcn/message_scroller/item/component.rb +45 -0
- data/app/components/shadcn/message_scroller/preview.rb +11 -0
- data/app/components/shadcn/message_scroller/previews/default.html.erb +72 -0
- data/app/components/shadcn/message_scroller/viewport/component.rb +41 -0
- data/app/components/shadcn/mode_switcher/component.rb +67 -0
- data/app/components/shadcn/mode_switcher/preview.rb +11 -0
- data/app/components/shadcn/mode_switcher/previews/default.html.erb +5 -0
- data/app/components/shadcn/mode_toggle/component.rb +75 -0
- data/app/components/shadcn/mode_toggle/preview.rb +11 -0
- data/app/components/shadcn/mode_toggle/previews/default.html.erb +20 -0
- data/app/components/shadcn/native_select/component.rb +59 -0
- data/app/components/shadcn/native_select/preview.rb +16 -0
- data/app/components/shadcn/native_select/previews/default.html.erb +9 -0
- data/app/components/shadcn/native_select/previews/groups.html.erb +26 -0
- data/app/components/shadcn/native_select.rb +16 -0
- data/app/components/shadcn/navigation_menu/component.rb +51 -0
- data/app/components/shadcn/navigation_menu/content/component.rb +58 -0
- data/app/components/shadcn/navigation_menu/preview.rb +11 -0
- data/app/components/shadcn/navigation_menu/previews/default.html.erb +49 -0
- data/app/components/shadcn/navigation_menu/trigger/component.rb +59 -0
- data/app/components/shadcn/navigation_menu.rb +28 -0
- data/app/components/shadcn/pagination/component.rb +19 -0
- data/app/components/shadcn/pagination/ellipsis/component.rb +28 -0
- data/app/components/shadcn/pagination/link/component.rb +37 -0
- data/app/components/shadcn/pagination/next/component.rb +31 -0
- data/app/components/shadcn/pagination/preview.rb +16 -0
- data/app/components/shadcn/pagination/previews/default.html.erb +18 -0
- data/app/components/shadcn/pagination/previews/simple.html.erb +39 -0
- data/app/components/shadcn/pagination/previous/component.rb +31 -0
- data/app/components/shadcn/pagination.rb +14 -0
- data/app/components/shadcn/parts.rb +38 -0
- data/app/components/shadcn/popover/anchor/component.rb +17 -0
- data/app/components/shadcn/popover/component.rb +42 -0
- data/app/components/shadcn/popover/content/component.rb +40 -0
- data/app/components/shadcn/popover/preview.rb +28 -0
- data/app/components/shadcn/popover/previews/align.html.erb +13 -0
- data/app/components/shadcn/popover/previews/default.html.erb +9 -0
- data/app/components/shadcn/popover/previews/inside_containing_block.html.erb +41 -0
- data/app/components/shadcn/popover/previews/inside_stacking_context.html.erb +30 -0
- data/app/components/shadcn/popover/trigger/component.rb +24 -0
- data/app/components/shadcn/popover.rb +16 -0
- data/app/components/shadcn/progress/component.rb +62 -0
- data/app/components/shadcn/progress/preview.rb +11 -0
- data/app/components/shadcn/progress/previews/default.html.erb +5 -0
- data/app/components/shadcn/radio_group/component.rb +51 -0
- data/app/components/shadcn/radio_group/item/component.rb +74 -0
- data/app/components/shadcn/radio_group/preview.rb +16 -0
- data/app/components/shadcn/radio_group/previews/default.html.erb +15 -0
- data/app/components/shadcn/radio_group/previews/states.html.erb +41 -0
- data/app/components/shadcn/resizable/component.rb +59 -0
- data/app/components/shadcn/resizable/handle/component.rb +70 -0
- data/app/components/shadcn/resizable/panel/component.rb +53 -0
- data/app/components/shadcn/resizable/preview.rb +23 -0
- data/app/components/shadcn/resizable/previews/default.html.erb +25 -0
- data/app/components/shadcn/resizable/previews/vertical.html.erb +19 -0
- data/app/components/shadcn/resizable/previews/with_handle.html.erb +17 -0
- data/app/components/shadcn/scroll_area/component.rb +84 -0
- data/app/components/shadcn/scroll_area/preview.rb +11 -0
- data/app/components/shadcn/scroll_area/previews/default.html.erb +36 -0
- data/app/components/shadcn/scroll_area/scrollbar/component.rb +72 -0
- data/app/components/shadcn/scroll_area/viewport/component.rb +70 -0
- data/app/components/shadcn/scroll_area.rb +14 -0
- data/app/components/shadcn/select/component.rb +67 -0
- data/app/components/shadcn/select/content/component.rb +157 -0
- data/app/components/shadcn/select/empty/component.rb +38 -0
- data/app/components/shadcn/select/group/component.rb +16 -0
- data/app/components/shadcn/select/item/component.rb +71 -0
- data/app/components/shadcn/select/list/component.rb +41 -0
- data/app/components/shadcn/select/preview.rb +24 -0
- data/app/components/shadcn/select/previews/default.html.erb +17 -0
- data/app/components/shadcn/select/previews/scrollable.html.erb +19 -0
- data/app/components/shadcn/select/previews/searchable.html.erb +18 -0
- data/app/components/shadcn/select/previews/states.html.erb +25 -0
- data/app/components/shadcn/select/search/component.rb +54 -0
- data/app/components/shadcn/select/trigger/component.rb +78 -0
- data/app/components/shadcn/select/value/component.rb +28 -0
- data/app/components/shadcn/select.rb +15 -0
- data/app/components/shadcn/separator/component.rb +41 -0
- data/app/components/shadcn/separator/preview.rb +11 -0
- data/app/components/shadcn/separator/previews/default.html.erb +9 -0
- data/app/components/shadcn/sheet/component.rb +16 -0
- data/app/components/shadcn/sheet/content/component.rb +98 -0
- data/app/components/shadcn/sheet/header/component.rb +23 -0
- data/app/components/shadcn/sheet/preview.rb +16 -0
- data/app/components/shadcn/sheet/previews/default.html.erb +16 -0
- data/app/components/shadcn/sheet/previews/no_close_button.html.erb +14 -0
- data/app/components/shadcn/sheet.rb +22 -0
- data/app/components/shadcn/sidebar/component.rb +232 -0
- data/app/components/shadcn/sidebar/input/component.rb +25 -0
- data/app/components/shadcn/sidebar/menu_action/component.rb +58 -0
- data/app/components/shadcn/sidebar/menu_button/component.rb +100 -0
- data/app/components/shadcn/sidebar/menu_skeleton/component.rb +51 -0
- data/app/components/shadcn/sidebar/menu_sub_button/component.rb +60 -0
- data/app/components/shadcn/sidebar/preview.rb +22 -0
- data/app/components/shadcn/sidebar/previews/default.html.erb +189 -0
- data/app/components/shadcn/sidebar/previews/floating.html.erb +39 -0
- data/app/components/shadcn/sidebar/previews/inset.html.erb +48 -0
- data/app/components/shadcn/sidebar/provider/component.rb +53 -0
- data/app/components/shadcn/sidebar/rail/component.rb +43 -0
- data/app/components/shadcn/sidebar/separator/component.rb +23 -0
- data/app/components/shadcn/sidebar/trigger/component.rb +39 -0
- data/app/components/shadcn/sidebar.rb +98 -0
- data/app/components/shadcn/skeleton/component.rb +14 -0
- data/app/components/shadcn/skeleton/preview.rb +11 -0
- data/app/components/shadcn/skeleton/previews/default.html.erb +7 -0
- data/app/components/shadcn/slider/component.rb +145 -0
- data/app/components/shadcn/slider/preview.rb +16 -0
- data/app/components/shadcn/slider/previews/default.html.erb +17 -0
- data/app/components/shadcn/slider/previews/vertical.html.erb +7 -0
- data/app/components/shadcn/slider/range/component.rb +51 -0
- data/app/components/shadcn/slider/thumb/component.rb +58 -0
- data/app/components/shadcn/slider/track/component.rb +32 -0
- data/app/components/shadcn/spinner/component.rb +21 -0
- data/app/components/shadcn/spinner/preview.rb +11 -0
- data/app/components/shadcn/spinner/previews/default.html.erb +7 -0
- data/app/components/shadcn/switch/component.rb +104 -0
- data/app/components/shadcn/switch/preview.rb +11 -0
- data/app/components/shadcn/switch/previews/default.html.erb +24 -0
- data/app/components/shadcn/table/component.rb +26 -0
- data/app/components/shadcn/table/preview.rb +16 -0
- data/app/components/shadcn/table/previews/default.html.erb +19 -0
- data/app/components/shadcn/table/previews/footer.html.erb +25 -0
- data/app/components/shadcn/table.rb +33 -0
- data/app/components/shadcn/tabs/component.rb +40 -0
- data/app/components/shadcn/tabs/content/component.rb +34 -0
- data/app/components/shadcn/tabs/list/component.rb +51 -0
- data/app/components/shadcn/tabs/preview.rb +16 -0
- data/app/components/shadcn/tabs/previews/default.html.erb +21 -0
- data/app/components/shadcn/tabs/previews/line.html.erb +23 -0
- data/app/components/shadcn/tabs/trigger/component.rb +66 -0
- data/app/components/shadcn/textarea/component.rb +24 -0
- data/app/components/shadcn/textarea/preview.rb +16 -0
- data/app/components/shadcn/textarea/previews/default.html.erb +3 -0
- data/app/components/shadcn/textarea/previews/states.html.erb +18 -0
- data/app/components/shadcn/theme_selector/component.rb +88 -0
- data/app/components/shadcn/theme_selector/preview.rb +17 -0
- data/app/components/shadcn/theme_selector/previews/all_themes.html.erb +20 -0
- data/app/components/shadcn/theme_selector/previews/default.html.erb +18 -0
- data/app/components/shadcn/toaster/component.rb +139 -0
- data/app/components/shadcn/toaster/preview.rb +16 -0
- data/app/components/shadcn/toaster/previews/default.html.erb +19 -0
- data/app/components/shadcn/toaster/previews/from_the_server.html.erb +15 -0
- data/app/components/shadcn/toaster/toast/component.rb +134 -0
- data/app/components/shadcn/toggle/component.rb +81 -0
- data/app/components/shadcn/toggle/preview.rb +11 -0
- data/app/components/shadcn/toggle/previews/default.html.erb +6 -0
- data/app/components/shadcn/toggle_group/component.rb +62 -0
- data/app/components/shadcn/toggle_group/item/component.rb +59 -0
- data/app/components/shadcn/toggle_group/preview.rb +16 -0
- data/app/components/shadcn/toggle_group/previews/default.html.erb +12 -0
- data/app/components/shadcn/toggle_group/previews/sizes.html.erb +37 -0
- data/app/components/shadcn/tooltip/component.rb +38 -0
- data/app/components/shadcn/tooltip/content/component.rb +62 -0
- data/app/components/shadcn/tooltip/preview.rb +21 -0
- data/app/components/shadcn/tooltip/previews/default.html.erb +4 -0
- data/app/components/shadcn/tooltip/previews/sides.html.erb +19 -0
- data/app/components/shadcn/tooltip/previews/with_keyboard_shortcut.html.erb +11 -0
- data/app/components/shadcn/tooltip/provider/component.rb +28 -0
- data/app/components/shadcn/tooltip/trigger/component.rb +25 -0
- data/app/helpers/shadcn_view_component/form_helper.rb +25 -0
- data/app/helpers/shadcn_view_component/theme_helper.rb +61 -0
- data/app/javascript/shadcn/animation.js +180 -0
- data/app/javascript/shadcn/command_score.js +110 -0
- data/app/javascript/shadcn/controllers/accordion_controller.js +127 -0
- data/app/javascript/shadcn/controllers/avatar_controller.js +32 -0
- data/app/javascript/shadcn/controllers/calendar_controller.js +633 -0
- data/app/javascript/shadcn/controllers/carousel_controller.js +139 -0
- data/app/javascript/shadcn/controllers/chart_controller.js +110 -0
- data/app/javascript/shadcn/controllers/checkbox_controller.js +61 -0
- data/app/javascript/shadcn/controllers/collapsible_controller.js +55 -0
- data/app/javascript/shadcn/controllers/combobox_controller.js +360 -0
- data/app/javascript/shadcn/controllers/command_controller.js +199 -0
- data/app/javascript/shadcn/controllers/dialog_controller.js +154 -0
- data/app/javascript/shadcn/controllers/drawer_controller.js +265 -0
- data/app/javascript/shadcn/controllers/dropdown_menu_controller.js +259 -0
- data/app/javascript/shadcn/controllers/hover_card_controller.js +85 -0
- data/app/javascript/shadcn/controllers/input_group_controller.js +17 -0
- data/app/javascript/shadcn/controllers/input_otp_controller.js +80 -0
- data/app/javascript/shadcn/controllers/menubar_controller.js +151 -0
- data/app/javascript/shadcn/controllers/message_scroller_controller.js +456 -0
- data/app/javascript/shadcn/controllers/navigation_menu_controller.js +211 -0
- data/app/javascript/shadcn/controllers/popover_controller.js +66 -0
- data/app/javascript/shadcn/controllers/radio_group_controller.js +69 -0
- data/app/javascript/shadcn/controllers/resizable_controller.js +167 -0
- data/app/javascript/shadcn/controllers/scroll_area_controller.js +253 -0
- data/app/javascript/shadcn/controllers/select_controller.js +320 -0
- data/app/javascript/shadcn/controllers/sidebar_controller.js +273 -0
- data/app/javascript/shadcn/controllers/slider_controller.js +230 -0
- data/app/javascript/shadcn/controllers/switch_controller.js +39 -0
- data/app/javascript/shadcn/controllers/tabs_controller.js +81 -0
- data/app/javascript/shadcn/controllers/theme_controller.js +67 -0
- data/app/javascript/shadcn/controllers/toaster_controller.js +281 -0
- data/app/javascript/shadcn/controllers/toggle_controller.js +24 -0
- data/app/javascript/shadcn/controllers/toggle_group_controller.js +73 -0
- data/app/javascript/shadcn/controllers/tooltip_controller.js +58 -0
- data/app/javascript/shadcn/direction.js +41 -0
- data/app/javascript/shadcn/dismiss.js +73 -0
- data/app/javascript/shadcn/floating.js +207 -0
- data/app/javascript/shadcn/focus.js +86 -0
- data/app/javascript/shadcn/id.js +14 -0
- data/app/javascript/shadcn/index.js +110 -0
- data/app/javascript/shadcn/popper.js +262 -0
- data/app/javascript/shadcn/scroll_geometry.js +267 -0
- data/app/javascript/shadcn/theme.js +161 -0
- data/app/javascript/shadcn/top_layer.js +59 -0
- data/app/javascript/shadcn/typeahead.js +99 -0
- data/config/importmap.rb +6 -0
- data/config/locales/en.yml +62 -0
- data/lib/generators/shadcn_view_component/install/install_generator.rb +149 -0
- data/lib/shadcn_view_component/engine.rb +73 -0
- data/lib/shadcn_view_component/form_builder.rb +264 -0
- data/lib/shadcn_view_component/icon_registry.rb +66 -0
- data/lib/shadcn_view_component/icons.rb +32 -0
- data/lib/shadcn_view_component/themes.rb +1065 -0
- data/lib/shadcn_view_component/version.rb +3 -0
- data/lib/shadcn_view_component.rb +82 -0
- data/lib/tasks/icons.rake +53 -0
- data/lib/tasks/themes.rake +171 -0
- metadata +608 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Shared open/close machinery for the popper-based components (popover,
|
|
2
|
+
// dropdown menu, select, tooltip).
|
|
3
|
+
//
|
|
4
|
+
// Radix renders floating content through a portal on `document.body`. We keep
|
|
5
|
+
// it inside the component instead, wrapped in the same fixed-position element
|
|
6
|
+
// Radix uses (`data-radix-popper-content-wrapper`). The component roots are
|
|
7
|
+
// `display: contents`, so a fixed wrapper still positions against the viewport,
|
|
8
|
+
// and staying in place is what keeps the Stimulus actions on the items — menu
|
|
9
|
+
// items, select options, close buttons — bound and working.
|
|
10
|
+
|
|
11
|
+
import { createWrapper, position } from "shadcn/popper"
|
|
12
|
+
import * as topLayer from "shadcn/top_layer"
|
|
13
|
+
import { pushLayer, removeLayer } from "shadcn/dismiss"
|
|
14
|
+
import { ExitQueue } from "shadcn/animation"
|
|
15
|
+
|
|
16
|
+
export class FloatingLayer {
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.trigger = options.trigger
|
|
19
|
+
// What the content is *measured* against, which is not always what opened
|
|
20
|
+
// it: a context menu is anchored to the point you pressed, not to the area
|
|
21
|
+
// you pressed it in. Anything with a `getBoundingClientRect()` will do —
|
|
22
|
+
// `popper.js` asks for nothing else — so a caller can hand over a bare
|
|
23
|
+
// object standing for a point. Everything else the trigger is used for
|
|
24
|
+
// (its `data-state`, the dismiss anchor, the resize observer) still wants
|
|
25
|
+
// the real element.
|
|
26
|
+
this.anchor = options.anchor || options.trigger
|
|
27
|
+
this.content = options.content
|
|
28
|
+
this.home = options.home || this.content.parentElement
|
|
29
|
+
this.prefix = options.prefix || "popper"
|
|
30
|
+
this.side = options.side || "bottom"
|
|
31
|
+
this.align = options.align || "center"
|
|
32
|
+
this.sideOffset = options.sideOffset || 0
|
|
33
|
+
this.alignOffset = options.alignOffset || 0
|
|
34
|
+
this.matchAnchorWidth = options.matchAnchorWidth || false
|
|
35
|
+
// Base UI's four unprefixed custom properties, for the one family
|
|
36
|
+
// answerable to Base UI rather than to Radix. Asked for rather than always
|
|
37
|
+
// published: `--anchor-width` is a name a host could plausibly use for
|
|
38
|
+
// something of its own, and only the combobox's classes read it.
|
|
39
|
+
this.publishBaseUiVariables = options.publishBaseUiVariables || false
|
|
40
|
+
this.onOpen = options.onOpen || (() => {})
|
|
41
|
+
this.onClose = options.onClose || (() => {})
|
|
42
|
+
this.onDismiss = options.onDismiss || null
|
|
43
|
+
|
|
44
|
+
this.open = false
|
|
45
|
+
this.mounted = false
|
|
46
|
+
this.wrapper = null
|
|
47
|
+
this.layer = null
|
|
48
|
+
this.placeholder = null
|
|
49
|
+
this.frame = null
|
|
50
|
+
this.exits = new ExitQueue()
|
|
51
|
+
this.reposition = this.reposition.bind(this)
|
|
52
|
+
this.resizes = new ResizeObserver(this.reposition)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
show() {
|
|
56
|
+
if (this.open) return
|
|
57
|
+
this.open = true
|
|
58
|
+
|
|
59
|
+
// Reopened before the exit finished — the wrapper, the placeholder and the
|
|
60
|
+
// content are all still in place, so only the interaction state has to come
|
|
61
|
+
// back. Rebuilding them would strand the old placeholder and leave a second
|
|
62
|
+
// wrapper in the DOM.
|
|
63
|
+
if (this.exits.has(this.content)) {
|
|
64
|
+
this.exits.cancel(this.content)
|
|
65
|
+
} else {
|
|
66
|
+
this.mount()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.content.hidden = false
|
|
70
|
+
this.content.dataset.state = "open"
|
|
71
|
+
if (this.trigger) this.trigger.dataset.state = "open"
|
|
72
|
+
|
|
73
|
+
this.applyPosition()
|
|
74
|
+
|
|
75
|
+
window.addEventListener("scroll", this.reposition, true)
|
|
76
|
+
window.addEventListener("resize", this.reposition)
|
|
77
|
+
|
|
78
|
+
// A `resize` event only fires for the window, and the anchor can change
|
|
79
|
+
// size while it stays still — a sidebar menu button goes from 239px to
|
|
80
|
+
// 32px when the panel collapses under an open tooltip, which left the
|
|
81
|
+
// label 200px out to the right of the icon it names. floating-ui's
|
|
82
|
+
// `autoUpdate` observes both the reference and the floating element; this
|
|
83
|
+
// observes the anchor only, which is the side that moves the result.
|
|
84
|
+
if (this.trigger) this.resizes.observe(this.trigger)
|
|
85
|
+
|
|
86
|
+
this.layer = pushLayer({
|
|
87
|
+
element: this.content,
|
|
88
|
+
anchors: [ this.trigger ],
|
|
89
|
+
onDismiss: (event) => (this.onDismiss ? this.onDismiss(event) : this.hide())
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
this.onOpen()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Leave a marker where the content was, so closing puts it back exactly
|
|
96
|
+
// there. Appending to `home` instead would quietly reorder the markup —
|
|
97
|
+
// after one open/close a Select's content ends up after the hidden input.
|
|
98
|
+
mount() {
|
|
99
|
+
this.mounted = true
|
|
100
|
+
|
|
101
|
+
this.placeholder = document.createComment("shadcn-floating-content")
|
|
102
|
+
this.content.replaceWith(this.placeholder)
|
|
103
|
+
|
|
104
|
+
this.wrapper = createWrapper()
|
|
105
|
+
this.placeholder.parentNode.insertBefore(this.wrapper, this.placeholder)
|
|
106
|
+
this.wrapper.appendChild(this.content)
|
|
107
|
+
|
|
108
|
+
// Above every stacking context, without leaving the DOM.
|
|
109
|
+
topLayer.enable(this.wrapper)
|
|
110
|
+
topLayer.show(this.wrapper)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
hide() {
|
|
114
|
+
if (!this.open) return
|
|
115
|
+
this.open = false
|
|
116
|
+
|
|
117
|
+
// Interaction state is immediate: a layer that is fading out must not
|
|
118
|
+
// answer Escape or an outside click. Positioning is not interaction
|
|
119
|
+
// state — the content stays mounted through the exit (`this.mounted`
|
|
120
|
+
// goes false only in `dismount()`), so a scroll or resize during the fade
|
|
121
|
+
// still moves it instead of leaving it stranded over its old anchor.
|
|
122
|
+
if (this.layer) removeLayer(this.layer)
|
|
123
|
+
this.layer = null
|
|
124
|
+
|
|
125
|
+
// This is what starts the exit animation, so it has to be set before the
|
|
126
|
+
// queue looks for one.
|
|
127
|
+
this.content.dataset.state = "closed"
|
|
128
|
+
if (this.trigger) this.trigger.dataset.state = "closed"
|
|
129
|
+
|
|
130
|
+
this.exits.defer(this.content, () => this.dismount())
|
|
131
|
+
|
|
132
|
+
this.onClose()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
dismount() {
|
|
136
|
+
window.removeEventListener("scroll", this.reposition, true)
|
|
137
|
+
window.removeEventListener("resize", this.reposition)
|
|
138
|
+
this.resizes.disconnect()
|
|
139
|
+
|
|
140
|
+
if (this.frame) cancelAnimationFrame(this.frame)
|
|
141
|
+
this.frame = null
|
|
142
|
+
|
|
143
|
+
// Not what keeps a closed layer still — both listeners are gone and the
|
|
144
|
+
// pending frame is cancelled, so nothing can reach `reposition()` or
|
|
145
|
+
// `applyPosition()` from here. It is what keeps `mounted` answering "is the
|
|
146
|
+
// content in the wrapper", which is the only thing either of them asks
|
|
147
|
+
// before moving it, and the layer is reopened rather than replaced.
|
|
148
|
+
this.mounted = false
|
|
149
|
+
|
|
150
|
+
this.content.hidden = true
|
|
151
|
+
|
|
152
|
+
if (this.placeholder?.parentNode) {
|
|
153
|
+
this.placeholder.replaceWith(this.content)
|
|
154
|
+
} else {
|
|
155
|
+
this.home.appendChild(this.content)
|
|
156
|
+
}
|
|
157
|
+
this.placeholder = null
|
|
158
|
+
|
|
159
|
+
if (this.wrapper) {
|
|
160
|
+
topLayer.hide(this.wrapper)
|
|
161
|
+
this.wrapper.remove()
|
|
162
|
+
}
|
|
163
|
+
this.wrapper = null
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
toggle() {
|
|
167
|
+
this.open ? this.hide() : this.show()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Bound to `scroll` (capture, so every scrolling ancestor fires it), to
|
|
171
|
+
// `resize`, and to the anchor's own `ResizeObserver` — which also fires once
|
|
172
|
+
// on `observe()`, harmlessly: `show()` has already positioned synchronously.
|
|
173
|
+
// Positioning writes a transform and then reads `offsetWidth`,
|
|
174
|
+
// which forces a synchronous layout, so coalesce into one frame instead of
|
|
175
|
+
// reflowing on every event.
|
|
176
|
+
reposition() {
|
|
177
|
+
if (!this.mounted || !this.trigger || this.frame) return
|
|
178
|
+
|
|
179
|
+
this.frame = requestAnimationFrame(() => {
|
|
180
|
+
this.frame = null
|
|
181
|
+
this.applyPosition()
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// The opening position cannot wait for a frame: the wrapper starts at the
|
|
186
|
+
// top-left of the viewport, so a deferred first placement flashes there.
|
|
187
|
+
applyPosition() {
|
|
188
|
+
if (!this.mounted || !this.anchor) return
|
|
189
|
+
|
|
190
|
+
position(this.anchor, this.content, {
|
|
191
|
+
side: this.side,
|
|
192
|
+
align: this.align,
|
|
193
|
+
sideOffset: this.sideOffset,
|
|
194
|
+
alignOffset: this.alignOffset,
|
|
195
|
+
prefix: this.prefix,
|
|
196
|
+
matchAnchorWidth: this.matchAnchorWidth,
|
|
197
|
+
publishBaseUiVariables: this.publishBaseUiVariables
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
destroy() {
|
|
202
|
+
this.hide()
|
|
203
|
+
// Turbo may be detaching the element; there is nothing to animate for and
|
|
204
|
+
// nowhere to put the content back afterwards.
|
|
205
|
+
this.exits.flushAll()
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Port of Radix's FocusScope: keep Tab inside a container while it is open and
|
|
2
|
+
// hand focus back to whatever had it when the container closes.
|
|
3
|
+
|
|
4
|
+
const FOCUSABLE = [
|
|
5
|
+
"a[href]",
|
|
6
|
+
"button:not([disabled])",
|
|
7
|
+
"input:not([disabled]):not([type='hidden'])",
|
|
8
|
+
"select:not([disabled])",
|
|
9
|
+
"textarea:not([disabled])",
|
|
10
|
+
"[tabindex]:not([tabindex='-1'])",
|
|
11
|
+
"audio[controls]",
|
|
12
|
+
"video[controls]",
|
|
13
|
+
"[contenteditable]:not([contenteditable='false'])"
|
|
14
|
+
].join(",")
|
|
15
|
+
|
|
16
|
+
function isVisible(element) {
|
|
17
|
+
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function tabbable(container) {
|
|
21
|
+
return Array.from(container.querySelectorAll(FOCUSABLE)).filter(
|
|
22
|
+
(element) => !element.hasAttribute("disabled") && isVisible(element)
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function focusFirst(container) {
|
|
27
|
+
const candidates = tabbable(container)
|
|
28
|
+
const target = candidates[0] || container
|
|
29
|
+
target.focus({ preventScroll: true })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Traps Tab/Shift+Tab inside `container`. Returns a function that releases the
|
|
33
|
+
// trap and restores focus to `restoreTo` (defaults to the previously focused
|
|
34
|
+
// element).
|
|
35
|
+
export function trapFocus(container, restoreTo = document.activeElement) {
|
|
36
|
+
function onKeyDown(event) {
|
|
37
|
+
if (event.key !== "Tab") return
|
|
38
|
+
|
|
39
|
+
const candidates = tabbable(container)
|
|
40
|
+
if (candidates.length === 0) {
|
|
41
|
+
event.preventDefault()
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const first = candidates[0]
|
|
46
|
+
const last = candidates[candidates.length - 1]
|
|
47
|
+
|
|
48
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
49
|
+
event.preventDefault()
|
|
50
|
+
last.focus({ preventScroll: true })
|
|
51
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
52
|
+
event.preventDefault()
|
|
53
|
+
first.focus({ preventScroll: true })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
container.addEventListener("keydown", onKeyDown)
|
|
58
|
+
|
|
59
|
+
return function release() {
|
|
60
|
+
container.removeEventListener("keydown", onKeyDown)
|
|
61
|
+
if (restoreTo && document.contains(restoreTo)) {
|
|
62
|
+
restoreTo.focus({ preventScroll: true })
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Radix locks body scrolling while a modal layer is open. Reference counted so
|
|
68
|
+
// nested layers behave.
|
|
69
|
+
let scrollLocks = 0
|
|
70
|
+
let previousOverflow = null
|
|
71
|
+
|
|
72
|
+
export function lockScroll() {
|
|
73
|
+
if (scrollLocks === 0) {
|
|
74
|
+
previousOverflow = document.body.style.overflow
|
|
75
|
+
document.body.style.overflow = "hidden"
|
|
76
|
+
}
|
|
77
|
+
scrollLocks += 1
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function unlockScroll() {
|
|
81
|
+
scrollLocks = Math.max(0, scrollLocks - 1)
|
|
82
|
+
if (scrollLocks === 0) {
|
|
83
|
+
document.body.style.overflow = previousOverflow || ""
|
|
84
|
+
previousOverflow = null
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Unique DOM ids for the ARIA wiring between triggers and their content.
|
|
2
|
+
//
|
|
3
|
+
// Deliberately not `crypto.randomUUID()`: that is only defined in a secure
|
|
4
|
+
// context, so over plain HTTP — a LAN IP, a staging box, `rails s` reached from
|
|
5
|
+
// another device — it is `undefined` and every controller that called it threw
|
|
6
|
+
// in `connect()`, silently killing the component. A counter is enough; these ids
|
|
7
|
+
// only have to be unique within one document.
|
|
8
|
+
|
|
9
|
+
let counter = 0
|
|
10
|
+
|
|
11
|
+
export function uniqueId(prefix = "shadcn") {
|
|
12
|
+
counter += 1
|
|
13
|
+
return `${prefix}-${counter}`
|
|
14
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Entry point for the Stimulus controllers that reproduce the Radix UI
|
|
2
|
+
// behaviour of the interactive components.
|
|
3
|
+
//
|
|
4
|
+
// import { Application } from "@hotwired/stimulus"
|
|
5
|
+
// import { registerShadcnControllers } from "shadcn"
|
|
6
|
+
//
|
|
7
|
+
// registerShadcnControllers(Application.start())
|
|
8
|
+
//
|
|
9
|
+
// Controllers register under the `shadcn--` prefix, which is what the
|
|
10
|
+
// components emit: `data-controller="shadcn--accordion"`.
|
|
11
|
+
|
|
12
|
+
import AccordionController from "shadcn/controllers/accordion_controller"
|
|
13
|
+
import AvatarController from "shadcn/controllers/avatar_controller"
|
|
14
|
+
import CalendarController from "shadcn/controllers/calendar_controller"
|
|
15
|
+
import CarouselController from "shadcn/controllers/carousel_controller"
|
|
16
|
+
import ChartController from "shadcn/controllers/chart_controller"
|
|
17
|
+
import ComboboxController from "shadcn/controllers/combobox_controller"
|
|
18
|
+
import CommandController from "shadcn/controllers/command_controller"
|
|
19
|
+
import CheckboxController from "shadcn/controllers/checkbox_controller"
|
|
20
|
+
import CollapsibleController from "shadcn/controllers/collapsible_controller"
|
|
21
|
+
import DialogController from "shadcn/controllers/dialog_controller"
|
|
22
|
+
import DrawerController from "shadcn/controllers/drawer_controller"
|
|
23
|
+
import DropdownMenuController from "shadcn/controllers/dropdown_menu_controller"
|
|
24
|
+
import HoverCardController from "shadcn/controllers/hover_card_controller"
|
|
25
|
+
import InputGroupController from "shadcn/controllers/input_group_controller"
|
|
26
|
+
import InputOtpController from "shadcn/controllers/input_otp_controller"
|
|
27
|
+
import MenubarController from "shadcn/controllers/menubar_controller"
|
|
28
|
+
import MessageScrollerController from "shadcn/controllers/message_scroller_controller"
|
|
29
|
+
import NavigationMenuController from "shadcn/controllers/navigation_menu_controller"
|
|
30
|
+
import PopoverController from "shadcn/controllers/popover_controller"
|
|
31
|
+
import RadioGroupController from "shadcn/controllers/radio_group_controller"
|
|
32
|
+
import ResizableController from "shadcn/controllers/resizable_controller"
|
|
33
|
+
import ScrollAreaController from "shadcn/controllers/scroll_area_controller"
|
|
34
|
+
import SelectController from "shadcn/controllers/select_controller"
|
|
35
|
+
import SidebarController from "shadcn/controllers/sidebar_controller"
|
|
36
|
+
import SliderController from "shadcn/controllers/slider_controller"
|
|
37
|
+
import SwitchController from "shadcn/controllers/switch_controller"
|
|
38
|
+
import TabsController from "shadcn/controllers/tabs_controller"
|
|
39
|
+
import ToasterController from "shadcn/controllers/toaster_controller"
|
|
40
|
+
import ThemeController from "shadcn/controllers/theme_controller"
|
|
41
|
+
import ToggleController from "shadcn/controllers/toggle_controller"
|
|
42
|
+
import ToggleGroupController from "shadcn/controllers/toggle_group_controller"
|
|
43
|
+
import TooltipController from "shadcn/controllers/tooltip_controller"
|
|
44
|
+
|
|
45
|
+
const CONTROLLERS = {
|
|
46
|
+
accordion: AccordionController,
|
|
47
|
+
avatar: AvatarController,
|
|
48
|
+
calendar: CalendarController,
|
|
49
|
+
carousel: CarouselController,
|
|
50
|
+
chart: ChartController,
|
|
51
|
+
checkbox: CheckboxController,
|
|
52
|
+
combobox: ComboboxController,
|
|
53
|
+
command: CommandController,
|
|
54
|
+
collapsible: CollapsibleController,
|
|
55
|
+
dialog: DialogController,
|
|
56
|
+
drawer: DrawerController,
|
|
57
|
+
"dropdown-menu": DropdownMenuController,
|
|
58
|
+
"hover-card": HoverCardController,
|
|
59
|
+
"input-group": InputGroupController,
|
|
60
|
+
"input-otp": InputOtpController,
|
|
61
|
+
menubar: MenubarController,
|
|
62
|
+
"message-scroller": MessageScrollerController,
|
|
63
|
+
"navigation-menu": NavigationMenuController,
|
|
64
|
+
popover: PopoverController,
|
|
65
|
+
"radio-group": RadioGroupController,
|
|
66
|
+
resizable: ResizableController,
|
|
67
|
+
"scroll-area": ScrollAreaController,
|
|
68
|
+
select: SelectController,
|
|
69
|
+
sidebar: SidebarController,
|
|
70
|
+
slider: SliderController,
|
|
71
|
+
switch: SwitchController,
|
|
72
|
+
tabs: TabsController,
|
|
73
|
+
theme: ThemeController,
|
|
74
|
+
toggle: ToggleController,
|
|
75
|
+
"toggle-group": ToggleGroupController,
|
|
76
|
+
toaster: ToasterController,
|
|
77
|
+
tooltip: TooltipController
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function registerShadcnControllers(application) {
|
|
81
|
+
for (const [ name, controller ] of Object.entries(CONTROLLERS)) {
|
|
82
|
+
application.register(`shadcn--${name}`, controller)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
resyncOnMorph(application)
|
|
86
|
+
|
|
87
|
+
return application
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// A Turbo morph refresh rewrites attributes in place without disconnecting the
|
|
91
|
+
// controllers, so `connect()` never runs again: the DOM goes back to whatever
|
|
92
|
+
// the server rendered while the controller still holds the old ids, targets and
|
|
93
|
+
// state. Re-running `render()` puts the two back in agreement.
|
|
94
|
+
//
|
|
95
|
+
// The server's state wins, which is what a refresh means. To keep a component's
|
|
96
|
+
// open state across a morph, mark it `data-turbo-permanent` in your own markup —
|
|
97
|
+
// that is the application's call, not the library's.
|
|
98
|
+
function resyncOnMorph(application) {
|
|
99
|
+
if (typeof document === "undefined") return
|
|
100
|
+
|
|
101
|
+
document.addEventListener("turbo:morph", () => {
|
|
102
|
+
for (const controller of application.controllers) {
|
|
103
|
+
if (!controller.identifier.startsWith("shadcn--")) continue
|
|
104
|
+
if (typeof controller.render === "function") controller.render()
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { CONTROLLERS }
|
|
110
|
+
export default registerShadcnControllers
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// Port of the positioning half of Radix's Popper.
|
|
2
|
+
//
|
|
3
|
+
// Radix wraps floating content in a fixed-position element carrying
|
|
4
|
+
// `data-radix-popper-content-wrapper` and publishes a set of CSS custom
|
|
5
|
+
// properties that the shadcn Tailwind classes read directly — e.g.
|
|
6
|
+
// `origin-(--radix-popover-content-transform-origin)` and
|
|
7
|
+
// `max-h-(--radix-select-content-available-height)`. Those names have to match
|
|
8
|
+
// exactly, so `prefix` names the component the variables belong to.
|
|
9
|
+
|
|
10
|
+
const SIDES = [ "top", "right", "bottom", "left" ]
|
|
11
|
+
|
|
12
|
+
const OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" }
|
|
13
|
+
|
|
14
|
+
export function createWrapper() {
|
|
15
|
+
const wrapper = document.createElement("div")
|
|
16
|
+
wrapper.setAttribute("data-radix-popper-content-wrapper", "")
|
|
17
|
+
Object.assign(wrapper.style, {
|
|
18
|
+
position: "fixed",
|
|
19
|
+
left: "0",
|
|
20
|
+
top: "0",
|
|
21
|
+
minWidth: "max-content",
|
|
22
|
+
zIndex: "50"
|
|
23
|
+
})
|
|
24
|
+
return wrapper
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function measure(anchor) {
|
|
28
|
+
const rect = anchor.getBoundingClientRect()
|
|
29
|
+
return {
|
|
30
|
+
top: rect.top,
|
|
31
|
+
left: rect.left,
|
|
32
|
+
right: rect.right,
|
|
33
|
+
bottom: rect.bottom,
|
|
34
|
+
width: rect.width,
|
|
35
|
+
height: rect.height
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function place(side, align, anchorRect, size, sideOffset, alignOffset) {
|
|
40
|
+
let x
|
|
41
|
+
let y
|
|
42
|
+
|
|
43
|
+
switch (side) {
|
|
44
|
+
case "top":
|
|
45
|
+
y = anchorRect.top - size.height - sideOffset
|
|
46
|
+
break
|
|
47
|
+
case "bottom":
|
|
48
|
+
y = anchorRect.bottom + sideOffset
|
|
49
|
+
break
|
|
50
|
+
case "left":
|
|
51
|
+
x = anchorRect.left - size.width - sideOffset
|
|
52
|
+
break
|
|
53
|
+
case "right":
|
|
54
|
+
x = anchorRect.right + sideOffset
|
|
55
|
+
break
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const vertical = side === "top" || side === "bottom"
|
|
59
|
+
|
|
60
|
+
if (vertical) {
|
|
61
|
+
if (align === "start") x = anchorRect.left + alignOffset
|
|
62
|
+
else if (align === "end") x = anchorRect.right - size.width - alignOffset
|
|
63
|
+
else x = anchorRect.left + anchorRect.width / 2 - size.width / 2 + alignOffset
|
|
64
|
+
} else {
|
|
65
|
+
if (align === "start") y = anchorRect.top + alignOffset
|
|
66
|
+
else if (align === "end") y = anchorRect.bottom - size.height - alignOffset
|
|
67
|
+
else y = anchorRect.top + anchorRect.height / 2 - size.height / 2 + alignOffset
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { x, y }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function fits(side, anchorRect, size, sideOffset, padding, viewport) {
|
|
74
|
+
switch (side) {
|
|
75
|
+
case "top":
|
|
76
|
+
return anchorRect.top - size.height - sideOffset >= padding
|
|
77
|
+
case "bottom":
|
|
78
|
+
return anchorRect.bottom + size.height + sideOffset <= viewport.height - padding
|
|
79
|
+
case "left":
|
|
80
|
+
return anchorRect.left - size.width - sideOffset >= padding
|
|
81
|
+
case "right":
|
|
82
|
+
return anchorRect.right + size.width + sideOffset <= viewport.width - padding
|
|
83
|
+
}
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function availableSpace(side, anchorRect, sideOffset, padding, viewport) {
|
|
88
|
+
switch (side) {
|
|
89
|
+
case "top":
|
|
90
|
+
return anchorRect.top - sideOffset - padding
|
|
91
|
+
case "bottom":
|
|
92
|
+
return viewport.height - anchorRect.bottom - sideOffset - padding
|
|
93
|
+
case "left":
|
|
94
|
+
return anchorRect.left - sideOffset - padding
|
|
95
|
+
case "right":
|
|
96
|
+
return viewport.width - anchorRect.right - sideOffset - padding
|
|
97
|
+
}
|
|
98
|
+
return 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Positions `content` (already inside a wrapper from `createWrapper`) relative
|
|
102
|
+
// to `anchor`, flipping and shifting to stay on screen. Returns the resolved
|
|
103
|
+
// side and align, which callers mirror onto `data-side` / `data-align`.
|
|
104
|
+
// Radix places the arrow itself, through Popper: a wrapper pinned to the side
|
|
105
|
+
// the content ended up on, offset along the cross axis so it points at the
|
|
106
|
+
// anchor's middle (vendor's `@radix-ui/react-popper`, measured on the live
|
|
107
|
+
// tooltip on 2026-08-12 — `position:absolute; bottom:0; transform:translateY(100%);
|
|
108
|
+
// left:43.5px` for a tooltip placed on top).
|
|
109
|
+
//
|
|
110
|
+
// The tooltip's content renders the same two elements Radix does — a bare
|
|
111
|
+
// wrapper and, inside it, the rotated square shadcn styles — because the
|
|
112
|
+
// styling classes set `rotate` and `translate`, which are their own CSS
|
|
113
|
+
// properties in Tailwind v4 and compose with `transform` rather than losing to
|
|
114
|
+
// it. Placement written on the same element fights them; on the wrapper it does
|
|
115
|
+
// not. Without any of this the arrow is laid out in the text flow after the
|
|
116
|
+
// label: rendered, never seen, which is how it shipped.
|
|
117
|
+
// Radix's own, verbatim: the wrapper is pinned to the edge opposite the side
|
|
118
|
+
// the content took, and each side turns it so the same square points outwards.
|
|
119
|
+
const ARROW_TRANSFORM = {
|
|
120
|
+
top: "translateY(100%)",
|
|
121
|
+
bottom: "rotate(180deg)",
|
|
122
|
+
left: "translateY(50%) rotate(-90deg) translateX(50%)",
|
|
123
|
+
right: "translateY(50%) rotate(90deg) translateX(-50%)"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// One origin per side, and they are not decoration: three of these transforms
|
|
127
|
+
// rotate, and a rotation is only as right as the point it turns about. A single
|
|
128
|
+
// origin for all four — which is what this had, and what put the left and right
|
|
129
|
+
// arrows somewhere nobody could see them — spins two of them off their own edge.
|
|
130
|
+
const ARROW_ORIGIN = {
|
|
131
|
+
top: "",
|
|
132
|
+
right: "0 0",
|
|
133
|
+
bottom: "center 0",
|
|
134
|
+
left: "100% 0"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function placeArrow(arrow, side, anchorRect, size, wrapperX, wrapperY) {
|
|
138
|
+
const width = arrow.offsetWidth || 10
|
|
139
|
+
const height = arrow.offsetHeight || 10
|
|
140
|
+
const vertical = side === "top" || side === "bottom"
|
|
141
|
+
const padding = 6
|
|
142
|
+
|
|
143
|
+
Object.assign(arrow.style, {
|
|
144
|
+
position: "absolute",
|
|
145
|
+
top: "", right: "", bottom: "", left: "",
|
|
146
|
+
transform: ARROW_TRANSFORM[side],
|
|
147
|
+
transformOrigin: ARROW_ORIGIN[side]
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
if (vertical) {
|
|
151
|
+
const centre = anchorRect.left + anchorRect.width / 2 - wrapperX - width / 2
|
|
152
|
+
arrow.style.left = `${Math.round(Math.min(Math.max(padding, centre), Math.max(padding, size.width - width - padding)))}px`
|
|
153
|
+
arrow.style[side === "top" ? "bottom" : "top"] = "0px"
|
|
154
|
+
} else {
|
|
155
|
+
const centre = anchorRect.top + anchorRect.height / 2 - wrapperY - height / 2
|
|
156
|
+
arrow.style.top = `${Math.round(Math.min(Math.max(padding, centre), Math.max(padding, size.height - height - padding)))}px`
|
|
157
|
+
arrow.style[side === "left" ? "right" : "left"] = "0px"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function position(anchor, content, options = {}) {
|
|
162
|
+
const {
|
|
163
|
+
side = "bottom",
|
|
164
|
+
align = "center",
|
|
165
|
+
sideOffset = 0,
|
|
166
|
+
alignOffset = 0,
|
|
167
|
+
collisionPadding = 8,
|
|
168
|
+
prefix = "popper",
|
|
169
|
+
matchAnchorWidth = false,
|
|
170
|
+
publishBaseUiVariables = false
|
|
171
|
+
} = options
|
|
172
|
+
|
|
173
|
+
const wrapper = content.parentElement
|
|
174
|
+
const viewport = { width: window.innerWidth, height: window.innerHeight }
|
|
175
|
+
const anchorRect = measure(anchor)
|
|
176
|
+
|
|
177
|
+
// An arrow has to fit between the panel and what it points at, so its height
|
|
178
|
+
// is part of the offset rather than something to subtract later — Radix does
|
|
179
|
+
// the same, `offset({ mainAxis: sideOffset + arrowHeight })` in
|
|
180
|
+
// `@radix-ui/react-popper`. Which is why a tooltip whose `sideOffset` is 0,
|
|
181
|
+
// as shadcn's is (tooltip.tsx:47), still stands clear of its trigger: without
|
|
182
|
+
// this the panel sits flush and the arrow lies across the thing it came from.
|
|
183
|
+
const arrow = content.querySelector("[data-slot$='-arrow']")
|
|
184
|
+
const offset = sideOffset + (arrow ? arrow.offsetHeight : 0)
|
|
185
|
+
|
|
186
|
+
if (matchAnchorWidth) content.style.minWidth = `${anchorRect.width}px`
|
|
187
|
+
|
|
188
|
+
// Measure without a stale transform in the way.
|
|
189
|
+
wrapper.style.transform = "translate(0, 0)"
|
|
190
|
+
const size = { width: content.offsetWidth, height: content.offsetHeight }
|
|
191
|
+
|
|
192
|
+
let resolvedSide = side
|
|
193
|
+
if (!fits(side, anchorRect, size, offset, collisionPadding, viewport)) {
|
|
194
|
+
const flipped = OPPOSITE[side]
|
|
195
|
+
if (fits(flipped, anchorRect, size, offset, collisionPadding, viewport)) {
|
|
196
|
+
resolvedSide = flipped
|
|
197
|
+
} else {
|
|
198
|
+
// Neither fits: take whichever has more room.
|
|
199
|
+
resolvedSide =
|
|
200
|
+
availableSpace(flipped, anchorRect, offset, collisionPadding, viewport) >
|
|
201
|
+
availableSpace(side, anchorRect, offset, collisionPadding, viewport)
|
|
202
|
+
? flipped
|
|
203
|
+
: side
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let { x, y } = place(resolvedSide, align, anchorRect, size, offset, alignOffset)
|
|
208
|
+
|
|
209
|
+
// Shift back into the viewport along the cross axis.
|
|
210
|
+
const maxX = viewport.width - size.width - collisionPadding
|
|
211
|
+
const maxY = viewport.height - size.height - collisionPadding
|
|
212
|
+
x = Math.min(Math.max(collisionPadding, x), Math.max(collisionPadding, maxX))
|
|
213
|
+
y = Math.min(Math.max(collisionPadding, y), Math.max(collisionPadding, maxY))
|
|
214
|
+
|
|
215
|
+
wrapper.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`
|
|
216
|
+
|
|
217
|
+
const available = availableSpace(resolvedSide, anchorRect, offset, collisionPadding, viewport)
|
|
218
|
+
const originX = resolvedSide === "left" ? "100%" : resolvedSide === "right" ? "0%" : "50%"
|
|
219
|
+
const originY = resolvedSide === "top" ? "100%" : resolvedSide === "bottom" ? "0%" : "50%"
|
|
220
|
+
const transformOrigin = `${originX} ${originY}`
|
|
221
|
+
|
|
222
|
+
const vars = {
|
|
223
|
+
[`--radix-${prefix}-content-transform-origin`]: transformOrigin,
|
|
224
|
+
[`--radix-${prefix}-content-available-width`]: `${viewport.width - collisionPadding * 2}px`,
|
|
225
|
+
[`--radix-${prefix}-content-available-height`]: `${Math.max(0, available)}px`,
|
|
226
|
+
"--radix-popper-transform-origin": transformOrigin,
|
|
227
|
+
"--radix-popper-available-width": `${viewport.width - collisionPadding * 2}px`,
|
|
228
|
+
"--radix-popper-available-height": `${Math.max(0, available)}px`,
|
|
229
|
+
"--radix-popper-anchor-width": `${anchorRect.width}px`,
|
|
230
|
+
"--radix-popper-anchor-height": `${anchorRect.height}px`,
|
|
231
|
+
[`--radix-${prefix}-trigger-width`]: `${anchorRect.width}px`,
|
|
232
|
+
[`--radix-${prefix}-trigger-height`]: `${anchorRect.height}px`,
|
|
233
|
+
// Base UI spells the same four numbers without a prefix, and
|
|
234
|
+
// `combobox.tsx` — the one file of the 61 written against Base UI — reads
|
|
235
|
+
// them: `w-(--anchor-width)`, `max-w-(--available-width)`,
|
|
236
|
+
// `origin-(--transform-origin)`, and the list's `--available-height`.
|
|
237
|
+
// Published only where a caller asks, because an unprefixed name in a
|
|
238
|
+
// host's page is a name a host may already be using.
|
|
239
|
+
...(publishBaseUiVariables
|
|
240
|
+
? {
|
|
241
|
+
"--anchor-width": `${anchorRect.width}px`,
|
|
242
|
+
"--available-width": `${viewport.width - collisionPadding * 2}px`,
|
|
243
|
+
"--available-height": `${Math.max(0, available)}px`,
|
|
244
|
+
"--transform-origin": transformOrigin
|
|
245
|
+
}
|
|
246
|
+
: {})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const [ name, value ] of Object.entries(vars)) {
|
|
250
|
+
content.style.setProperty(name, value)
|
|
251
|
+
wrapper.style.setProperty(name, value)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
content.dataset.side = resolvedSide
|
|
255
|
+
content.dataset.align = align
|
|
256
|
+
|
|
257
|
+
if (arrow) placeArrow(arrow, resolvedSide, anchorRect, size, x, y)
|
|
258
|
+
|
|
259
|
+
return { side: resolvedSide, align }
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export { SIDES }
|