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,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module ThemeSelector
|
|
5
|
+
# Port of apps/v4/components/theme-selector.tsx — the select that swaps the
|
|
6
|
+
# colour palette.
|
|
7
|
+
#
|
|
8
|
+
# In React this reads `useThemeConfig()`; here each option hands its value
|
|
9
|
+
# to the `shadcn--theme` Stimulus controller, which swaps the `theme-*`
|
|
10
|
+
# class the way shadcn's `ActiveThemeProvider` does.
|
|
11
|
+
class Component < ApplicationViewComponent
|
|
12
|
+
slot_name :"theme-selector"
|
|
13
|
+
|
|
14
|
+
style do
|
|
15
|
+
base { "flex items-center gap-2" }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attr_reader :themes, :value
|
|
19
|
+
|
|
20
|
+
# @param themes [Array<Hash>] entries from ShadcnViewComponent::Themes
|
|
21
|
+
# @param value [String] the palette currently applied
|
|
22
|
+
def initialize(themes: ShadcnViewComponent::Themes::BASE_COLORS,
|
|
23
|
+
value: ShadcnViewComponent::Themes::DEFAULT,
|
|
24
|
+
label: nil,
|
|
25
|
+
**attributes)
|
|
26
|
+
@themes = themes
|
|
27
|
+
@value = value.to_s
|
|
28
|
+
@label = label
|
|
29
|
+
super(**attributes)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def label
|
|
33
|
+
@label || shadcn_t("theme.label")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def label_id
|
|
37
|
+
@label_id ||= "shadcn-theme-selector-#{SecureRandom.hex(4)}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def element_attributes(**defaults)
|
|
41
|
+
super(**{ "data-controller" => "shadcn--theme" }.merge(defaults))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def call
|
|
45
|
+
render_element(body: safe_join([ hidden_label, select ]))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def hidden_label
|
|
51
|
+
render(Label::Component.new(id: label_id, class: "sr-only")) { label }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def select
|
|
55
|
+
render(Select::Component.new(value:, placeholder: "Select a theme")) do |component|
|
|
56
|
+
component.with_trigger(
|
|
57
|
+
"aria-labelledby" => label_id,
|
|
58
|
+
class: "w-36",
|
|
59
|
+
placeholder: current_title.nil?
|
|
60
|
+
) do |trigger|
|
|
61
|
+
trigger.with_value(placeholder: "Select a theme") { current_title }
|
|
62
|
+
end
|
|
63
|
+
component.with_select_content { options }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def current_title
|
|
68
|
+
ShadcnViewComponent::Themes.find(value)&.fetch(:title)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def options
|
|
72
|
+
render(Select::Group::Component.new) do
|
|
73
|
+
safe_join([ render(Select::Label::Component.new) { label } ] + items)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def items
|
|
78
|
+
themes.map do |theme|
|
|
79
|
+
render(Select::Item::Component.new(
|
|
80
|
+
value: theme[:name],
|
|
81
|
+
selected: theme[:name] == value,
|
|
82
|
+
"data-action": "click->shadcn--theme#setTheme"
|
|
83
|
+
)) { theme[:title] }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module ThemeSelector
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
# The base colours a project picks from.
|
|
7
|
+
def default
|
|
8
|
+
render_with_template
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Every palette in the registry, accents included.
|
|
12
|
+
def all_themes
|
|
13
|
+
render_with_template
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<div class="flex flex-col gap-6">
|
|
2
|
+
<%= render(Shadcn::ThemeSelector::Component.new(
|
|
3
|
+
themes: ShadcnViewComponent::Themes::ALL,
|
|
4
|
+
value: shadcn_theme_name,
|
|
5
|
+
label: "Palette"
|
|
6
|
+
)) %>
|
|
7
|
+
|
|
8
|
+
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
|
9
|
+
<% ShadcnViewComponent::Themes::ALL.each do |theme| %>
|
|
10
|
+
<div class="theme-<%= theme[:name] %> flex flex-col gap-2 rounded-lg border bg-card p-3">
|
|
11
|
+
<span class="text-xs font-medium text-card-foreground"><%= theme[:title] %></span>
|
|
12
|
+
<div class="flex gap-1">
|
|
13
|
+
<% %w[bg-primary bg-secondary bg-muted bg-accent bg-destructive].each do |swatch| %>
|
|
14
|
+
<span class="size-5 rounded-sm border <%= swatch %>"></span>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div class="flex flex-col gap-6">
|
|
2
|
+
<%= render(Shadcn::ThemeSelector::Component.new(value: shadcn_theme_name)) %>
|
|
3
|
+
|
|
4
|
+
<%= render(Shadcn::Card::Component.new(class: "w-full max-w-sm")) do |card| %>
|
|
5
|
+
<% card.with_header do |header| %>
|
|
6
|
+
<% header.with_title { "Pick a palette" } %>
|
|
7
|
+
<% header.with_description { "The theme-* class on <body> re-points every token." } %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% card.with_card_content do %>
|
|
10
|
+
<div class="flex flex-wrap gap-2">
|
|
11
|
+
<%= render(Shadcn::Button::Component.new) { "Primary" } %>
|
|
12
|
+
<%= render(Shadcn::Button::Component.new(variant: :secondary)) { "Secondary" } %>
|
|
13
|
+
<%= render(Shadcn::Button::Component.new(variant: :destructive)) { "Destructive" } %>
|
|
14
|
+
<%= render(Shadcn::Badge::Component.new) { "Badge" } %>
|
|
15
|
+
</div>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Toaster
|
|
5
|
+
# Ours, not a port — and the distinction matters more here than anywhere
|
|
6
|
+
# else in this gem.
|
|
7
|
+
#
|
|
8
|
+
# `sonner.tsx` is forty lines and emits no `data-slot` at all: a theme, five
|
|
9
|
+
# icons and four custom properties handed to the `sonner` package, which
|
|
10
|
+
# supplies every element and every rule — 1,601 lines of TypeScript and 729
|
|
11
|
+
# of CSS. There is no upstream markup here to be 1:1 with, so `parity_spec`
|
|
12
|
+
# has nothing to check and this component's shape is this port's own.
|
|
13
|
+
#
|
|
14
|
+
# What is kept from upstream is what can be: sonner's measurements, read
|
|
15
|
+
# from its stylesheet — 356px wide, 14px between, 24px from the edge, four
|
|
16
|
+
# seconds of life — and the four colours `sonner.tsx` itself chooses, which
|
|
17
|
+
# are the popover's.
|
|
18
|
+
#
|
|
19
|
+
# And the way in is Rails': a toast is a component, so the server can raise
|
|
20
|
+
# one. Three routes, in order of how much an app will use them —
|
|
21
|
+
#
|
|
22
|
+
# flash `Toaster::Component.new(flash:)` turns each into a toast
|
|
23
|
+
# turbo_stream `turbo_stream.append "shadcn-toasts", …` onto this list
|
|
24
|
+
# javascript `document.dispatchEvent(new CustomEvent("shadcn--toast", …))`
|
|
25
|
+
#
|
|
26
|
+
# — where sonner has only the third. See features/toaster.md.
|
|
27
|
+
class Component < ApplicationViewComponent
|
|
28
|
+
POSITIONS = {
|
|
29
|
+
"top-left" => "top-6 left-6 items-start",
|
|
30
|
+
"top-center" => "top-6 left-1/2 -translate-x-1/2 items-center",
|
|
31
|
+
"top-right" => "top-6 right-6 items-end",
|
|
32
|
+
"bottom-left" => "bottom-6 left-6 items-start",
|
|
33
|
+
"bottom-center" => "bottom-6 left-1/2 -translate-x-1/2 items-center",
|
|
34
|
+
"bottom-right" => "bottom-6 right-6 items-end"
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# sonner's own, so a caller who has read its docs is not surprised.
|
|
38
|
+
DEFAULT_DURATION = 4000
|
|
39
|
+
|
|
40
|
+
renders_many :toasts, "Shadcn::Toaster::Toast::Component"
|
|
41
|
+
|
|
42
|
+
# A `<section>`, as sonner renders (index.tsx:788): `aria-label` on a bare
|
|
43
|
+
# `<div>` is prohibited ARIA — a label needs a role to belong to, and a
|
|
44
|
+
# section is the landmark that has one.
|
|
45
|
+
default_tag :section
|
|
46
|
+
slot_name :toaster
|
|
47
|
+
|
|
48
|
+
attr_reader :position, :duration, :list_id, :flash
|
|
49
|
+
|
|
50
|
+
def initialize(position: "bottom-right", duration: DEFAULT_DURATION,
|
|
51
|
+
list_id: "shadcn-toasts", flash: nil, **attributes)
|
|
52
|
+
@position = POSITIONS.key?(position.to_s) ? position.to_s : "bottom-right"
|
|
53
|
+
@duration = duration
|
|
54
|
+
@list_id = list_id
|
|
55
|
+
@flash = flash
|
|
56
|
+
super(**attributes)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def element_attributes(**defaults)
|
|
60
|
+
super(**{
|
|
61
|
+
class: "group/toaster pointer-events-none fixed z-[100] flex " \
|
|
62
|
+
"#{POSITIONS.fetch(position)}",
|
|
63
|
+
"aria-live" => "polite",
|
|
64
|
+
# sonner's own (index.tsx:792): each toast is read on its own, not as
|
|
65
|
+
# a re-reading of the whole stack every time one arrives.
|
|
66
|
+
"aria-atomic" => "false",
|
|
67
|
+
"aria-label" => shadcn_t("toaster.label"),
|
|
68
|
+
"data-position" => position,
|
|
69
|
+
"data-controller" => "shadcn--toaster",
|
|
70
|
+
"data-shadcn--toaster-duration-value" => duration,
|
|
71
|
+
"data-action" => "shadcn--toast@document->shadcn--toaster#raise"
|
|
72
|
+
}.merge(defaults))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def call
|
|
76
|
+
render_element(body: safe_join([ list, template ]))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
# The element a `turbo_stream.append` aims at, which is why it has an id
|
|
82
|
+
# by default and why the id is an argument rather than generated.
|
|
83
|
+
def list
|
|
84
|
+
tag.ol(safe_join([ flash_toasts, toasts, content ].flatten.compact),
|
|
85
|
+
id: list_id,
|
|
86
|
+
# The stack, and it is a stack rather than a column: the toasts
|
|
87
|
+
# are absolutely placed inside this box and the controller writes
|
|
88
|
+
# where each one sits. `w-[356px]` is sonner's width, and the
|
|
89
|
+
# height is the controller's — it is what the pointer has to be
|
|
90
|
+
# inside for the stack to open.
|
|
91
|
+
# No transition on the height, and that is deliberate: this box is
|
|
92
|
+
# the area a pointer has to be inside for the stack to stay open,
|
|
93
|
+
# and an area that arrives four hundred milliseconds late is an
|
|
94
|
+
# area that is wrong while it matters. The toasts animate; the
|
|
95
|
+
# box they are in does not. Measured in a browser that runs no
|
|
96
|
+
# animation frames, a transition here left it at zero for good.
|
|
97
|
+
class: "relative w-[356px] max-w-[calc(100vw-2rem)]",
|
|
98
|
+
"data-shadcn--toaster-target": "list",
|
|
99
|
+
"data-action": "pointerenter->shadcn--toaster#hold " \
|
|
100
|
+
"pointerleave->shadcn--toaster#release " \
|
|
101
|
+
"focusin->shadcn--toaster#hold " \
|
|
102
|
+
"focusout->shadcn--toaster#release")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Rails' own flash, rendered with the page. `:notice` and `:alert` are the
|
|
106
|
+
# two `redirect_to` sets without being asked, so they are mapped rather
|
|
107
|
+
# than left to be configured.
|
|
108
|
+
FLASH_VARIANTS = { "notice" => :success, "alert" => :error, "error" => :error,
|
|
109
|
+
"warning" => :warning, "info" => :info }.freeze
|
|
110
|
+
|
|
111
|
+
def flash_toasts
|
|
112
|
+
return [] if flash.blank?
|
|
113
|
+
|
|
114
|
+
flash.map do |key, message|
|
|
115
|
+
next if message.blank?
|
|
116
|
+
|
|
117
|
+
render(Toast::Component.new(title: message, variant: FLASH_VARIANTS.fetch(key.to_s, :default)))
|
|
118
|
+
end.compact
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# The shapes the JavaScript route clones — one per variant, because a
|
|
122
|
+
# variant is not only an attribute: `success` has an icon and `default`
|
|
123
|
+
# has none, and no attribute set afterwards can conjure an element that
|
|
124
|
+
# was never rendered. That is what the first version of this did, and the
|
|
125
|
+
# toasts it made came out blank-faced.
|
|
126
|
+
#
|
|
127
|
+
# Rendered in Ruby so the class strings live in one place. A controller
|
|
128
|
+
# that builds this markup from string literals is a second copy of every
|
|
129
|
+
# class in this file, kept in step by hand.
|
|
130
|
+
def template
|
|
131
|
+
safe_join(Toast::Component::VARIANTS.keys.map { |variant|
|
|
132
|
+
tag.template("data-shadcn--toaster-target": "template", "data-variant": variant) do
|
|
133
|
+
render(Toast::Component.new(title: "", description: "", variant:))
|
|
134
|
+
end
|
|
135
|
+
})
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Toaster
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
def default
|
|
7
|
+
render_with_template
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Rendered with the page, which is what a Rails flash does.
|
|
11
|
+
def from_the_server
|
|
12
|
+
render_with_template
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%# The JavaScript route: an event on the document, which the toaster hears
|
|
2
|
+
wherever it is on the page. `sonner`'s `toast()` is a function you import;
|
|
3
|
+
this is an event, because a Rails app has no bundler step to import into. %>
|
|
4
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
5
|
+
<% [["default", "Event created"],
|
|
6
|
+
["success", "Changes saved"],
|
|
7
|
+
["info", "A new version is available"],
|
|
8
|
+
["warning", "Your trial ends on Friday"],
|
|
9
|
+
["error", "Could not save changes"]].each do |variant, message| %>
|
|
10
|
+
<%= render(Shadcn::Button::Component.new(
|
|
11
|
+
variant: :outline,
|
|
12
|
+
onclick: "document.dispatchEvent(new CustomEvent('shadcn--toast', { detail: " \
|
|
13
|
+
"{ title: #{message.to_json}, description: 'Sunday, December 03, 2023 at 9:00 AM', " \
|
|
14
|
+
"variant: #{variant.to_json} } }))"
|
|
15
|
+
)) { variant.capitalize } %>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<%= render(Shadcn::Toaster::Component.new) %>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<%# The two routes sonner does not have.
|
|
2
|
+
|
|
3
|
+
Rendered with the page — which is what `Toaster::Component.new(flash:)` does
|
|
4
|
+
with a Rails flash, mapping `:notice` and `:alert` to the two variants
|
|
5
|
+
`redirect_to` produces without being asked — and appended afterwards, which
|
|
6
|
+
is what a `turbo_stream.append "shadcn-toasts"` does. Both arrive as ordinary
|
|
7
|
+
elements in the list, and the controller starts their clocks because it
|
|
8
|
+
watches the list rather than being told. %>
|
|
9
|
+
<%= render(Shadcn::Toaster::Component.new(position: "top-center", duration: 0)) do |t| %>
|
|
10
|
+
<% t.with_toast(title: "Rendered with the page", description: "A flash, or anything else the server knows.", variant: :success) %>
|
|
11
|
+
<% t.with_toast(title: "Appended afterwards", description: "A turbo_stream.append onto this same list.", variant: :info) do |toast| %>
|
|
12
|
+
<% toast.with_action(variant: :outline, size: :sm) { "Undo" } %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% t.with_toast(title: "Still working", description: "A loading toast keeps no clock at all.", variant: :loading) %>
|
|
15
|
+
<% end %>
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Toaster
|
|
5
|
+
module Toast
|
|
6
|
+
# One toast.
|
|
7
|
+
#
|
|
8
|
+
# A real component rather than a shape the JavaScript builds, because the
|
|
9
|
+
# most useful way to raise one in a Rails app is from the server: a flash
|
|
10
|
+
# rendered with the page, or a `turbo_stream.append` onto the list. The
|
|
11
|
+
# controller then only has to start its clock and take it away again.
|
|
12
|
+
class Component < ApplicationViewComponent
|
|
13
|
+
VARIANTS = {
|
|
14
|
+
default: nil,
|
|
15
|
+
success: "circle-check",
|
|
16
|
+
info: "info",
|
|
17
|
+
warning: "triangle-alert",
|
|
18
|
+
error: "octagon-x",
|
|
19
|
+
loading: "loader-circle"
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
default_tag :li
|
|
23
|
+
slot_name :toast
|
|
24
|
+
|
|
25
|
+
style do
|
|
26
|
+
base {
|
|
27
|
+
# sonner's own measurements, read from its stylesheet: 356px wide,
|
|
28
|
+
# 16px of padding, a 13px face and a soft shadow. The colours are
|
|
29
|
+
# the four custom properties `sonner.tsx` sets — popover, its
|
|
30
|
+
# foreground, the border and the radius — written here as the
|
|
31
|
+
# Tailwind tokens they point at.
|
|
32
|
+
# Absolutely placed: the toasts are a *stack*, one behind another,
|
|
33
|
+
# and the controller writes where each sits. A column would be
|
|
34
|
+
# simpler and would not be this component — the fan-out on hover is
|
|
35
|
+
# what a toaster is.
|
|
36
|
+
#
|
|
37
|
+
# The movement is a transition rather than a keyframe animation, as
|
|
38
|
+
# sonner's is: a toast moves whenever the one in front of it goes
|
|
39
|
+
# away, and an animation would have to be restarted each time where
|
|
40
|
+
# a transition simply follows.
|
|
41
|
+
"pointer-events-auto absolute right-0 flex w-full items-start gap-3 overflow-hidden " \
|
|
42
|
+
"rounded-lg border bg-popover p-4 text-[13px] text-popover-foreground " \
|
|
43
|
+
"shadow-[0_4px_12px_rgba(0,0,0,0.1)] transition-all duration-[400ms] " \
|
|
44
|
+
"data-[state=closed]:opacity-0"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
variants {
|
|
48
|
+
variant {
|
|
49
|
+
default { "" }
|
|
50
|
+
success { "[&_[data-slot=toast-icon]]:text-emerald-600 dark:[&_[data-slot=toast-icon]]:text-emerald-400" }
|
|
51
|
+
info { "[&_[data-slot=toast-icon]]:text-blue-600 dark:[&_[data-slot=toast-icon]]:text-blue-400" }
|
|
52
|
+
warning { "[&_[data-slot=toast-icon]]:text-amber-600 dark:[&_[data-slot=toast-icon]]:text-amber-400" }
|
|
53
|
+
error { "[&_[data-slot=toast-icon]]:text-destructive" }
|
|
54
|
+
loading { "[&_[data-slot=toast-icon]]:animate-spin" }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
defaults { { variant: :default } }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
renders_one :action, "Shadcn::Button::Component"
|
|
62
|
+
|
|
63
|
+
attr_reader :variant, :title, :description, :duration, :dismissible
|
|
64
|
+
|
|
65
|
+
def initialize(title: nil, description: nil, variant: :default, duration: nil,
|
|
66
|
+
dismissible: true, **attributes)
|
|
67
|
+
@variant = VARIANTS.key?(variant&.to_sym) ? variant.to_sym : :default
|
|
68
|
+
@title = title
|
|
69
|
+
@description = description
|
|
70
|
+
@duration = duration
|
|
71
|
+
@dismissible = dismissible
|
|
72
|
+
super(**attributes)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def style_variants = { variant: }
|
|
76
|
+
|
|
77
|
+
def element_attributes(**defaults)
|
|
78
|
+
# No `role` here, and sonner sets none either (index.tsx renders a bare
|
|
79
|
+
# `<li>`): a role on an `<li>` replaces its implicit `listitem`, and
|
|
80
|
+
# an `<ol>` whose children are not list items is a list axe fails and
|
|
81
|
+
# a screen reader miscounts. The announcement comes from the region's
|
|
82
|
+
# `aria-live`, which is where sonner puts it too (index.tsx:790).
|
|
83
|
+
super(**{
|
|
84
|
+
"data-variant" => variant,
|
|
85
|
+
"data-state" => "open",
|
|
86
|
+
"data-duration" => duration,
|
|
87
|
+
"data-shadcn--toaster-target" => "toast"
|
|
88
|
+
}.compact.merge(defaults))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def call
|
|
92
|
+
render_element(body: safe_join([ icon, body, dismiss ].compact))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def icon
|
|
98
|
+
name = VARIANTS[variant]
|
|
99
|
+
return unless name
|
|
100
|
+
|
|
101
|
+
render(Icon::Component.new(name, class: "size-4 shrink-0", "data-slot": "toast-icon"))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def body
|
|
105
|
+
tag.div(class: "flex min-w-0 flex-1 flex-col gap-1") do
|
|
106
|
+
safe_join([
|
|
107
|
+
(tag.div(title, class: "font-medium", "data-slot": "toast-title") if title),
|
|
108
|
+
(tag.div(description, class: "text-muted-foreground", "data-slot": "toast-description") if description),
|
|
109
|
+
(tag.div(action, class: "pt-1") if action?),
|
|
110
|
+
content
|
|
111
|
+
].compact)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def dismiss
|
|
116
|
+
return unless dismissible
|
|
117
|
+
|
|
118
|
+
# Always visible, where sonner reveals its own on hover. sonner can
|
|
119
|
+
# afford to hide it because a toast there is also dismissed by
|
|
120
|
+
# swiping it away; this port has no swipe, so the button is the only
|
|
121
|
+
# way out — and a control that appears on hover is not a control on a
|
|
122
|
+
# phone.
|
|
123
|
+
tag.button(
|
|
124
|
+
type: "button",
|
|
125
|
+
class: "absolute top-2 right-2 rounded-sm text-muted-foreground transition-colors " \
|
|
126
|
+
"hover:text-foreground",
|
|
127
|
+
"aria-label": shadcn_t("toaster.dismiss"),
|
|
128
|
+
"data-action": "click->shadcn--toaster#dismiss"
|
|
129
|
+
) { render(Icon::Component.new("x", class: "size-4")) }
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Toggle
|
|
5
|
+
# Port of registry/new-york-v4/ui/toggle.tsx
|
|
6
|
+
class Component < ApplicationViewComponent
|
|
7
|
+
default_tag :button
|
|
8
|
+
slot_name :toggle
|
|
9
|
+
|
|
10
|
+
style do
|
|
11
|
+
base {
|
|
12
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium " \
|
|
13
|
+
"whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted " \
|
|
14
|
+
"hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] " \
|
|
15
|
+
"focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 " \
|
|
16
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 " \
|
|
17
|
+
"data-[state=on]:bg-accent data-[state=on]:text-accent-foreground " \
|
|
18
|
+
"dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none " \
|
|
19
|
+
"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
variants {
|
|
23
|
+
variant {
|
|
24
|
+
default { "bg-transparent" }
|
|
25
|
+
outline {
|
|
26
|
+
"border border-input bg-transparent shadow-xs hover:bg-accent " \
|
|
27
|
+
"hover:text-accent-foreground"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
size {
|
|
32
|
+
default { "h-9 min-w-9 px-2" }
|
|
33
|
+
sm { "h-8 min-w-8 px-1.5" }
|
|
34
|
+
lg { "h-10 min-w-10 px-2.5" }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
defaults { { variant: :default, size: :default } }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Exposed the way shadcn exports `toggleVariants`, for ToggleGroupItem.
|
|
42
|
+
def self.variant_classes(variant: :default, size: :default, class: nil)
|
|
43
|
+
style_config.compile(
|
|
44
|
+
default_style_name.to_sym,
|
|
45
|
+
variant: variant&.to_sym || :default,
|
|
46
|
+
size: size&.to_sym || :default,
|
|
47
|
+
class: binding.local_variable_get(:class)
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attr_reader :variant, :size, :pressed, :disabled
|
|
52
|
+
|
|
53
|
+
def initialize(variant: :default, size: :default, pressed: false, disabled: false,
|
|
54
|
+
**attributes)
|
|
55
|
+
@variant = variant&.to_sym || :default
|
|
56
|
+
@size = size&.to_sym || :default
|
|
57
|
+
@pressed = pressed
|
|
58
|
+
@disabled = disabled
|
|
59
|
+
super(**attributes)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def style_variants
|
|
63
|
+
{ variant:, size: }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def element_attributes(**defaults)
|
|
67
|
+
super(**{
|
|
68
|
+
type: "button",
|
|
69
|
+
disabled: (true if disabled),
|
|
70
|
+
"aria-pressed" => pressed.to_s,
|
|
71
|
+
"data-state" => (pressed ? "on" : "off"),
|
|
72
|
+
"data-disabled" => (true if disabled),
|
|
73
|
+
"data-controller" => "shadcn--toggle",
|
|
74
|
+
"data-shadcn--toggle-pressed-value" => pressed,
|
|
75
|
+
"data-shadcn--toggle-disabled-value" => disabled,
|
|
76
|
+
"data-action" => "shadcn--toggle#toggle"
|
|
77
|
+
}.merge(defaults))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<div class="flex items-center gap-2">
|
|
2
|
+
<%= render(Shadcn::Toggle::Component.new) { "Default" } %>
|
|
3
|
+
<%= render(Shadcn::Toggle::Component.new(variant: :outline, pressed: true)) { "Outline, on" } %>
|
|
4
|
+
<%= render(Shadcn::Toggle::Component.new(size: :sm)) { "Small" } %>
|
|
5
|
+
<%= render(Shadcn::Toggle::Component.new(size: :lg, disabled: true)) { "Disabled" } %>
|
|
6
|
+
</div>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module ToggleGroup
|
|
5
|
+
# Port of registry/new-york-v4/ui/toggle-group.tsx
|
|
6
|
+
#
|
|
7
|
+
# React passes variant/size/spacing down through context; here the group
|
|
8
|
+
# exposes them to its items through the slot, which is the same idea.
|
|
9
|
+
class Component < ApplicationViewComponent
|
|
10
|
+
include Concerns::SubmitsValue
|
|
11
|
+
renders_many :items, ->(**kwargs, &block) {
|
|
12
|
+
Item::Component.new(variant:, size:, spacing:, **kwargs, &block)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
slot_name :"toggle-group"
|
|
16
|
+
|
|
17
|
+
style do
|
|
18
|
+
base {
|
|
19
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md " \
|
|
20
|
+
"data-[spacing=default]:data-[variant=outline]:shadow-xs"
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :type, :variant, :size, :spacing, :value, :name
|
|
25
|
+
|
|
26
|
+
def initialize(type: :single, variant: :default, size: :default, spacing: 0,
|
|
27
|
+
value: nil, name: nil, **attributes)
|
|
28
|
+
@type = type&.to_sym || :single
|
|
29
|
+
@variant = variant&.to_sym || :default
|
|
30
|
+
@size = size&.to_sym || :default
|
|
31
|
+
@spacing = spacing
|
|
32
|
+
@value = Array(value).compact.join(",")
|
|
33
|
+
@name = name
|
|
34
|
+
super(**attributes)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def element_attributes(**defaults)
|
|
38
|
+
super(**{
|
|
39
|
+
"data-variant" => variant,
|
|
40
|
+
"data-size" => size,
|
|
41
|
+
"data-spacing" => spacing,
|
|
42
|
+
style: [ attributes[:style], "--gap: #{spacing}" ].compact.join(";"),
|
|
43
|
+
"data-controller" => "shadcn--toggle-group",
|
|
44
|
+
"data-shadcn--toggle-group-type-value" => type,
|
|
45
|
+
"data-shadcn--toggle-group-value-value" => value
|
|
46
|
+
}.merge(defaults))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def call
|
|
50
|
+
render_element(body: safe_join([ items, content, hidden_input ].flatten.compact))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def hidden_input
|
|
56
|
+
return unless name
|
|
57
|
+
|
|
58
|
+
tag.input(**hidden_input_attributes("data-shadcn--toggle-group-target" => "input"))
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|