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,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module ScrollArea
|
|
5
|
+
module Viewport
|
|
6
|
+
# The box that actually scrolls. Native scrollbars are hidden on it —
|
|
7
|
+
# `shadcn.css` carries the rule Radix injects as a `<style>` tag
|
|
8
|
+
# (vendor/radix/ui/scroll-area.tsx:266-278), keyed on the slot rather than
|
|
9
|
+
# on a class, so a host cannot switch it off by accident and there is no
|
|
10
|
+
# token for `parity_spec` to miss.
|
|
11
|
+
#
|
|
12
|
+
# `overflow` is inline and per axis, as Radix writes it (`:222-223`):
|
|
13
|
+
# `scroll` on an axis with a scrollbar, `hidden` on one without. It has to
|
|
14
|
+
# be `scroll` rather than `auto` — the element must keep its scrollable
|
|
15
|
+
# geometry even when the custom bar is the only thing showing it.
|
|
16
|
+
class Component < ApplicationViewComponent
|
|
17
|
+
slot_name :"scroll-area-viewport"
|
|
18
|
+
|
|
19
|
+
style do
|
|
20
|
+
base {
|
|
21
|
+
"size-full rounded-[inherit] transition-[color,box-shadow] outline-none " \
|
|
22
|
+
"focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :horizontal, :vertical
|
|
27
|
+
|
|
28
|
+
def initialize(horizontal: false, vertical: true, **attributes)
|
|
29
|
+
@horizontal = horizontal
|
|
30
|
+
@vertical = vertical
|
|
31
|
+
super(**attributes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# `tabindex: 0`, which Radix does **not** set — measured, there is no
|
|
35
|
+
# `tabIndex` anywhere in the primitive. A deliberate divergence, for
|
|
36
|
+
# three reasons that line up: axe fails a scrollable region with no
|
|
37
|
+
# keyboard access; Chrome and Firefox make scrollers focusable
|
|
38
|
+
# themselves and Safari does not, so without this the component's
|
|
39
|
+
# keyboard behaviour depends on the browser; and shadcn's own classes
|
|
40
|
+
# here style `focus-visible`, which only means anything on an element
|
|
41
|
+
# that can be focused. The sibling primitive shadcn publishes does set
|
|
42
|
+
# it on its viewport (`vendor/shadcn-react/message-scroller/components.tsx:209`).
|
|
43
|
+
def element_attributes(**defaults)
|
|
44
|
+
super(**{
|
|
45
|
+
tabindex: 0,
|
|
46
|
+
style: merged_style(
|
|
47
|
+
"overflow-x: #{horizontal ? 'scroll' : 'hidden'}; " \
|
|
48
|
+
"overflow-y: #{vertical ? 'scroll' : 'hidden'};"
|
|
49
|
+
),
|
|
50
|
+
"data-shadcn--scroll-area-target" => "viewport"
|
|
51
|
+
}.merge(defaults))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# `display: table` with `min-width: 100%`, which is Radix's own
|
|
55
|
+
# (`:296-299`) and is the only reason content *size* can be measured at
|
|
56
|
+
# all: a block child matches the viewport's width and would report the
|
|
57
|
+
# same number however wide its content really is.
|
|
58
|
+
def call
|
|
59
|
+
render_element(
|
|
60
|
+
body: tag.div(
|
|
61
|
+
content,
|
|
62
|
+
style: "min-width: 100%; display: table;",
|
|
63
|
+
"data-shadcn--scroll-area-target": "content"
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# The one part of the ScrollArea family that is an element with a `data-slot`
|
|
5
|
+
# and fixed classes.
|
|
6
|
+
module ScrollArea
|
|
7
|
+
extend Parts
|
|
8
|
+
|
|
9
|
+
# Radix's `ScrollAreaThumb`. Its width, height and transform are inline and
|
|
10
|
+
# the controller's — the classes only make it a rounded bar.
|
|
11
|
+
part :thumb, slot: "scroll-area-thumb",
|
|
12
|
+
classes: "relative flex-1 rounded-full bg-border"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
# Port of registry/new-york-v4/ui/select.tsx
|
|
6
|
+
#
|
|
7
|
+
# A `role="combobox"` trigger driving a `role="listbox"` layer. When `name:`
|
|
8
|
+
# is given a hidden input mirrors the selection, the way Radix's BubbleSelect
|
|
9
|
+
# does, so the control submits with the form.
|
|
10
|
+
class Component < ApplicationViewComponent
|
|
11
|
+
include Concerns::SubmitsValue
|
|
12
|
+
# Lambda slots so `searchable` reaches the two parts that change shape
|
|
13
|
+
# because of it — the same way ToggleGroup feeds variant and size to its
|
|
14
|
+
# items, and for the same reason React would use context.
|
|
15
|
+
renders_one :trigger, ->(**kwargs, &block) {
|
|
16
|
+
Trigger::Component.new(searchable:, **kwargs, &block)
|
|
17
|
+
}
|
|
18
|
+
renders_one :select_content, ->(**kwargs, &block) {
|
|
19
|
+
Content::Component.new(searchable:, **kwargs, &block)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
slot_name :select
|
|
23
|
+
|
|
24
|
+
attr_reader :open, :value, :name, :placeholder, :side, :align, :side_offset, :searchable
|
|
25
|
+
|
|
26
|
+
def initialize(value: nil, name: nil, placeholder: nil, open: false,
|
|
27
|
+
side: :bottom, align: :center, side_offset: 4, searchable: false,
|
|
28
|
+
**attributes)
|
|
29
|
+
@searchable = searchable
|
|
30
|
+
@value = value.to_s
|
|
31
|
+
@name = name
|
|
32
|
+
@placeholder = placeholder.to_s
|
|
33
|
+
@open = open
|
|
34
|
+
@side = side&.to_sym || :bottom
|
|
35
|
+
@align = align&.to_sym || :center
|
|
36
|
+
@side_offset = side_offset
|
|
37
|
+
super(**attributes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def element_attributes(**defaults)
|
|
41
|
+
super(**{
|
|
42
|
+
style: merged_style(CONTENTS_STYLE),
|
|
43
|
+
"data-controller" => "shadcn--select",
|
|
44
|
+
"data-shadcn--select-open-value" => open,
|
|
45
|
+
"data-shadcn--select-value-value" => value,
|
|
46
|
+
"data-shadcn--select-placeholder-value" => placeholder,
|
|
47
|
+
"data-shadcn--select-side-value" => side,
|
|
48
|
+
"data-shadcn--select-align-value" => align,
|
|
49
|
+
"data-shadcn--select-side-offset-value" => side_offset,
|
|
50
|
+
"data-shadcn--select-searchable-value" => searchable
|
|
51
|
+
}.merge(defaults))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def call
|
|
55
|
+
render_element(body: safe_join([ trigger, select_content, content, hidden_input ].compact))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def hidden_input
|
|
61
|
+
return unless name
|
|
62
|
+
|
|
63
|
+
tag.input(**hidden_input_attributes("data-shadcn--select-target" => "input"))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
module Content
|
|
6
|
+
# SelectContent — the viewport and the two scroll buttons are part of the
|
|
7
|
+
# markup shadcn emits, so they are reproduced here.
|
|
8
|
+
class Component < ApplicationViewComponent
|
|
9
|
+
# `shrink-0` mirrors the `flexShrink: 0` Radix sets inline
|
|
10
|
+
# (vendor/radix/ui/select.tsx:1691): the buttons keep their height while
|
|
11
|
+
# the viewport takes the rest, which is what pins them to the edges.
|
|
12
|
+
SCROLL_BUTTON_CLASSES = "flex shrink-0 cursor-default items-center justify-center py-1"
|
|
13
|
+
|
|
14
|
+
renders_many :items, "Shadcn::Select::Item::Component"
|
|
15
|
+
|
|
16
|
+
slot_name :"select-content"
|
|
17
|
+
|
|
18
|
+
style do
|
|
19
|
+
base {
|
|
20
|
+
"relative z-50 flex flex-col max-h-(--radix-select-content-available-height) min-w-[8rem] " \
|
|
21
|
+
"origin-(--radix-select-content-transform-origin) overflow-x-hidden " \
|
|
22
|
+
"overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md " \
|
|
23
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 " \
|
|
24
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 " \
|
|
25
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 " \
|
|
26
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:animate-in " \
|
|
27
|
+
"data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
variants {
|
|
31
|
+
position {
|
|
32
|
+
popper {
|
|
33
|
+
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 " \
|
|
34
|
+
"data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1"
|
|
35
|
+
}
|
|
36
|
+
send(:"item-aligned") { "" }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
defaults { { position: :"item-aligned" } }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
attr_reader :position, :searchable
|
|
44
|
+
|
|
45
|
+
def initialize(position: :"item-aligned", searchable: false, **attributes)
|
|
46
|
+
@position = position&.to_sym || :"item-aligned"
|
|
47
|
+
@searchable = searchable
|
|
48
|
+
super(**attributes)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def style_variants
|
|
52
|
+
{ position: }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def element_attributes(**defaults)
|
|
56
|
+
super(**{
|
|
57
|
+
# A searchable popover holds a text field *and* a list, so it
|
|
58
|
+
# cannot be the list: a textbox is not an allowed child of a
|
|
59
|
+
# listbox. The role moves to Select::List and this becomes the
|
|
60
|
+
# dialog that contains both, which is what axe accepts and what
|
|
61
|
+
# shadcn's aria variant emits.
|
|
62
|
+
role: (searchable ? "dialog" : "listbox"),
|
|
63
|
+
"aria-label" => (shadcn_t("select.dialog_label") if searchable),
|
|
64
|
+
tabindex: "-1",
|
|
65
|
+
"data-state" => "closed",
|
|
66
|
+
hidden: true,
|
|
67
|
+
"data-shadcn--select-target" => "content",
|
|
68
|
+
# `scroll` does not bubble, but Stimulus binds the action to this
|
|
69
|
+
# element itself, so it fires. Which element actually scrolls
|
|
70
|
+
# differs by mode — see the controller's `scrollContainer`.
|
|
71
|
+
"data-action" => "keydown->shadcn--select#contentKeydown"
|
|
72
|
+
}.merge(defaults))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def call
|
|
76
|
+
render_element(body: safe_join([
|
|
77
|
+
scroll_button("select-scroll-up-button", "chevron-up", "scrollUpButton"),
|
|
78
|
+
viewport,
|
|
79
|
+
scroll_button("select-scroll-down-button", "chevron-down", "scrollDownButton")
|
|
80
|
+
]))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def viewport
|
|
86
|
+
tag.div(
|
|
87
|
+
searchable ? searchable_body : options,
|
|
88
|
+
class: viewport_classes,
|
|
89
|
+
"data-shadcn--select-target": "viewport",
|
|
90
|
+
# `scroll` does not bubble, but Stimulus binds to this element
|
|
91
|
+
# itself, so it fires. In a searchable panel the list scrolls
|
|
92
|
+
# instead and carries its own copy of this action.
|
|
93
|
+
"data-action": "scroll->shadcn--select#syncScrollButtons"
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The field is pinned and the list scrolls under it, rather than the
|
|
98
|
+
# whole popover scrolling and taking the field with it.
|
|
99
|
+
def searchable_body
|
|
100
|
+
safe_join([
|
|
101
|
+
render(Search::Component.new),
|
|
102
|
+
render(List::Component.new) { options },
|
|
103
|
+
render(Empty::Component.new)
|
|
104
|
+
])
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def options
|
|
108
|
+
safe_join([ items, content ].flatten.compact)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Radix puts the scrolling here, not on the content: `position: relative`
|
|
112
|
+
# so an item's offsetTop is measured against the viewport rather than
|
|
113
|
+
# the scroll-up button, `flex: 1` to take the space the buttons leave,
|
|
114
|
+
# and `overflow: hidden auto` (vendor/radix/ui/select.tsx:1240-1247).
|
|
115
|
+
# Without it the content scrolled and carried the buttons out of sight
|
|
116
|
+
# with the options — see decisions/04-bugs-fixed.md.
|
|
117
|
+
VIEWPORT_CLASSES = "relative flex-1 overflow-x-hidden overflow-y-auto"
|
|
118
|
+
|
|
119
|
+
def viewport_classes
|
|
120
|
+
# A searchable panel pins its search field, so the scrolling moves one
|
|
121
|
+
# level further in, onto the list.
|
|
122
|
+
return ShadcnViewComponent.cn("relative flex-1 flex flex-col overflow-hidden") if searchable
|
|
123
|
+
|
|
124
|
+
extra =
|
|
125
|
+
if position == :popper
|
|
126
|
+
"h-[var(--radix-select-trigger-height)] w-full " \
|
|
127
|
+
"min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
ShadcnViewComponent.cn(VIEWPORT_CLASSES, "p-1", extra)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Radix mounts these only while there is somewhere to scroll and
|
|
134
|
+
# unmounts them otherwise (vendor/radix/ui/select.tsx:1594, :1642).
|
|
135
|
+
# Rendered hidden here instead, which is this gem's convention for the
|
|
136
|
+
# same thing — see decisions/02-javascript.md, "Indicators are rendered
|
|
137
|
+
# hidden, not omitted" — so the controller only toggles a flag.
|
|
138
|
+
#
|
|
139
|
+
# `aria-hidden` because they are a pointer affordance: the keyboard
|
|
140
|
+
# scrolls by moving the highlight (vendor/radix/ui/select.tsx:1691).
|
|
141
|
+
def scroll_button(slot, icon, target)
|
|
142
|
+
tag.div(
|
|
143
|
+
render(Icon::Component.new(icon, class: "size-4")),
|
|
144
|
+
"data-slot": slot,
|
|
145
|
+
"aria-hidden": true,
|
|
146
|
+
hidden: true,
|
|
147
|
+
"data-shadcn--select-target": target,
|
|
148
|
+
"data-action": "pointerdown->shadcn--select#startAutoScroll " \
|
|
149
|
+
"pointermove->shadcn--select#startAutoScroll " \
|
|
150
|
+
"pointerleave->shadcn--select#stopAutoScroll",
|
|
151
|
+
class: SCROLL_BUTTON_CLASSES
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
module Empty
|
|
6
|
+
# Shown when the filter matches nothing.
|
|
7
|
+
#
|
|
8
|
+
# A *sibling* of the listbox rather than a child of it. shadcn's aria
|
|
9
|
+
# variant nests its empty state inside the list and gives it
|
|
10
|
+
# `role="option"` — an option that cannot be chosen — which is how React
|
|
11
|
+
# Aria's collection renders one. Outside the list the question does not
|
|
12
|
+
# arise, and a non-option inside a listbox is precisely the shape axe
|
|
13
|
+
# rejected while this component's structure was being chosen.
|
|
14
|
+
#
|
|
15
|
+
# The classes are this gem's own. Upstream's are a single
|
|
16
|
+
# `cn-select-empty-aria`, defined across the themed sheets in
|
|
17
|
+
# `registry/styles/`, which this gem does not ship.
|
|
18
|
+
class Component < ApplicationViewComponent
|
|
19
|
+
slot_name :"select-empty"
|
|
20
|
+
|
|
21
|
+
style do
|
|
22
|
+
base { "py-6 text-center text-sm text-muted-foreground" }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def element_attributes(**defaults)
|
|
26
|
+
super(**{
|
|
27
|
+
hidden: true,
|
|
28
|
+
"data-shadcn--select-target" => "empty"
|
|
29
|
+
}.merge(defaults))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def call
|
|
33
|
+
render_element(body: content.presence || shadcn_t("select.empty"))
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
module Group
|
|
6
|
+
# SelectGroup
|
|
7
|
+
class Component < ApplicationViewComponent
|
|
8
|
+
slot_name :"select-group"
|
|
9
|
+
|
|
10
|
+
def element_attributes(**defaults)
|
|
11
|
+
super(**{ role: "group" }.merge(defaults))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
module Item
|
|
6
|
+
# SelectItem
|
|
7
|
+
class Component < ApplicationViewComponent
|
|
8
|
+
INDICATOR_CLASSES = "absolute right-2 flex size-3.5 items-center justify-center"
|
|
9
|
+
|
|
10
|
+
slot_name :"select-item"
|
|
11
|
+
|
|
12
|
+
style do
|
|
13
|
+
base {
|
|
14
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 " \
|
|
15
|
+
"pl-2 text-sm outline-hidden select-none focus:bg-accent " \
|
|
16
|
+
"focus:text-accent-foreground data-[highlighted]:bg-accent " \
|
|
17
|
+
"data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none " \
|
|
18
|
+
"data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 " \
|
|
19
|
+
"[&_svg:not([class*='size-'])]:size-4 " \
|
|
20
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground " \
|
|
21
|
+
"*:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2"
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :value, :selected, :disabled
|
|
26
|
+
|
|
27
|
+
def initialize(value:, selected: false, disabled: false, **attributes)
|
|
28
|
+
@value = value.to_s
|
|
29
|
+
@selected = selected
|
|
30
|
+
@disabled = disabled
|
|
31
|
+
super(**attributes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def element_attributes(**defaults)
|
|
35
|
+
super(**{
|
|
36
|
+
role: "option",
|
|
37
|
+
tabindex: "-1",
|
|
38
|
+
"aria-selected" => selected.to_s,
|
|
39
|
+
"data-value" => value,
|
|
40
|
+
"data-state" => (selected ? "checked" : "unchecked"),
|
|
41
|
+
"data-disabled" => (true if disabled),
|
|
42
|
+
"data-shadcn--select-target" => "item",
|
|
43
|
+
"data-action" => "click->shadcn--select#select " \
|
|
44
|
+
"pointerenter->shadcn--select#pointerenter"
|
|
45
|
+
}.merge(defaults))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def call
|
|
49
|
+
render_element(body: safe_join([ indicator, item_text ]))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def indicator
|
|
55
|
+
tag.span(
|
|
56
|
+
tag.span(
|
|
57
|
+
render(Icon::Component.new("check", class: "size-4")),
|
|
58
|
+
hidden: (true unless selected)
|
|
59
|
+
),
|
|
60
|
+
"data-slot": "select-item-indicator",
|
|
61
|
+
class: INDICATOR_CLASSES
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def item_text
|
|
66
|
+
tag.span(content, "data-slot": "select-item-text")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
module List
|
|
6
|
+
# Where the options live once a search field shares the popover.
|
|
7
|
+
#
|
|
8
|
+
# In the plain select the listbox role sits on SelectContent itself. It
|
|
9
|
+
# cannot when a text field is in there too: a textbox is not an allowed
|
|
10
|
+
# child of a listbox, and axe reports `aria-required-children` — critical —
|
|
11
|
+
# for exactly that shape. shadcn's aria variant splits them the same way.
|
|
12
|
+
#
|
|
13
|
+
# Classes are that variant's verbatim, except `p-1` for its `p-0`: the
|
|
14
|
+
# padding this port keeps on the viewport moves here, because the viewport
|
|
15
|
+
# now holds the search field too.
|
|
16
|
+
class Component < ApplicationViewComponent
|
|
17
|
+
slot_name :"select-list"
|
|
18
|
+
|
|
19
|
+
style do
|
|
20
|
+
base {
|
|
21
|
+
"group/select-list max-h-[inherit] overflow-x-hidden overflow-y-auto p-1 outline-hidden"
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def element_attributes(**defaults)
|
|
26
|
+
# A named listbox, because an unnamed one is an
|
|
27
|
+
# `aria-input-field-name` violation — serious, and WCAG rather than
|
|
28
|
+
# best-practice. The dialog around it is named separately, so a
|
|
29
|
+
# screen reader announces the popover and then the list rather than
|
|
30
|
+
# the same words twice. shadcn's aria variant names both too.
|
|
31
|
+
super(**{
|
|
32
|
+
role: "listbox",
|
|
33
|
+
"data-action" => "scroll->shadcn--select#syncScrollButtons",
|
|
34
|
+
"aria-label" => shadcn_t("select.list_label"),
|
|
35
|
+
"data-shadcn--select-target" => "list"
|
|
36
|
+
}.merge(defaults))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
def default
|
|
7
|
+
render_with_template
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def searchable
|
|
11
|
+
render_with_template
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def scrollable
|
|
15
|
+
render_with_template
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Upstream's "Disabled" — as a disabled *item*, which is the half a listbox has to answer for — and "Invalid".
|
|
19
|
+
def states
|
|
20
|
+
render_with_template
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%= render(Shadcn::Select::Component.new(name: "fruit", placeholder: "Select a fruit")) do |s| %>
|
|
2
|
+
<%# `role="combobox"` forbids taking the name from its own content. %>
|
|
3
|
+
<% s.with_trigger(class: "w-[200px]", "aria-label": "Favourite fruit") do |tr| %>
|
|
4
|
+
<% tr.with_value(placeholder: "Select a fruit") %>
|
|
5
|
+
<% end %>
|
|
6
|
+
<%# Items go in the block rather than through the `item` slot, so the group
|
|
7
|
+
label keeps its place above them: slot content renders before block
|
|
8
|
+
content. %>
|
|
9
|
+
<% s.with_select_content do %>
|
|
10
|
+
<%= render(Shadcn::Select::Group::Component.new) do %>
|
|
11
|
+
<%= render(Shadcn::Select::Label::Component.new) { "Fruits" } %>
|
|
12
|
+
<% %w[Apple Banana Blueberry Grapes Pineapple].each do |fruit| %>
|
|
13
|
+
<%= render(Shadcn::Select::Item::Component.new(value: fruit.downcase)) { fruit } %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<%# Long enough to overflow the popover, which is what puts the two scroll
|
|
2
|
+
buttons to work: they stay hidden while there is nowhere to scroll and
|
|
3
|
+
appear per direction, as Radix's do. %>
|
|
4
|
+
<%= render(Shadcn::Select::Component.new(name: "timezone", placeholder: "Select a timezone")) do |s| %>
|
|
5
|
+
<% s.with_trigger(class: "w-[240px]", "aria-label": "Timezone") do |tr| %>
|
|
6
|
+
<% tr.with_value(placeholder: "Select a timezone") %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<% s.with_select_content do %>
|
|
9
|
+
<% [
|
|
10
|
+
"Auckland", "Bangkok", "Berlin", "Cairo", "Chicago", "Denver",
|
|
11
|
+
"Dublin", "Helsinki", "Istanbul", "Jakarta", "Karachi", "Lagos",
|
|
12
|
+
"Lima", "Lisbon", "London", "Madrid", "Manila", "Melbourne",
|
|
13
|
+
"Mexico City", "Moscow", "Nairobi", "New York", "Oslo", "Paris",
|
|
14
|
+
"Rome", "Seoul", "Singapore", "Sydney", "Tokyo", "Toronto"
|
|
15
|
+
].each do |city| %>
|
|
16
|
+
<%= render(Shadcn::Select::Item::Component.new(value: city.parameterize)) { city } %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<%# The trigger is a plain button here rather than a combobox, so it could take
|
|
2
|
+
its name from its own text — but naming it explicitly is what the FormBuilder
|
|
3
|
+
does for every select, and a preview is documentation. %>
|
|
4
|
+
<%= render(Shadcn::Select::Component.new(name: "fruit", placeholder: "Select a fruit", searchable: true)) do |s| %>
|
|
5
|
+
<% s.with_trigger(class: "w-[220px]", "aria-label": "Favourite fruit") do |tr| %>
|
|
6
|
+
<% tr.with_value(placeholder: "Select a fruit") %>
|
|
7
|
+
<% end %>
|
|
8
|
+
<%# Items go in the block rather than through the `item` slot, so anything
|
|
9
|
+
rendered beside them keeps its place — the same reason default.html.erb
|
|
10
|
+
does it. %>
|
|
11
|
+
<% s.with_select_content do %>
|
|
12
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "apple")) { "Apple" } %>
|
|
13
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "banana")) { "Banana" } %>
|
|
14
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "blueberry")) { "Blueberry" } %>
|
|
15
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "grapes")) { "Grapes" } %>
|
|
16
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "pineapple")) { "Pineapple" } %>
|
|
17
|
+
<% end %>
|
|
18
|
+
<% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%# Upstream's "Disabled" and "Invalid". The first is a *disabled item* as well
|
|
2
|
+
as a disabled trigger — the two are different things and only the item
|
|
3
|
+
version shows what a listbox does with one. %>
|
|
4
|
+
<div class="flex items-start gap-4">
|
|
5
|
+
<%= render(Shadcn::Select::Component.new(name: "plan", placeholder: "Choose a plan")) do |s| %>
|
|
6
|
+
<% s.with_trigger(class: "w-[200px]", "aria-label": "Plan") do |tr| %>
|
|
7
|
+
<% tr.with_value(placeholder: "Choose a plan") %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% s.with_select_content do %>
|
|
10
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "free")) { "Free" } %>
|
|
11
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "pro")) { "Pro" } %>
|
|
12
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "enterprise", disabled: true)) { "Enterprise — sales only" } %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
|
|
16
|
+
<%= render(Shadcn::Select::Component.new(name: "region", placeholder: "Pick a region")) do |s| %>
|
|
17
|
+
<% s.with_trigger(class: "w-[200px]", "aria-label": "Region", "aria-invalid": "true") do |tr| %>
|
|
18
|
+
<% tr.with_value(placeholder: "Pick a region") %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% s.with_select_content do %>
|
|
21
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "eu")) { "Europe" } %>
|
|
22
|
+
<%= render(Shadcn::Select::Item::Component.new(value: "us")) { "United States" } %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Select
|
|
5
|
+
module Search
|
|
6
|
+
# The filter field, composing the already-ported InputGroup the way
|
|
7
|
+
# shadcn's aria variant does — its popover renders `data-slot="input-group"`
|
|
8
|
+
# around the control, with the magnifier as an addon.
|
|
9
|
+
#
|
|
10
|
+
# Two things it does *not* copy from that variant, both with reasons in
|
|
11
|
+
# decisions/01-architecture.md: the control keeps `input-group-control`
|
|
12
|
+
# rather than being restamped `select-input`, because this port's group
|
|
13
|
+
# raises its focus ring off that slot name and the aria one does not use
|
|
14
|
+
# `has-[[data-slot=…]]` selectors at all; and the field carries an
|
|
15
|
+
# accessible name, where upstream's has none — axe calls an unnamed one a
|
|
16
|
+
# critical `label` violation.
|
|
17
|
+
class Component < ApplicationViewComponent
|
|
18
|
+
slot_name :"select-input-wrapper"
|
|
19
|
+
|
|
20
|
+
style do
|
|
21
|
+
base { "p-1 pb-0" }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :label
|
|
25
|
+
|
|
26
|
+
def initialize(label: nil, **attributes)
|
|
27
|
+
@label = label
|
|
28
|
+
super(**attributes)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def call
|
|
32
|
+
render_element(body: render(InputGroup::Component.new) { safe_join([ field, addon ]) })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def field
|
|
38
|
+
render(InputGroup::Input::Component.new(
|
|
39
|
+
"aria-label": label || shadcn_t("select.search_label"),
|
|
40
|
+
"aria-autocomplete": "list",
|
|
41
|
+
"data-shadcn--select-target": "search",
|
|
42
|
+
"data-action": "input->shadcn--select#search"
|
|
43
|
+
))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def addon
|
|
47
|
+
render(InputGroup::Addon::Component.new) do
|
|
48
|
+
render(Icon::Component.new("search", class: "size-4"))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|