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,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module ToggleGroup
|
|
5
|
+
module Item
|
|
6
|
+
# ToggleGroupItem — styled with the toggle's own variants, like the TSX
|
|
7
|
+
# does via `toggleVariants({ variant, size })`.
|
|
8
|
+
class Component < ApplicationViewComponent
|
|
9
|
+
EXTRA_CLASSES = "w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10 " \
|
|
10
|
+
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none " \
|
|
11
|
+
"data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md " \
|
|
12
|
+
"data-[spacing=0]:data-[variant=outline]:border-l-0 " \
|
|
13
|
+
"data-[spacing=0]:data-[variant=outline]:first:border-l"
|
|
14
|
+
|
|
15
|
+
default_tag :button
|
|
16
|
+
slot_name :"toggle-group-item"
|
|
17
|
+
|
|
18
|
+
attr_reader :value, :variant, :size, :spacing, :pressed, :disabled
|
|
19
|
+
|
|
20
|
+
def initialize(value:, variant: :default, size: :default, spacing: 0,
|
|
21
|
+
pressed: false, disabled: false, **attributes)
|
|
22
|
+
@value = value.to_s
|
|
23
|
+
@variant = variant&.to_sym || :default
|
|
24
|
+
@size = size&.to_sym || :default
|
|
25
|
+
@spacing = spacing
|
|
26
|
+
@pressed = pressed
|
|
27
|
+
@disabled = disabled
|
|
28
|
+
super(**attributes)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def element_attributes(**defaults)
|
|
32
|
+
super(**{
|
|
33
|
+
type: "button",
|
|
34
|
+
disabled: (true if disabled),
|
|
35
|
+
tabindex: "-1",
|
|
36
|
+
"aria-pressed" => pressed.to_s,
|
|
37
|
+
"data-state" => (pressed ? "on" : "off"),
|
|
38
|
+
"data-value" => value,
|
|
39
|
+
"data-variant" => variant,
|
|
40
|
+
"data-size" => size,
|
|
41
|
+
"data-spacing" => spacing,
|
|
42
|
+
"data-disabled" => (true if disabled),
|
|
43
|
+
"data-shadcn--toggle-group-target" => "item",
|
|
44
|
+
"data-action" => "click->shadcn--toggle-group#toggle " \
|
|
45
|
+
"keydown->shadcn--toggle-group#keydown"
|
|
46
|
+
}.merge(defaults))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def css_classes(extra = nil)
|
|
50
|
+
Toggle::Component.variant_classes(
|
|
51
|
+
variant:,
|
|
52
|
+
size:,
|
|
53
|
+
class: ShadcnViewComponent.cn(EXTRA_CLASSES, extra)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module ToggleGroup
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
def default
|
|
7
|
+
render_with_template
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Upstream's "Size" and "Disabled". Its "Vertical" is the other registry's.
|
|
11
|
+
def sizes
|
|
12
|
+
render_with_template
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<div class="flex flex-col items-start gap-6">
|
|
2
|
+
<%= render(Shadcn::ToggleGroup::Component.new(type: :multiple, variant: :outline)) do |g| %>
|
|
3
|
+
<% %w[Bold Italic Underline].each do |label| %>
|
|
4
|
+
<% g.with_item(value: label.downcase) { label } %>
|
|
5
|
+
<% end %>
|
|
6
|
+
<% end %>
|
|
7
|
+
<%= render(Shadcn::ToggleGroup::Component.new(type: :single, spacing: 2, value: "center")) do |g| %>
|
|
8
|
+
<% %w[Left Center Right].each do |label| %>
|
|
9
|
+
<% g.with_item(value: label.downcase, pressed: label == "Center") { label } %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<%# Upstream's "Size" and "Disabled".
|
|
2
|
+
|
|
3
|
+
Icon-only items, because that is where the size shows. The three are
|
|
4
|
+
`h-8 min-w-8`, `h-9 min-w-9` and `h-10 min-w-10` — with a text label the
|
|
5
|
+
width comes from the text and only 4px of height separates them, which is
|
|
6
|
+
what the first version of this preview looked like: three rows the same.
|
|
7
|
+
|
|
8
|
+
Upstream's "Vertical" is not here. `toggle-group.tsx` takes no orientation
|
|
9
|
+
at all, so there is nothing in this registry to show — the first version
|
|
10
|
+
passed `orientation:`, which landed as an inert HTML attribute, and the
|
|
11
|
+
version after it reached for `flex-col`, which stacks items of three
|
|
12
|
+
different widths because the group is `w-fit`. Both were this port
|
|
13
|
+
inventing a variant. See todo.md for the others upstream's docs show and
|
|
14
|
+
this registry does not have. %>
|
|
15
|
+
<div class="flex items-center gap-6">
|
|
16
|
+
<% %i[sm default lg].each do |size| %>
|
|
17
|
+
<div class="flex flex-col items-center gap-1">
|
|
18
|
+
<%= render(Shadcn::ToggleGroup::Component.new(type: :single, variant: :outline, size:, value: "check")) do |g| %>
|
|
19
|
+
<% %w[check circle x].each do |icon| %>
|
|
20
|
+
<% g.with_item(value: icon, pressed: icon == "check", "aria-label": icon) do %>
|
|
21
|
+
<%= render(Shadcn::Icon::Component.new(icon)) %>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
<span class="text-xs text-muted-foreground"><%= size %></span>
|
|
26
|
+
</div>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<div class="flex flex-col items-center gap-1">
|
|
30
|
+
<%= render(Shadcn::ToggleGroup::Component.new(type: :single, variant: :outline, disabled: true, value: "b")) do |g| %>
|
|
31
|
+
<% %w[A B C].each do |label| %>
|
|
32
|
+
<% g.with_item(value: label.downcase, pressed: label == "B") { label } %>
|
|
33
|
+
<% end %>
|
|
34
|
+
<% end %>
|
|
35
|
+
<span class="text-xs text-muted-foreground">disabled</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Tooltip
|
|
5
|
+
# Port of registry/new-york-v4/ui/tooltip.tsx
|
|
6
|
+
class Component < ApplicationViewComponent
|
|
7
|
+
renders_one :trigger, "Shadcn::Tooltip::Trigger::Component"
|
|
8
|
+
renders_one :tooltip_content, "Shadcn::Tooltip::Content::Component"
|
|
9
|
+
|
|
10
|
+
slot_name :tooltip
|
|
11
|
+
|
|
12
|
+
attr_reader :side, :align, :side_offset, :delay_duration
|
|
13
|
+
|
|
14
|
+
def initialize(side: :top, align: :center, side_offset: 0, delay_duration: 0, **attributes)
|
|
15
|
+
@side = side&.to_sym || :top
|
|
16
|
+
@align = align&.to_sym || :center
|
|
17
|
+
@side_offset = side_offset
|
|
18
|
+
@delay_duration = delay_duration
|
|
19
|
+
super(**attributes)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def element_attributes(**defaults)
|
|
23
|
+
super(**{
|
|
24
|
+
style: merged_style(CONTENTS_STYLE),
|
|
25
|
+
"data-controller" => "shadcn--tooltip",
|
|
26
|
+
"data-shadcn--tooltip-side-value" => side,
|
|
27
|
+
"data-shadcn--tooltip-align-value" => align,
|
|
28
|
+
"data-shadcn--tooltip-side-offset-value" => side_offset,
|
|
29
|
+
"data-shadcn--tooltip-delay-value" => delay_duration
|
|
30
|
+
}.merge(defaults))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
render_element(body: safe_join([ trigger, tooltip_content, content ].compact))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Tooltip
|
|
5
|
+
module Content
|
|
6
|
+
# TooltipContent — includes the rotated square Radix renders as the arrow.
|
|
7
|
+
class Component < ApplicationViewComponent
|
|
8
|
+
ARROW_CLASSES = "z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 " \
|
|
9
|
+
"rounded-[2px] bg-foreground fill-foreground"
|
|
10
|
+
|
|
11
|
+
slot_name :"tooltip-content"
|
|
12
|
+
|
|
13
|
+
style do
|
|
14
|
+
base {
|
|
15
|
+
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in " \
|
|
16
|
+
"rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background " \
|
|
17
|
+
"fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 " \
|
|
18
|
+
"data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 " \
|
|
19
|
+
"data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out " \
|
|
20
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def element_attributes(**defaults)
|
|
25
|
+
super(**{
|
|
26
|
+
role: "tooltip",
|
|
27
|
+
"data-state" => "closed",
|
|
28
|
+
hidden: true,
|
|
29
|
+
"data-shadcn--tooltip-target" => "content"
|
|
30
|
+
}.merge(defaults))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
render_element(body: safe_join([ content, arrow ]))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Two elements, as Radix renders them: a wrapper that Popper pins to the
|
|
40
|
+
# side the content landed on, and inside it the rotated square shadcn
|
|
41
|
+
# styles. They cannot be one element — the classes set `rotate` and
|
|
42
|
+
# `translate`, which in Tailwind v4 are their own CSS properties and
|
|
43
|
+
# compose with `transform` rather than being overridden by it, so a
|
|
44
|
+
# placement written on the same element fights them instead of moving
|
|
45
|
+
# them. `popper.js` positions the wrapper.
|
|
46
|
+
#
|
|
47
|
+
# `display:block` on both, in a style attribute rather than a class,
|
|
48
|
+
# because upstream's arrow is an `<svg>` — a replaced element, which
|
|
49
|
+
# takes a width and a height where an inline `<span>` does not. Without
|
|
50
|
+
# it `size-2.5` applies to nothing and the arrow is 10px of intent and
|
|
51
|
+
# 0px of box: which is how this shipped, invisible however it was
|
|
52
|
+
# placed. A class would have to be one no vendored source carries, and
|
|
53
|
+
# `reverse_parity_spec` is there to notice that.
|
|
54
|
+
def arrow
|
|
55
|
+
tag.span("data-slot": "tooltip-arrow", style: "position:absolute;display:block") do
|
|
56
|
+
tag.span(class: ARROW_CLASSES, style: "display:block")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Tooltip
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
def default
|
|
7
|
+
render_with_template
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Upstream's "Side": the same tooltip on each of the four edges.
|
|
11
|
+
def sides
|
|
12
|
+
render_with_template
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Upstream's "With Keyboard Shortcut".
|
|
16
|
+
def with_keyboard_shortcut
|
|
17
|
+
render_with_template
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%# Upstream's "Side": the same tooltip on each of the four edges, and the arrow
|
|
2
|
+
that points back at the trigger from each.
|
|
3
|
+
|
|
4
|
+
`side:` goes to the *root*, not to the content. Passed to
|
|
5
|
+
`with_tooltip_content` it is swallowed into the content's attributes and
|
|
6
|
+
quietly does nothing — which is what the first version of this preview did,
|
|
7
|
+
leaving all four asking for `top` and looking, correctly, identical.
|
|
8
|
+
|
|
9
|
+
Room on every side on purpose: the popper flips a tooltip that will not fit,
|
|
10
|
+
so a cramped page shows four tooltips on top and says nothing about `side:`
|
|
11
|
+
either. %>
|
|
12
|
+
<div class="flex h-72 items-center justify-center gap-16 px-24">
|
|
13
|
+
<% %i[top right bottom left].each do |side| %>
|
|
14
|
+
<%= render(Shadcn::Tooltip::Component.new(side:)) do |t| %>
|
|
15
|
+
<% t.with_trigger(class: Shadcn::Button::Component.variant_classes(variant: :outline)) { side.to_s.capitalize } %>
|
|
16
|
+
<% t.with_tooltip_content { "On the #{side}" } %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%# Upstream's "With Keyboard Shortcut": the tooltip carries the shortcut for
|
|
2
|
+
the thing it names, which is the case a `Kbd` inside one exists for. %>
|
|
3
|
+
<%= render(Shadcn::Tooltip::Component.new) do |t| %>
|
|
4
|
+
<% t.with_trigger(class: Shadcn::Button::Component.variant_classes(variant: :outline)) { "Save" } %>
|
|
5
|
+
<% t.with_tooltip_content do %>
|
|
6
|
+
<span class="flex items-center gap-2">
|
|
7
|
+
Save changes
|
|
8
|
+
<%= render(Shadcn::Kbd::Component.new) { "⌘S" } %>
|
|
9
|
+
</span>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Tooltip
|
|
5
|
+
module Provider
|
|
6
|
+
# TooltipProvider — in React this only supplies the shared delay through
|
|
7
|
+
# context. Kept as a component so call sites read like the TSX; it renders
|
|
8
|
+
# nothing but its children.
|
|
9
|
+
class Component < ApplicationViewComponent
|
|
10
|
+
slot_name :"tooltip-provider"
|
|
11
|
+
|
|
12
|
+
attr_reader :delay_duration
|
|
13
|
+
|
|
14
|
+
def initialize(delay_duration: 0, **attributes)
|
|
15
|
+
@delay_duration = delay_duration
|
|
16
|
+
super(**attributes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def element_attributes(**defaults)
|
|
20
|
+
super(**{
|
|
21
|
+
style: merged_style(CONTENTS_STYLE),
|
|
22
|
+
"data-delay-duration" => delay_duration
|
|
23
|
+
}.merge(defaults))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Tooltip
|
|
5
|
+
module Trigger
|
|
6
|
+
# TooltipTrigger — opens on hover and on keyboard focus.
|
|
7
|
+
class Component < ApplicationViewComponent
|
|
8
|
+
default_tag :button
|
|
9
|
+
slot_name :"tooltip-trigger"
|
|
10
|
+
|
|
11
|
+
def element_attributes(**defaults)
|
|
12
|
+
super(**{
|
|
13
|
+
type: "button",
|
|
14
|
+
"data-state" => "closed",
|
|
15
|
+
"data-shadcn--tooltip-target" => "trigger",
|
|
16
|
+
"data-action" => "mouseenter->shadcn--tooltip#show " \
|
|
17
|
+
"mouseleave->shadcn--tooltip#hide " \
|
|
18
|
+
"focus->shadcn--tooltip#show " \
|
|
19
|
+
"blur->shadcn--tooltip#hide"
|
|
20
|
+
}.merge(defaults))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShadcnViewComponent
|
|
4
|
+
# `shadcn_form_with` is `form_with` with the shadcn builder already set, so
|
|
5
|
+
# the `f.shadcn_*` helpers are available without repeating `builder:`.
|
|
6
|
+
#
|
|
7
|
+
# <%= shadcn_form_with model: @user do |f| %>
|
|
8
|
+
# <%= f.shadcn_input_field :email, label: "Email", type: "email" %>
|
|
9
|
+
# <%= f.shadcn_submit "Save" %>
|
|
10
|
+
# <% end %>
|
|
11
|
+
#
|
|
12
|
+
# `form_with(..., builder: ShadcnViewComponent::FormBuilder)` does the same
|
|
13
|
+
# thing if you would rather be explicit.
|
|
14
|
+
module FormHelper
|
|
15
|
+
def shadcn_form_with(**options, &block)
|
|
16
|
+
form_with(**options.reverse_merge(builder: ShadcnViewComponent::FormBuilder), &block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# For `fields_for` / `form_for` call sites that want the builder without the
|
|
20
|
+
# helper above.
|
|
21
|
+
def shadcn_form_builder
|
|
22
|
+
ShadcnViewComponent::FormBuilder
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShadcnViewComponent
|
|
4
|
+
# View helpers for the theming system.
|
|
5
|
+
#
|
|
6
|
+
# <html>
|
|
7
|
+
# <head><%= shadcn_theme_script_tag %></head>
|
|
8
|
+
# <body class="<%= shadcn_theme_class %>">
|
|
9
|
+
#
|
|
10
|
+
# `shadcn_theme_script_tag` is what stops the flash of the wrong palette: it
|
|
11
|
+
# runs before the first paint, reads the stored preference and puts `.dark`
|
|
12
|
+
# and `data-shadcn-theme` on <html> itself, since <body> does not exist yet.
|
|
13
|
+
module ThemeHelper
|
|
14
|
+
MODE_COOKIE = "shadcn-ui-mode"
|
|
15
|
+
THEME_COOKIE = "shadcn-ui-theme"
|
|
16
|
+
|
|
17
|
+
# Emits the blocking snippet that applies the stored preference before the
|
|
18
|
+
# page paints. Put it in <head>, as early as possible.
|
|
19
|
+
#
|
|
20
|
+
# @param default_mode [String] "system", "light" or "dark"
|
|
21
|
+
# @param default_theme [String] a name from ShadcnViewComponent::Themes
|
|
22
|
+
def shadcn_theme_script_tag(default_mode: "system", default_theme: Themes::DEFAULT)
|
|
23
|
+
javascript_tag(nonce: true) do
|
|
24
|
+
raw(<<~JS)
|
|
25
|
+
(function () {
|
|
26
|
+
try {
|
|
27
|
+
var root = document.documentElement;
|
|
28
|
+
var mode = localStorage.getItem("#{MODE_COOKIE}") || "#{default_mode}";
|
|
29
|
+
var theme = localStorage.getItem("#{THEME_COOKIE}") || "#{default_theme}";
|
|
30
|
+
var dark = mode === "dark" ||
|
|
31
|
+
(mode === "system" && matchMedia("(prefers-color-scheme: dark)").matches);
|
|
32
|
+
|
|
33
|
+
root.classList.toggle("dark", dark);
|
|
34
|
+
root.style.colorScheme = dark ? "dark" : "light";
|
|
35
|
+
root.dataset.shadcnTheme = theme;
|
|
36
|
+
} catch (e) {}
|
|
37
|
+
})();
|
|
38
|
+
JS
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The colour theme this request should render with, from the cookie the
|
|
43
|
+
# store mirrors its `localStorage` value into. Lets a server-rendered page
|
|
44
|
+
# be correct on the very first byte.
|
|
45
|
+
def shadcn_theme_name(default: Themes::DEFAULT)
|
|
46
|
+
name = cookies[THEME_COOKIE].to_s
|
|
47
|
+
Themes.exists?(name) ? name : default
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The class to put on <body>.
|
|
51
|
+
def shadcn_theme_class(default: Themes::DEFAULT)
|
|
52
|
+
"theme-#{shadcn_theme_name(default:)}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The mode this request should render with: "light", "dark" or "system".
|
|
56
|
+
def shadcn_mode(default: "system")
|
|
57
|
+
mode = cookies[MODE_COOKIE].to_s
|
|
58
|
+
%w[light dark system].include?(mode) ? mode : default
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Closing a layer has two halves that used to happen in one tick, which is why
|
|
2
|
+
// no `data-[state=closed]:animate-out` class in this library ever played.
|
|
3
|
+
//
|
|
4
|
+
// `data-state="closed"` and everything about *interaction* — the dismiss layer,
|
|
5
|
+
// aria, focus, the scroll lock — has to be immediate: a layer on its way out
|
|
6
|
+
// must not answer Escape. Taking the element out of the DOM has to wait, or the
|
|
7
|
+
// exit keyframes never get a frame.
|
|
8
|
+
//
|
|
9
|
+
// `getAnimations()` rather than an `animationend` listener. The event bubbles
|
|
10
|
+
// from descendants, so it would need filtering by both target and name; worse,
|
|
11
|
+
// it never fires when no animation applies, which forces a timeout — and that
|
|
12
|
+
// timeout then becomes the *normal* path in a host that has not loaded this
|
|
13
|
+
// gem's stylesheet. An empty animation list is unambiguous and needs no
|
|
14
|
+
// fallback.
|
|
15
|
+
|
|
16
|
+
// The animations worth holding a teardown for: the ones already under way that
|
|
17
|
+
// are also going to end.
|
|
18
|
+
//
|
|
19
|
+
// `playState === "running"` on its own is not that: an
|
|
20
|
+
// `animation-iteration-count: infinite` animation reports `"running"` and has
|
|
21
|
+
// no end to reach. Caller classes concatenate onto a component's own, so a host
|
|
22
|
+
// utility carrying one — `animate-pulse` among them — can land on closing
|
|
23
|
+
// content through supported API, and waiting on its `finished` leaves the layer
|
|
24
|
+
// in the document, painted and `pointer-events: none`, after everything that
|
|
25
|
+
// closes it has already run. Measured before this filter existed: an Escaped
|
|
26
|
+
// dialog still in the document five seconds later, `hidden` never set. An
|
|
27
|
+
// animation that never ends is not the exit either way, so nothing is lost by
|
|
28
|
+
// leaving it out. A timeout was the alternative: an arbitrary constant, and the
|
|
29
|
+
// layer would still be stuck for its length.
|
|
30
|
+
//
|
|
31
|
+
// `endTime` rather than `iterations` because it is the one number that answers
|
|
32
|
+
// "will this ever end" whatever put the animation there. Measured in headless
|
|
33
|
+
// Chrome: `Infinity` for both spellings — CSS `infinite` and
|
|
34
|
+
// `element.animate(…, { iterations: Infinity })` — and not a number at all for
|
|
35
|
+
// an animation on a `scroll()` timeline, which has no end on a clock either.
|
|
36
|
+
// `Number.isFinite` rejects all three.
|
|
37
|
+
const willEnd = (animation) =>
|
|
38
|
+
animation.playState === "running" &&
|
|
39
|
+
Number.isFinite(animation.effect?.getComputedTiming()?.endTime)
|
|
40
|
+
|
|
41
|
+
// A second literal, not a shared one: this selector is retyped to match
|
|
42
|
+
// `[data-slot][data-exiting]:not([data-slot="accordion-content"])` in
|
|
43
|
+
// shadcn.css, not read from it, so nothing in the code stops the two from
|
|
44
|
+
// drifting apart if one changes and the other does not. What actually catches
|
|
45
|
+
// that today is `exit_animation_spec.rb:426` — it probes a real control
|
|
46
|
+
// inside a collapsing panel for focus and hit-testing, which fails the same
|
|
47
|
+
// way whether the CSS exception goes missing or this one does.
|
|
48
|
+
//
|
|
49
|
+
// A collapsing accordion panel sits in the page flow with nothing behind it to
|
|
50
|
+
// protect from a click, which is what that CSS exception is for — and `inert` is
|
|
51
|
+
// a stronger tool than `pointer-events`, not an equivalent one: it drops the
|
|
52
|
+
// subtree out of hit-testing itself, so a control inside would neither focus nor
|
|
53
|
+
// receive a click no matter what its own `pointer-events` computed to. Applying
|
|
54
|
+
// `inert` here without the same carve-out would silently defeat the CSS rule's
|
|
55
|
+
// exception rather than agree with it. Measured with the carve-out missing: a
|
|
56
|
+
// button inside a collapsing panel read `pointer-events: auto`, was not
|
|
57
|
+
// focusable, and `elementFromPoint` at its centre returned the trigger instead.
|
|
58
|
+
const exemptFromInert = (element) => element.matches("[data-slot='accordion-content']")
|
|
59
|
+
|
|
60
|
+
// Holds teardowns that are waiting on an element's exit animation, one per
|
|
61
|
+
// element.
|
|
62
|
+
export class ExitQueue {
|
|
63
|
+
constructor() {
|
|
64
|
+
this.pending = new Map()
|
|
65
|
+
this.generation = 0
|
|
66
|
+
this.onBeforeCache = null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Runs `teardown` once the animations already running on `element` have
|
|
70
|
+
// finished — or immediately and synchronously when there are none. That last
|
|
71
|
+
// part is the point: a host that never loaded this stylesheet, a host that
|
|
72
|
+
// overrode the classes away, and a host whose only animation here is an
|
|
73
|
+
// endless one all keep exactly today's behaviour.
|
|
74
|
+
//
|
|
75
|
+
// A second call for an element already waiting is ignored, so a `turbo:morph`
|
|
76
|
+
// re-render in the middle of an exit cannot queue the same teardown twice.
|
|
77
|
+
defer(element, teardown) {
|
|
78
|
+
if (this.pending.has(element)) return
|
|
79
|
+
|
|
80
|
+
const running = element.getAnimations().filter(willEnd)
|
|
81
|
+
|
|
82
|
+
if (!running.length) {
|
|
83
|
+
teardown()
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Marks the element as exiting for the length of the animation only — it
|
|
88
|
+
// is never rendered markup. Read by `[data-slot][data-exiting]` in
|
|
89
|
+
// shadcn.css, which stops most exiting elements intercepting clicks, as
|
|
90
|
+
// Radix does; that rule, not this marker, is what carries the accordion's
|
|
91
|
+
// exception.
|
|
92
|
+
element.dataset.exiting = ""
|
|
93
|
+
|
|
94
|
+
// `data-exiting` and `inert` are not two spellings of one thing: the
|
|
95
|
+
// marker above is a CSS hook, deliberately excluded for the accordion so
|
|
96
|
+
// its collapsing panel keeps taking clicks; `inert` takes the element out
|
|
97
|
+
// of the tab order and the accessibility tree instead, exempting the same
|
|
98
|
+
// accordion panel via `exemptFromInert` (above, and see its comment for
|
|
99
|
+
// how that exemption is kept in step with the CSS one) — `hidden` used to
|
|
100
|
+
// do both jobs, in the same tick, before either started waiting on this
|
|
101
|
+
// queue.
|
|
102
|
+
if (!exemptFromInert(element)) element.inert = true
|
|
103
|
+
|
|
104
|
+
// Tags this call's continuation with the generation it belongs to. Without
|
|
105
|
+
// it, cancel() followed by a fresh defer() in the same tick — reopen then
|
|
106
|
+
// close again before the first exit's promise has settled — would let the
|
|
107
|
+
// first continuation flush the second exit's teardown as soon as it drains,
|
|
108
|
+
// dismounting a layer whose new animation has barely started.
|
|
109
|
+
const generation = ++this.generation
|
|
110
|
+
this.pending.set(element, { teardown, generation })
|
|
111
|
+
this.watchTurbo()
|
|
112
|
+
|
|
113
|
+
// Reopening mid-exit cancels the animation, which rejects `finished`. Either
|
|
114
|
+
// way the waiting is over; whether the teardown still applies is decided by
|
|
115
|
+
// whoever calls `cancel`.
|
|
116
|
+
Promise.all(running.map((a) => a.finished))
|
|
117
|
+
.catch(() => {})
|
|
118
|
+
.then(() => {
|
|
119
|
+
if (this.pending.get(element)?.generation === generation) this.flush(element)
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Runs a pending teardown now. Called when the animation ends, and directly
|
|
124
|
+
// from `disconnect()`, where waiting is not an option: Turbo may be detaching
|
|
125
|
+
// the element, and a continuation would then be operating on a subtree that
|
|
126
|
+
// has left the document.
|
|
127
|
+
flush(element) {
|
|
128
|
+
const entry = this.pending.get(element)
|
|
129
|
+
if (!entry) return
|
|
130
|
+
|
|
131
|
+
this.pending.delete(element)
|
|
132
|
+
delete element.dataset.exiting
|
|
133
|
+
if (!exemptFromInert(element)) element.inert = false
|
|
134
|
+
this.unwatchTurbo()
|
|
135
|
+
entry.teardown()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Drops a pending teardown without running it — the element is opening again,
|
|
139
|
+
// so there is nothing left to take out of the DOM.
|
|
140
|
+
cancel(element) {
|
|
141
|
+
if (!this.pending.delete(element)) return
|
|
142
|
+
|
|
143
|
+
delete element.dataset.exiting
|
|
144
|
+
if (!exemptFromInert(element)) element.inert = false
|
|
145
|
+
this.unwatchTurbo()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
has(element) {
|
|
149
|
+
return this.pending.has(element)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
flushAll() {
|
|
153
|
+
for (const element of [ ...this.pending.keys() ]) this.flush(element)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// A pending teardown must never survive into a Turbo snapshot. `cacheSnapshot()`
|
|
157
|
+
// clones whatever is in the document while handling `turbo:before-cache` — well
|
|
158
|
+
// inside an exit animation's duration — so without this a layer closed by the
|
|
159
|
+
// same pointerdown that starts a Drive navigation gets cached mid-exit: wrapper,
|
|
160
|
+
// placeholder and `data-exiting` all cloned in. A later restore built from that
|
|
161
|
+
// clone would then nest a second wrapper the next time the layer opens, and the
|
|
162
|
+
// surviving `data-exiting` would leave it unclickable.
|
|
163
|
+
//
|
|
164
|
+
// One listener per queue, added only while something is pending and removed
|
|
165
|
+
// as soon as nothing is, rather than one at module load: most pages never
|
|
166
|
+
// have an exit in flight when they navigate away.
|
|
167
|
+
watchTurbo() {
|
|
168
|
+
if (this.onBeforeCache) return
|
|
169
|
+
|
|
170
|
+
this.onBeforeCache = () => this.flushAll()
|
|
171
|
+
document.addEventListener("turbo:before-cache", this.onBeforeCache)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
unwatchTurbo() {
|
|
175
|
+
if (this.pending.size || !this.onBeforeCache) return
|
|
176
|
+
|
|
177
|
+
document.removeEventListener("turbo:before-cache", this.onBeforeCache)
|
|
178
|
+
this.onBeforeCache = null
|
|
179
|
+
}
|
|
180
|
+
}
|