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,225 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Base class for every ported shadcn/ui component.
|
|
5
|
+
#
|
|
6
|
+
# It exists to make the React → ViewComponent mapping mechanical:
|
|
7
|
+
#
|
|
8
|
+
# React Ruby
|
|
9
|
+
# ------------------------------------ ---------------------------------------
|
|
10
|
+
# cva(base, {variants, defaultVariants}) style { base {} variants {} defaults {} }
|
|
11
|
+
# cn(...) TailwindMerge, wired as the style postprocessor
|
|
12
|
+
# data-slot="card-header" slot_name :"card-header"
|
|
13
|
+
# <div {...props} /> **attributes, splatted through Rails' tag builder
|
|
14
|
+
# asChild as: :a / as: :span
|
|
15
|
+
#
|
|
16
|
+
# A component that is a single element carrying `data-slot` + classes needs no
|
|
17
|
+
# template at all: the inherited `#call` renders it. Components whose markup is
|
|
18
|
+
# richer (an icon, a nested wrapper, an indicator) get a sidecar
|
|
19
|
+
# `component.html.erb`, which overrides `#call`.
|
|
20
|
+
class ApplicationViewComponent < ViewComponent::Base
|
|
21
|
+
include ViewComponentContrib::StyleVariants
|
|
22
|
+
|
|
23
|
+
# `cn()`: concatenate, then let tailwind-merge drop conflicting utilities so
|
|
24
|
+
# that classes passed in by the caller win — exactly like shadcn's helper.
|
|
25
|
+
style_config.postprocess_with { |classes| ShadcnViewComponent.cn(classes) }
|
|
26
|
+
|
|
27
|
+
# Elements that must not be given a block/closing tag.
|
|
28
|
+
VOID_TAGS = %i[area base br col embed hr img input link meta source track wbr].freeze
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
# The element rendered by default, i.e. the tag in the TSX source.
|
|
32
|
+
def default_tag(value = nil)
|
|
33
|
+
return @default_tag = value.to_sym if value
|
|
34
|
+
|
|
35
|
+
@default_tag ||= superclass.respond_to?(:default_tag) ? superclass.default_tag : :div
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The `data-slot` value shadcn stamps on this part. Inherited, so that
|
|
39
|
+
# specialisations such as PaginationPrevious keep PaginationLink's slot.
|
|
40
|
+
def slot_name(value = nil)
|
|
41
|
+
return @slot_name = value.to_s if value
|
|
42
|
+
return @slot_name if defined?(@slot_name)
|
|
43
|
+
|
|
44
|
+
@slot_name = superclass.respond_to?(:slot_name) ? superclass.slot_name : nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
attr_reader :attributes
|
|
49
|
+
|
|
50
|
+
# @param as [Symbol, String, nil] render a different element (shadcn's `asChild`)
|
|
51
|
+
# @param attributes [Hash] anything else is forwarded to the element, like `{...props}`
|
|
52
|
+
def initialize(as: nil, **attributes)
|
|
53
|
+
@as = as
|
|
54
|
+
@attributes = attributes
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def call
|
|
58
|
+
render_element(body: content)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def tag_name
|
|
62
|
+
(@as || self.class.default_tag).to_sym
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Renders the component's root element. `defaults` are the attributes the
|
|
66
|
+
# component itself contributes; see `#element_attributes` for how they rank
|
|
67
|
+
# against the caller's.
|
|
68
|
+
#
|
|
69
|
+
# The body comes either from a block (templates: `<%= render_element do %>`)
|
|
70
|
+
# or from `body:` — `#call` passes ViewComponent's already-captured `content`
|
|
71
|
+
# that way, because re-capturing it here would swallow it.
|
|
72
|
+
def render_element(body: nil, **defaults, &block)
|
|
73
|
+
attrs = element_attributes(**defaults)
|
|
74
|
+
return tag.public_send(tag_name, **attrs) if VOID_TAGS.include?(tag_name)
|
|
75
|
+
|
|
76
|
+
content_tag(tag_name, block ? capture(&block) : body, attrs)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The full attribute hash for the root element.
|
|
80
|
+
#
|
|
81
|
+
# Precedence, lowest to highest: the `data-slot` marker, then the component's
|
|
82
|
+
# own `defaults` (what subclasses pass up through `super`), then whatever the
|
|
83
|
+
# caller gave us. The caller winning is the point — it is what `{...props}`
|
|
84
|
+
# does in every shadcn component, where the spread comes last.
|
|
85
|
+
def element_attributes(**defaults)
|
|
86
|
+
base = { "data-slot" => self.class.slot_name }.compact
|
|
87
|
+
merged = merge_attributes(base, defaults, attributes)
|
|
88
|
+
merged["class"] = css_classes(merged.delete("class"))
|
|
89
|
+
merged.compact
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Compiles the cva-equivalent style set. Caller classes go last so
|
|
93
|
+
# tailwind-merge resolves conflicts in their favour. Parts that carry no
|
|
94
|
+
# classes of their own still have to pass the caller's through.
|
|
95
|
+
#
|
|
96
|
+
# Without caller classes the result depends only on the component class and
|
|
97
|
+
# its variants, and the style blocks are static once loaded — so it is
|
|
98
|
+
# computed once. That is not just a saving on the merge: it keeps the
|
|
99
|
+
# library's own class strings out of tailwind_merge's LRU, which is a fixed
|
|
100
|
+
# size shared with everything the caller varies.
|
|
101
|
+
def css_classes(extra = nil)
|
|
102
|
+
return compile_classes(extra) if extra.present?
|
|
103
|
+
|
|
104
|
+
ShadcnViewComponent.class_cache.fetch_or_store([ self.class.name, style_variants ]) do
|
|
105
|
+
compile_classes(nil)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Overridden by components that expose variants (button, badge, alert, …).
|
|
110
|
+
def style_variants
|
|
111
|
+
{}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# The user-visible strings the components render, looked up under
|
|
115
|
+
# `shadcn_view_component.*` and falling back to the bundled English — so the
|
|
116
|
+
# gem works untranslated and a host app can override any key.
|
|
117
|
+
#
|
|
118
|
+
# The fallback is the `locale: :en` default below, and it has to be there.
|
|
119
|
+
# This method used to be a bare `I18n.t` while the comment above it already
|
|
120
|
+
# claimed the English was a default; it was not, and the gem only looked
|
|
121
|
+
# untranslatable-proof because every app it had been tried in ran in
|
|
122
|
+
# English. A host running `it` got `I18n::MissingTranslationData` out of a
|
|
123
|
+
# searchable select — and with `config.i18n.raise_on_missing_translations`,
|
|
124
|
+
# which the Rails generators turn on in development and test, a raised page
|
|
125
|
+
# rather than a fallback string.
|
|
126
|
+
#
|
|
127
|
+
# A lambda, so the second lookup only happens when the first misses.
|
|
128
|
+
def shadcn_t(key, **interpolations)
|
|
129
|
+
full_key = "shadcn_view_component.#{key}"
|
|
130
|
+
|
|
131
|
+
I18n.t(full_key, **interpolations,
|
|
132
|
+
default: ->(*) { I18n.t(full_key, locale: :en, **interpolations) })
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Combines inline styles without dropping whatever the caller passed.
|
|
136
|
+
def merged_style(*declarations)
|
|
137
|
+
[ attributes[:style], *declarations ].compact.reject(&:empty?).join(";").presence
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Radix's context-only roots (Dialog, Popover, Select, …) render no element
|
|
141
|
+
# at all. We need somewhere to hang the Stimulus controller, so those roots
|
|
142
|
+
# emit a wrapper that is invisible to layout.
|
|
143
|
+
CONTENTS_STYLE = "display:contents"
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def compile_classes(extra)
|
|
148
|
+
compiled = style(**style_variants, class: extra)
|
|
149
|
+
(compiled || ShadcnViewComponent.cn(extra)).presence
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Shallow merge, with three exceptions that are combined rather than
|
|
153
|
+
# replaced: `data:`/`aria:` hashes, `class`, and `data-action` — Stimulus
|
|
154
|
+
# reads a space-separated list, so an action passed in by the caller adds to
|
|
155
|
+
# the component's own instead of silently replacing it.
|
|
156
|
+
#
|
|
157
|
+
# Keys are normalised to strings so `class:` and `"class"` cannot end up as
|
|
158
|
+
# two separate attributes.
|
|
159
|
+
def merge_attributes(*hashes)
|
|
160
|
+
hashes.compact.map { |hash| normalize_attributes(hash) }.reduce({}) do |acc, hash|
|
|
161
|
+
hash.each do |name, value|
|
|
162
|
+
case name
|
|
163
|
+
when "data", "aria"
|
|
164
|
+
acc[name] = (acc[name] || {}).merge(value || {})
|
|
165
|
+
when "class"
|
|
166
|
+
acc["class"] = [ acc["class"], value ].compact
|
|
167
|
+
when "data-action"
|
|
168
|
+
append_action(acc, value)
|
|
169
|
+
when "data-controller"
|
|
170
|
+
append_controller(acc, value)
|
|
171
|
+
else
|
|
172
|
+
acc[name] = value
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
acc
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Stringifies the keys and lifts `data: { action: … }` — the idiomatic Rails
|
|
180
|
+
# spelling — out to a top-level `data-action`, so both spellings reach the
|
|
181
|
+
# concatenation rule above. Without this the two forms land in different
|
|
182
|
+
# branches and the element ends up with the attribute twice, which is invalid
|
|
183
|
+
# HTML: the browser keeps the first and the other action never fires.
|
|
184
|
+
def normalize_attributes(hash)
|
|
185
|
+
hash.each_with_object({}) do |(key, value), normalized|
|
|
186
|
+
name = key.to_s
|
|
187
|
+
|
|
188
|
+
if name == "data" && value.is_a?(Hash)
|
|
189
|
+
data = value.transform_keys(&:to_s)
|
|
190
|
+
action = data.delete("action")
|
|
191
|
+
controller = data.delete("controller")
|
|
192
|
+
|
|
193
|
+
append_action(normalized, action) if action
|
|
194
|
+
append_controller(normalized, controller) if controller
|
|
195
|
+
normalized["data"] = data unless data.empty?
|
|
196
|
+
elsif name == "data-action"
|
|
197
|
+
append_action(normalized, value)
|
|
198
|
+
elsif name == "data-controller"
|
|
199
|
+
append_controller(normalized, value)
|
|
200
|
+
else
|
|
201
|
+
normalized[name] = value
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def append_action(hash, value)
|
|
207
|
+
hash["data-action"] = [ hash["data-action"], value ].compact.join(" ")
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# The same treatment `data-action` gets, and for the same reason: Stimulus
|
|
211
|
+
# reads a space-separated list, so a controller a caller attaches has to add
|
|
212
|
+
# to the component's own rather than replace it.
|
|
213
|
+
#
|
|
214
|
+
# This was missed when `data-action` was fixed, and the failure is quieter.
|
|
215
|
+
# A host writing `data: { controller: "my-thing" }` got two `data-controller`
|
|
216
|
+
# attributes — invalid HTML, and the browser keeps the first, which is the
|
|
217
|
+
# component's. So `my-thing` never connected, with nothing logged and the
|
|
218
|
+
# component still working perfectly. Found in a host app wiring a dependent
|
|
219
|
+
# select: choosing a client was supposed to reload its locations, and simply
|
|
220
|
+
# stopped doing so.
|
|
221
|
+
def append_controller(hash, value)
|
|
222
|
+
hash["data-controller"] = [ hash["data-controller"], value ].compact.join(" ")
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module AspectRatio
|
|
5
|
+
# Port of registry/new-york-v4/ui/aspect-ratio.tsx
|
|
6
|
+
#
|
|
7
|
+
# Radix wraps the content in a relatively positioned box whose bottom
|
|
8
|
+
# padding encodes the ratio, then absolutely positions the slot inside it.
|
|
9
|
+
class Component < ApplicationViewComponent
|
|
10
|
+
slot_name :"aspect-ratio"
|
|
11
|
+
|
|
12
|
+
attr_reader :ratio
|
|
13
|
+
|
|
14
|
+
def initialize(ratio: 1, **attributes)
|
|
15
|
+
@ratio = ratio
|
|
16
|
+
super(**attributes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call
|
|
20
|
+
wrapper_style = "position:relative;width:100%;padding-bottom:#{100.0 / ratio}%"
|
|
21
|
+
|
|
22
|
+
content_tag(:div, style: wrapper_style) do
|
|
23
|
+
render_element(body: content, style: inner_style)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def inner_style
|
|
30
|
+
[ attributes[:style], "position:absolute;top:0;right:0;bottom:0;left:0" ].compact.join(";")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module AspectRatio
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
def default
|
|
7
|
+
render_with_template
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Upstream's "Square" and "Portrait", beside the default's 16/9.
|
|
11
|
+
def ratios
|
|
12
|
+
render_with_template
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%# Upstream shows "Square" and "Portrait" as separate examples; here they sit
|
|
2
|
+
beside the 16/9 of the default so the argument's effect is the difference
|
|
3
|
+
between them rather than something to remember between pages. %>
|
|
4
|
+
<div class="flex items-start gap-4">
|
|
5
|
+
<% [[1, "1 / 1"], [3.0 / 4, "3 / 4"], [16.0 / 9, "16 / 9"]].each do |ratio, label| %>
|
|
6
|
+
<div class="w-[180px]">
|
|
7
|
+
<%= render(Shadcn::AspectRatio::Component.new(ratio:, class: "rounded-lg bg-muted")) do %>
|
|
8
|
+
<div class="flex size-full items-center justify-center text-sm text-foreground/70"><%= label %></div>
|
|
9
|
+
<% end %>
|
|
10
|
+
</div>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Attachment
|
|
5
|
+
module Action
|
|
6
|
+
# AttachmentAction — a Button with two different defaults, which is all
|
|
7
|
+
# upstream changes about it (attachment.tsx:144-159). Subclassed rather
|
|
8
|
+
# than declared with `part … from:`, because that restamps the slot and
|
|
9
|
+
# leaves the arguments alone, and these are arguments.
|
|
10
|
+
class Component < Button::Component
|
|
11
|
+
slot_name :"attachment-action"
|
|
12
|
+
|
|
13
|
+
def initialize(variant: :ghost, size: :"icon-xs", **attributes)
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Attachment
|
|
5
|
+
# Port of registry/new-york-v4/ui/attachment.tsx
|
|
6
|
+
#
|
|
7
|
+
# `state:` is not a cva variant upstream and is not one here: it is written
|
|
8
|
+
# as `data-state` and read by selectors on this element and on its children
|
|
9
|
+
# — `data-[state=idle]:border-dashed` here, and
|
|
10
|
+
# `group-data-[state=error]/attachment:…` on the media and the description
|
|
11
|
+
# (attachment.tsx:9 and :49). Only `size` and `orientation` choose classes.
|
|
12
|
+
class Component < ApplicationViewComponent
|
|
13
|
+
slot_name :attachment
|
|
14
|
+
|
|
15
|
+
style do
|
|
16
|
+
base {
|
|
17
|
+
"group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap " \
|
|
18
|
+
"rounded-xl border bg-card text-card-foreground transition-colors " \
|
|
19
|
+
"focus-within:ring-1 focus-within:ring-ring/50 has-[>a,>button]:hover:bg-muted/50 " \
|
|
20
|
+
"data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
variants {
|
|
24
|
+
size {
|
|
25
|
+
default {
|
|
26
|
+
"gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 " \
|
|
27
|
+
"has-data-[slot=attachment-content]:py-2 " \
|
|
28
|
+
"has-data-[slot=attachment-media]:p-2"
|
|
29
|
+
}
|
|
30
|
+
sm {
|
|
31
|
+
"gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 " \
|
|
32
|
+
"has-data-[slot=attachment-content]:py-1.5 " \
|
|
33
|
+
"has-data-[slot=attachment-media]:p-1.5"
|
|
34
|
+
}
|
|
35
|
+
xs {
|
|
36
|
+
"gap-1.5 rounded-lg text-xs has-data-[slot=attachment-content]:px-1.5 " \
|
|
37
|
+
"has-data-[slot=attachment-content]:py-1 " \
|
|
38
|
+
"has-data-[slot=attachment-media]:p-1"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
orientation {
|
|
43
|
+
horizontal { "min-w-40 items-center" }
|
|
44
|
+
vertical { "w-24 flex-col has-data-[slot=attachment-content]:w-30" }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Upstream's cva declares no `defaultVariants` — the defaults live on the
|
|
49
|
+
# function's own parameters instead (attachment.tsx:28-30). Same values,
|
|
50
|
+
# and here there is only one place to put them.
|
|
51
|
+
defaults { { size: :default, orientation: :horizontal } }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
attr_reader :state, :size, :orientation
|
|
55
|
+
|
|
56
|
+
def initialize(state: :done, size: :default, orientation: :horizontal, **attributes)
|
|
57
|
+
@state = state&.to_sym || :done
|
|
58
|
+
@size = size&.to_sym || :default
|
|
59
|
+
@orientation = orientation&.to_sym || :horizontal
|
|
60
|
+
super(**attributes)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def style_variants
|
|
64
|
+
{ size:, orientation: }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def element_attributes(**defaults)
|
|
68
|
+
super(**{
|
|
69
|
+
"data-state" => state,
|
|
70
|
+
"data-size" => size,
|
|
71
|
+
"data-orientation" => orientation
|
|
72
|
+
}.merge(defaults))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Attachment
|
|
5
|
+
module Media
|
|
6
|
+
# AttachmentMedia — the square thumbnail: an icon, or the file itself.
|
|
7
|
+
class Component < ApplicationViewComponent
|
|
8
|
+
slot_name :"attachment-media"
|
|
9
|
+
|
|
10
|
+
style do
|
|
11
|
+
base {
|
|
12
|
+
"relative flex aspect-square w-10 shrink-0 items-center justify-center " \
|
|
13
|
+
"overflow-hidden rounded-lg bg-muted text-foreground " \
|
|
14
|
+
"group-data-[orientation=vertical]/attachment:w-full " \
|
|
15
|
+
"group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 " \
|
|
16
|
+
"group-data-[size=xs]/attachment:rounded-md " \
|
|
17
|
+
"group-data-[state=error]/attachment:bg-destructive/10 " \
|
|
18
|
+
"group-data-[state=error]/attachment:text-destructive " \
|
|
19
|
+
"group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! " \
|
|
20
|
+
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 " \
|
|
21
|
+
"group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 " \
|
|
22
|
+
"group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
variants {
|
|
26
|
+
variant {
|
|
27
|
+
icon { "" }
|
|
28
|
+
image {
|
|
29
|
+
"opacity-60 group-data-[state=done]/attachment:opacity-100 " \
|
|
30
|
+
"group-data-[state=idle]/attachment:opacity-100 " \
|
|
31
|
+
"*:[img]:aspect-square *:[img]:w-full *:[img]:object-cover"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
defaults { { variant: :icon } }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
attr_reader :variant
|
|
40
|
+
|
|
41
|
+
def initialize(variant: :icon, **attributes)
|
|
42
|
+
@variant = variant&.to_sym || :icon
|
|
43
|
+
super(**attributes)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def style_variants
|
|
47
|
+
{ variant: }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def element_attributes(**defaults)
|
|
51
|
+
super(**{ "data-variant" => variant }.merge(defaults))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<%# Read from upstream's own demo: an attachment is a media square beside a
|
|
2
|
+
content column of title and description, and the two orientations rearrange
|
|
3
|
+
exactly that.
|
|
4
|
+
|
|
5
|
+
`state:` is the interesting argument. It is not a variant — it picks no
|
|
6
|
+
classes — it is written as `data-state` and read by selectors on this
|
|
7
|
+
element and on its children, which is how `error` reaches the description's
|
|
8
|
+
colour and how `uploading` and `processing` reach the title's `shimmer`.
|
|
9
|
+
That last one is shadcn's own CSS rather than Tailwind's; see the section at
|
|
10
|
+
the end of `shadcn.css`.
|
|
11
|
+
|
|
12
|
+
The media squares carry a file extension rather than a glyph. `Icon`'s
|
|
13
|
+
registry holds the eleven icons the *components* need and none of them is a
|
|
14
|
+
document, and the alternative — inlining lucide paths for a demo — is what
|
|
15
|
+
the sidebar preview does only because that demo is a copy of upstream's.
|
|
16
|
+
Recalling an SVG path from memory is the one thing not to do here. %>
|
|
17
|
+
<div class="flex w-[420px] flex-col gap-6">
|
|
18
|
+
<%= render(Shadcn::Attachment::Component.new) do %>
|
|
19
|
+
<%= render(Shadcn::Attachment::Media::Component.new) do %>
|
|
20
|
+
<span class="text-[10px] font-medium">PDF</span>
|
|
21
|
+
<% end %>
|
|
22
|
+
<%= render(Shadcn::Attachment::Content::Component.new) do %>
|
|
23
|
+
<%= render(Shadcn::Attachment::Title::Component.new) { "quarterly-report.pdf" } %>
|
|
24
|
+
<%= render(Shadcn::Attachment::Description::Component.new) { "PDF · 2.4 MB" } %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<%= render(Shadcn::Attachment::Actions::Component.new) do %>
|
|
27
|
+
<%= render(Shadcn::Attachment::Action::Component.new("aria-label": "Remove")) do %>
|
|
28
|
+
<%= render(Shadcn::Icon::Component.new("x")) %>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<%# `uploading` and `error` are the two states that change what you see. %>
|
|
34
|
+
<%= render(Shadcn::Attachment::Component.new(state: :uploading, size: :sm)) do %>
|
|
35
|
+
<%= render(Shadcn::Attachment::Media::Component.new) do %>
|
|
36
|
+
<span class="text-[10px] font-medium">TXT</span>
|
|
37
|
+
<% end %>
|
|
38
|
+
<%= render(Shadcn::Attachment::Content::Component.new) do %>
|
|
39
|
+
<%= render(Shadcn::Attachment::Title::Component.new) { "notes.txt" } %>
|
|
40
|
+
<%= render(Shadcn::Attachment::Description::Component.new) { "Uploading…" } %>
|
|
41
|
+
<% end %>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
44
|
+
<%= render(Shadcn::Attachment::Component.new(state: :error, size: :sm)) do %>
|
|
45
|
+
<%= render(Shadcn::Attachment::Media::Component.new) do %>
|
|
46
|
+
<span class="text-[10px] font-medium">ZIP</span>
|
|
47
|
+
<% end %>
|
|
48
|
+
<%= render(Shadcn::Attachment::Content::Component.new) do %>
|
|
49
|
+
<%= render(Shadcn::Attachment::Title::Component.new) { "archive.zip" } %>
|
|
50
|
+
<%= render(Shadcn::Attachment::Description::Component.new) { "Upload failed" } %>
|
|
51
|
+
<% end %>
|
|
52
|
+
<% end %>
|
|
53
|
+
|
|
54
|
+
<%# The vertical orientation is what the group scrolls, and the trigger is
|
|
55
|
+
what makes a whole card clickable — stretched over it at `z-10`, under the
|
|
56
|
+
actions' `z-20` so a close button still wins.
|
|
57
|
+
|
|
58
|
+
Six of them, so the row genuinely overflows and `scroll-fade-x` has
|
|
59
|
+
something to fade. That utility is driven by a scroll timeline, and it is
|
|
60
|
+
the one thing on this page a screenshot taken through the suite gets
|
|
61
|
+
wrong: the harness injects `animation-duration: 0s !important`, which
|
|
62
|
+
lands both 1ms interpolations on their end value and paints a fade on the
|
|
63
|
+
leading edge at rest. Measured with the duration handed back —
|
|
64
|
+
`--scroll-fade-s` is 0 at `scrollLeft: 0` and only the trailing edge
|
|
65
|
+
fades, which is correct. %>
|
|
66
|
+
<%= render(Shadcn::Attachment::Group::Component.new) do %>
|
|
67
|
+
<% [ "workspace.png", "desk-reference.jpg", "office-reference.jpg",
|
|
68
|
+
"whiteboard.png", "sketch-01.jpg", "sketch-02.jpg" ].each do |name| %>
|
|
69
|
+
<%= render(Shadcn::Attachment::Component.new(orientation: :vertical)) do %>
|
|
70
|
+
<%= render(Shadcn::Attachment::Media::Component.new(variant: :image)) do %>
|
|
71
|
+
<span class="text-[10px] font-medium">IMG</span>
|
|
72
|
+
<% end %>
|
|
73
|
+
<%= render(Shadcn::Attachment::Content::Component.new) do %>
|
|
74
|
+
<%= render(Shadcn::Attachment::Title::Component.new) { name } %>
|
|
75
|
+
<% end %>
|
|
76
|
+
<%= render(Shadcn::Attachment::Trigger::Component.new("aria-label": "Open #{name}")) %>
|
|
77
|
+
<% end %>
|
|
78
|
+
<% end %>
|
|
79
|
+
<% end %>
|
|
80
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Attachment
|
|
5
|
+
module Trigger
|
|
6
|
+
# AttachmentTrigger — a transparent button stretched over the whole
|
|
7
|
+
# attachment, so the card is clickable without nesting a button inside the
|
|
8
|
+
# actions that already sit on top of it (`attachment-actions` carries
|
|
9
|
+
# `z-20` against this one's `z-10`).
|
|
10
|
+
#
|
|
11
|
+
# Its own file for the `type`: upstream sets `type="button"` only when it
|
|
12
|
+
# is really a `<button>`, and leaves it off when `asChild` makes it
|
|
13
|
+
# something else (attachment.tsx:174). `as:` is this gem's `asChild`, so
|
|
14
|
+
# the same condition is `tag_name == :button`.
|
|
15
|
+
class Component < ApplicationViewComponent
|
|
16
|
+
default_tag :button
|
|
17
|
+
slot_name :"attachment-trigger"
|
|
18
|
+
|
|
19
|
+
style do
|
|
20
|
+
base { "absolute inset-0 z-10 outline-none" }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def element_attributes(**defaults)
|
|
24
|
+
return super(**defaults) if tag_name.to_sym != :button
|
|
25
|
+
|
|
26
|
+
super(**{ type: "button" }.merge(defaults))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# The parts of the Attachment family that are just an element with a
|
|
5
|
+
# `data-slot` and a fixed set of classes. Anything with variants, slots or
|
|
6
|
+
# markup of its own keeps its own `component.rb`.
|
|
7
|
+
#
|
|
8
|
+
# `attachment-group` and `attachment-title` reach for three utilities that are
|
|
9
|
+
# shadcn's own CSS rather than Tailwind's — `scroll-fade-x`, `scrollbar-none`
|
|
10
|
+
# and `shimmer`. They are defined in `shadcn.css`; see the comment there for
|
|
11
|
+
# where they were read from and what could not be checked.
|
|
12
|
+
module Attachment
|
|
13
|
+
extend Parts
|
|
14
|
+
|
|
15
|
+
part :group, slot: "attachment-group",
|
|
16
|
+
classes: "flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 " \
|
|
17
|
+
"scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 " \
|
|
18
|
+
"*:data-[slot=attachment]:flex-none " \
|
|
19
|
+
"*:data-[slot=attachment]:snap-start"
|
|
20
|
+
|
|
21
|
+
part :content, slot: "attachment-content",
|
|
22
|
+
classes: "max-w-full min-w-0 flex-1 leading-tight " \
|
|
23
|
+
"group-data-[orientation=vertical]/attachment:px-1"
|
|
24
|
+
|
|
25
|
+
part :title, slot: "attachment-title", tag: :span,
|
|
26
|
+
classes: "block max-w-full min-w-0 truncate font-medium " \
|
|
27
|
+
"group-data-[state=processing]/attachment:shimmer " \
|
|
28
|
+
"group-data-[state=uploading]/attachment:shimmer"
|
|
29
|
+
|
|
30
|
+
part :description, slot: "attachment-description", tag: :span,
|
|
31
|
+
classes: "mt-0.5 block min-w-0 truncate text-xs text-muted-foreground " \
|
|
32
|
+
"group-data-[state=error]/attachment:text-destructive/80 " \
|
|
33
|
+
"max-w-full"
|
|
34
|
+
|
|
35
|
+
part :actions, slot: "attachment-actions",
|
|
36
|
+
classes: "relative z-20 flex shrink-0 items-center " \
|
|
37
|
+
"group-data-[orientation=vertical]/attachment:absolute " \
|
|
38
|
+
"group-data-[orientation=vertical]/attachment:top-3 " \
|
|
39
|
+
"group-data-[orientation=vertical]/attachment:right-3 " \
|
|
40
|
+
"group-data-[orientation=vertical]/attachment:gap-1"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Avatar
|
|
5
|
+
# Port of registry/new-york-v4/ui/avatar.tsx
|
|
6
|
+
#
|
|
7
|
+
# Radix swaps the image for the fallback based on the image's loading state;
|
|
8
|
+
# the `shadcn--avatar` Stimulus controller does the same here.
|
|
9
|
+
class Component < ApplicationViewComponent
|
|
10
|
+
renders_one :image, "Shadcn::Avatar::Image::Component"
|
|
11
|
+
renders_one :fallback, "Shadcn::Avatar::Fallback::Component"
|
|
12
|
+
renders_one :badge, "Shadcn::Avatar::Badge::Component"
|
|
13
|
+
|
|
14
|
+
default_tag :span
|
|
15
|
+
slot_name :avatar
|
|
16
|
+
|
|
17
|
+
style do
|
|
18
|
+
base {
|
|
19
|
+
"group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full " \
|
|
20
|
+
"select-none data-[size=lg]:size-10 data-[size=sm]:size-6"
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :size
|
|
25
|
+
|
|
26
|
+
def initialize(size: :default, **attributes)
|
|
27
|
+
@size = size&.to_sym || :default
|
|
28
|
+
super(**attributes)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def element_attributes(**defaults)
|
|
32
|
+
super(**{
|
|
33
|
+
"data-size" => size,
|
|
34
|
+
"data-controller" => "shadcn--avatar"
|
|
35
|
+
}.merge(defaults))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def call
|
|
39
|
+
render_element(body: safe_join([ image, fallback, badge, content ].compact))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|