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,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
# Port of registry/new-york-v4/ui/combobox.tsx — the one file of the 61 that
|
|
6
|
+
# is written against **Base UI** rather than Radix.
|
|
7
|
+
#
|
|
8
|
+
# That is the whole story of this port, and it is visible in the class
|
|
9
|
+
# strings: `data-open:`, `data-closed:`, `--anchor-width`,
|
|
10
|
+
# `--available-height`. Every other family here emits `data-state="open"`
|
|
11
|
+
# and reads `--radix-*`. Reproducing upstream's classes therefore means
|
|
12
|
+
# emitting Base UI's conventions in this family and nowhere else — which is
|
|
13
|
+
# what this does, and what `popper.js` publishes the four unprefixed
|
|
14
|
+
# variables for. See
|
|
15
|
+
# [features/combobox.md](../../../../.claude/docs/features/combobox.md).
|
|
16
|
+
#
|
|
17
|
+
# The root itself renders nothing upstream (`const Combobox =
|
|
18
|
+
# ComboboxPrimitive.Root`, a context provider), so it is a `display:
|
|
19
|
+
# contents` wrapper here — the same shape every context-only root in this
|
|
20
|
+
# gem takes.
|
|
21
|
+
class Component < ApplicationViewComponent
|
|
22
|
+
default_tag :div
|
|
23
|
+
slot_name :combobox
|
|
24
|
+
|
|
25
|
+
renders_one :combobox_input, "Shadcn::Combobox::Input::Component"
|
|
26
|
+
renders_one :combobox_content, "Shadcn::Combobox::Content::Component"
|
|
27
|
+
|
|
28
|
+
# In multiple mode the chips box replaces the field — upstream renders
|
|
29
|
+
# `ComboboxChips` where the single-selection examples render
|
|
30
|
+
# `ComboboxInput`, and the field moves *inside* it as `ComboboxChipsInput`.
|
|
31
|
+
# A separate slot rather than a variant of `combobox_input`, because the
|
|
32
|
+
# two are different components with different parents.
|
|
33
|
+
renders_one :combobox_chips, "Shadcn::Combobox::Chips::Component"
|
|
34
|
+
|
|
35
|
+
attr_reader :name, :label, :multiple
|
|
36
|
+
|
|
37
|
+
# `name:` submits with the form, as every other control in this gem does
|
|
38
|
+
# through a hidden input; `value:` is what is chosen and `label:` what is
|
|
39
|
+
# shown for it.
|
|
40
|
+
#
|
|
41
|
+
# `multiple:` is Base UI's own prop — "Whether multiple items can be
|
|
42
|
+
# selected" — and, as there, `value:` then takes an array. It submits the
|
|
43
|
+
# way Rails expects a collection to: `name` gains `[]` and there is one
|
|
44
|
+
# hidden input per chosen value, so `params[:post][:tag_ids]` is an array
|
|
45
|
+
# with no parsing on the receiving end.
|
|
46
|
+
def initialize(name: nil, value: nil, label: nil, multiple: false, **attributes)
|
|
47
|
+
@name = name
|
|
48
|
+
@multiple = multiple
|
|
49
|
+
@values = Array.wrap(value).map(&:to_s).reject(&:blank?)
|
|
50
|
+
@label = label
|
|
51
|
+
super(**attributes)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The single chosen value. In multiple mode there is no such thing, and
|
|
55
|
+
# the controller reads `values` instead.
|
|
56
|
+
def value
|
|
57
|
+
multiple ? nil : @values.first
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def values
|
|
61
|
+
@values
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def element_attributes(**defaults)
|
|
65
|
+
super(**{
|
|
66
|
+
style: merged_style("display:contents"),
|
|
67
|
+
"data-controller" => "shadcn--combobox",
|
|
68
|
+
"data-shadcn--combobox-value-value" => value,
|
|
69
|
+
"data-shadcn--combobox-multiple-value" => (multiple.presence && "true"),
|
|
70
|
+
"data-shadcn--combobox-values-value" => (values.to_json if multiple),
|
|
71
|
+
# The controller writes the hidden inputs as chips come and go, and
|
|
72
|
+
# needs a name to write them under. Kept off the DOM when there is no
|
|
73
|
+
# name, like the inputs themselves.
|
|
74
|
+
"data-shadcn--combobox-name-value" => (field_name if multiple && name.present?)
|
|
75
|
+
}.compact.merge(defaults))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def call
|
|
79
|
+
render_element(body: safe_join([ combobox_input, combobox_chips, combobox_content, *hidden_inputs ].compact))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Rails reads `foo[]` as a collection and `foo` as a scalar, so the
|
|
85
|
+
# brackets are what make several hidden inputs arrive as an array rather
|
|
86
|
+
# than as the last one to be parsed. Added only when the caller has not
|
|
87
|
+
# written them already.
|
|
88
|
+
def field_name
|
|
89
|
+
return name if !multiple || name.to_s.end_with?("[]")
|
|
90
|
+
|
|
91
|
+
"#{name}[]"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def hidden_inputs
|
|
95
|
+
return [] if name.blank?
|
|
96
|
+
return [ hidden_input(value) ] unless multiple
|
|
97
|
+
|
|
98
|
+
# An empty one first, so clearing every chip still submits the
|
|
99
|
+
# parameter — otherwise the key vanishes from the params and Rails
|
|
100
|
+
# leaves the association untouched instead of emptying it. This is the
|
|
101
|
+
# same trick `collection_select ... include_hidden: true` plays.
|
|
102
|
+
[ hidden_input("", target: false), *values.map { |v| hidden_input(v) } ]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def hidden_input(field_value, target: true)
|
|
106
|
+
attrs = { type: "hidden", name: field_name, value: field_value, autocomplete: "off" }
|
|
107
|
+
attrs["data-shadcn--combobox-target"] = "input" if target
|
|
108
|
+
|
|
109
|
+
tag.input(**attrs)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
module Content
|
|
6
|
+
# The panel. Its class string is where Base UI's conventions are most
|
|
7
|
+
# visible — `data-open:`, `data-closed:`, `--anchor-width`,
|
|
8
|
+
# `--available-width`, `--transform-origin` — and reproducing it is why
|
|
9
|
+
# the controller asks `popper.js` to publish those four unprefixed names
|
|
10
|
+
# beside the `--radix-*` ones it already computes.
|
|
11
|
+
class Component < ApplicationViewComponent
|
|
12
|
+
default_tag :div
|
|
13
|
+
slot_name :"combobox-content"
|
|
14
|
+
|
|
15
|
+
style do
|
|
16
|
+
base {
|
|
17
|
+
# `isolate z-50` is upstream's *Positioner*, which this port has no
|
|
18
|
+
# element for — `floating.js` makes the wrapper — so they ride on
|
|
19
|
+
# the panel itself.
|
|
20
|
+
"isolate z-50 group/combobox-content relative max-h-96 w-(--anchor-width) max-w-(--available-width) " \
|
|
21
|
+
"min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden " \
|
|
22
|
+
"rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 " \
|
|
23
|
+
"data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 " \
|
|
24
|
+
"data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 " \
|
|
25
|
+
"data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 " \
|
|
26
|
+
"*:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 " \
|
|
27
|
+
"*:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 " \
|
|
28
|
+
"*:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 " \
|
|
29
|
+
"data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95"
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_reader :side, :align, :side_offset, :align_offset
|
|
34
|
+
|
|
35
|
+
def initialize(side: :bottom, align: :start, side_offset: 6, align_offset: 0, **attributes)
|
|
36
|
+
@side = side&.to_sym || :bottom
|
|
37
|
+
@align = align&.to_sym || :start
|
|
38
|
+
@side_offset = side_offset
|
|
39
|
+
@align_offset = align_offset
|
|
40
|
+
super(**attributes)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def element_attributes(**defaults)
|
|
44
|
+
super(**{
|
|
45
|
+
role: "listbox",
|
|
46
|
+
hidden: true,
|
|
47
|
+
"data-closed" => "",
|
|
48
|
+
"data-side" => side,
|
|
49
|
+
"data-shadcn--combobox-target" => "content",
|
|
50
|
+
"data-shadcn--combobox-side-value" => side,
|
|
51
|
+
"data-shadcn--combobox-align-value" => align,
|
|
52
|
+
"data-shadcn--combobox-side-offset-value" => side_offset,
|
|
53
|
+
"data-shadcn--combobox-align-offset-value" => align_offset
|
|
54
|
+
}.merge(defaults))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
module Empty
|
|
6
|
+
# Shown when the filter leaves nothing. Upstream shows it with CSS alone —
|
|
7
|
+
# `hidden … group-data-empty/combobox-content:flex` — so the controller
|
|
8
|
+
# only has to put `data-empty` on the content, and this element needs no
|
|
9
|
+
# state of its own.
|
|
10
|
+
class Component < ApplicationViewComponent
|
|
11
|
+
default_tag :div
|
|
12
|
+
slot_name :"combobox-empty"
|
|
13
|
+
|
|
14
|
+
style do
|
|
15
|
+
base {
|
|
16
|
+
"hidden w-full justify-center py-2 text-center text-sm text-muted-foreground " \
|
|
17
|
+
"group-data-empty/combobox-content:flex"
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
module Input
|
|
6
|
+
# The field, inside an InputGroup with the chevron and — when asked for —
|
|
7
|
+
# the clear button in its trailing addon. Upstream composes exactly these
|
|
8
|
+
# three, and all three are ported here already.
|
|
9
|
+
class Component < ApplicationViewComponent
|
|
10
|
+
# No `data-slot` of its own: upstream renders
|
|
11
|
+
# `<ComboboxPrimitive.Input render={<InputGroupInput/>}/>`, so the field
|
|
12
|
+
# a person types into carries `input-group-control`. Read from the
|
|
13
|
+
# rendered example — `combobox-input` was invented here and does not
|
|
14
|
+
# exist upstream.
|
|
15
|
+
default_tag :input
|
|
16
|
+
slot_name :"input-group-control"
|
|
17
|
+
|
|
18
|
+
attr_reader :show_trigger, :show_clear, :disabled, :placeholder, :value
|
|
19
|
+
|
|
20
|
+
def initialize(show_trigger: true, show_clear: false, disabled: false,
|
|
21
|
+
placeholder: nil, value: nil, **attributes)
|
|
22
|
+
@show_trigger = show_trigger
|
|
23
|
+
@show_clear = show_clear
|
|
24
|
+
@disabled = disabled
|
|
25
|
+
@placeholder = placeholder
|
|
26
|
+
@value = value
|
|
27
|
+
super(**attributes)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Base UI's combobox input is a `role="combobox"` with the list under
|
|
31
|
+
# `aria-controls`, and the panel's open state on `aria-expanded` — the
|
|
32
|
+
# same contract Radix's select trigger has, which is why the controller
|
|
33
|
+
# can wire both the same way.
|
|
34
|
+
def element_attributes(**defaults)
|
|
35
|
+
super(**{
|
|
36
|
+
type: "text",
|
|
37
|
+
value:,
|
|
38
|
+
placeholder:,
|
|
39
|
+
disabled: disabled.presence,
|
|
40
|
+
role: "combobox",
|
|
41
|
+
autocomplete: "off",
|
|
42
|
+
autocapitalize: "none",
|
|
43
|
+
autocorrect: "off",
|
|
44
|
+
spellcheck: "false",
|
|
45
|
+
"aria-autocomplete" => "list",
|
|
46
|
+
"aria-haspopup" => "listbox",
|
|
47
|
+
"aria-expanded" => "false",
|
|
48
|
+
"data-shadcn--combobox-target" => "search",
|
|
49
|
+
# `click`, not `focus`: taking an option puts the caret back in the
|
|
50
|
+
# field, and on `focus` that reopened the panel the choice had just
|
|
51
|
+
# closed. A pointer opens it by clicking, a keyboard by pressing
|
|
52
|
+
# Down — which is what both do anyway.
|
|
53
|
+
"data-action" => "input->shadcn--combobox#search click->shadcn--combobox#open " \
|
|
54
|
+
"keydown->shadcn--combobox#keydown"
|
|
55
|
+
}.compact.merge(defaults))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def call
|
|
59
|
+
render(Shadcn::InputGroup::Component.new(class: "w-auto")) do
|
|
60
|
+
safe_join([ field, addon ].compact)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
# The input itself is an `InputGroupInput`, which is upstream's `render`
|
|
67
|
+
# prop — a subclass here, the gem's mapping for `asChild`.
|
|
68
|
+
def field
|
|
69
|
+
render(Shadcn::InputGroup::Input::Component.new(**element_attributes))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def addon
|
|
73
|
+
return if !show_trigger && !show_clear
|
|
74
|
+
|
|
75
|
+
render(Shadcn::InputGroup::Addon::Component.new(align: :"inline-end")) do
|
|
76
|
+
safe_join([
|
|
77
|
+
(render(Trigger::Component.new(disabled:)) if show_trigger),
|
|
78
|
+
(render(Clear::Component.new(disabled:)) if show_clear)
|
|
79
|
+
].compact)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
module Item
|
|
6
|
+
# One option, with the tick that marks the chosen one. `data-highlighted`
|
|
7
|
+
# is the virtual focus — Base UI and Radix agree on that name, which is
|
|
8
|
+
# why the select's arrangement carries over unchanged.
|
|
9
|
+
class Component < ApplicationViewComponent
|
|
10
|
+
INDICATOR_CLASSES = "pointer-events-none absolute right-2 flex size-4 items-center justify-center"
|
|
11
|
+
|
|
12
|
+
default_tag :div
|
|
13
|
+
slot_name :"combobox-item"
|
|
14
|
+
|
|
15
|
+
style do
|
|
16
|
+
base {
|
|
17
|
+
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm " \
|
|
18
|
+
"outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground " \
|
|
19
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none " \
|
|
20
|
+
"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :value, :label, :selected, :disabled
|
|
25
|
+
|
|
26
|
+
def initialize(value: nil, label: nil, selected: false, disabled: false, **attributes)
|
|
27
|
+
@value = value
|
|
28
|
+
@label = label
|
|
29
|
+
@selected = selected
|
|
30
|
+
@disabled = disabled
|
|
31
|
+
super(**attributes)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def element_attributes(**defaults)
|
|
35
|
+
super(**{
|
|
36
|
+
id: item_id,
|
|
37
|
+
role: "option",
|
|
38
|
+
"aria-selected" => selected.to_s,
|
|
39
|
+
"data-disabled" => (disabled.presence && ""),
|
|
40
|
+
"data-value" => value,
|
|
41
|
+
"data-label" => label,
|
|
42
|
+
"data-shadcn--combobox-target" => "item",
|
|
43
|
+
"data-action" => "click->shadcn--combobox#choose pointermove->shadcn--combobox#hover"
|
|
44
|
+
}.compact.merge(defaults))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call
|
|
48
|
+
render_element(body: safe_join([ content, indicator ]))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def indicator
|
|
54
|
+
tag.span(
|
|
55
|
+
render(Icon::Component.new("check", class: "pointer-events-none size-4 pointer-coarse:size-5")),
|
|
56
|
+
class: INDICATOR_CLASSES,
|
|
57
|
+
"data-slot": "combobox-item-indicator",
|
|
58
|
+
hidden: !selected,
|
|
59
|
+
"data-shadcn--combobox-target": "indicator"
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def item_id
|
|
64
|
+
attributes[:id] || "shadcn-combobox-item-#{SecureRandom.hex(4)}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
module List
|
|
6
|
+
# The scrolling part of the panel. `data-empty` is Base UI's, and the
|
|
7
|
+
# empty state's own class reads it through
|
|
8
|
+
# `group-data-empty/combobox-content:flex` — so the attribute lands on the
|
|
9
|
+
# content and the padding rule here reads it too.
|
|
10
|
+
class Component < ApplicationViewComponent
|
|
11
|
+
default_tag :div
|
|
12
|
+
slot_name :"combobox-list"
|
|
13
|
+
|
|
14
|
+
style do
|
|
15
|
+
base {
|
|
16
|
+
"max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] " \
|
|
17
|
+
"scroll-py-1 overflow-y-auto p-1 data-empty:p-0"
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def element_attributes(**defaults)
|
|
22
|
+
super(**{ "data-shadcn--combobox-target" => "list" }.merge(defaults))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
class Preview < ApplicationViewComponentPreview
|
|
6
|
+
# Upstream's own first example: type to filter, arrows to walk, Enter to
|
|
7
|
+
# take.
|
|
8
|
+
def default
|
|
9
|
+
render_with_template
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Upstream's "Clear": the X that empties the field, which hides the
|
|
13
|
+
# chevron while it is there — upstream's own rule, in a class.
|
|
14
|
+
def with_clear
|
|
15
|
+
render_with_template
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Groups with labels, and the separator between them.
|
|
19
|
+
def grouped
|
|
20
|
+
render_with_template
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Multiple selection: the chips box in place of the field. Two values
|
|
24
|
+
# start chosen, so the server-rendered half — the ticks, the chips and the
|
|
25
|
+
# hidden inputs agreeing before anything is clicked — is covered too.
|
|
26
|
+
def multiple
|
|
27
|
+
render_with_template
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<%# Upstream's own example. The items are server-rendered and the controller
|
|
2
|
+
filters them by substring — Base UI's default, and the same call the
|
|
3
|
+
searchable select makes: a combobox completes a value from a list its caller
|
|
4
|
+
ordered, where the palette ranks answers. %>
|
|
5
|
+
<%
|
|
6
|
+
languages = [ %w[js JavaScript], %w[rb Ruby], %w[py Python], %w[go Go], %w[rs Rust], %w[ex Elixir] ]
|
|
7
|
+
%>
|
|
8
|
+
<div class="w-64">
|
|
9
|
+
<%= render(Shadcn::Combobox::Component.new(name: "project[language]")) do |combobox| %>
|
|
10
|
+
<% combobox.with_combobox_input(placeholder: "Search a language…") %>
|
|
11
|
+
<% combobox.with_combobox_content do %>
|
|
12
|
+
<%= render(Shadcn::Combobox::List::Component.new) do %>
|
|
13
|
+
<% languages.each do |value, label| %>
|
|
14
|
+
<%= render(Shadcn::Combobox::Item::Component.new(value:, label:)) { label } %>
|
|
15
|
+
<% end %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<%= render(Shadcn::Combobox::Empty::Component.new) { "No language found." } %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%# Groups, their labels and the rule between them — the three parts `part`
|
|
2
|
+
declares, because each is an element with a `data-slot` and fixed classes. %>
|
|
3
|
+
<div class="w-64">
|
|
4
|
+
<%= render(Shadcn::Combobox::Component.new(name: "project[stack]")) do |combobox| %>
|
|
5
|
+
<% combobox.with_combobox_input(placeholder: "Search…") %>
|
|
6
|
+
<% combobox.with_combobox_content do %>
|
|
7
|
+
<%= render(Shadcn::Combobox::List::Component.new) do %>
|
|
8
|
+
<%= render(Shadcn::Combobox::Group::Component.new) do %>
|
|
9
|
+
<%= render(Shadcn::Combobox::Label::Component.new) { "Languages" } %>
|
|
10
|
+
<% [ %w[rb Ruby], %w[js JavaScript] ].each do |value, label| %>
|
|
11
|
+
<%= render(Shadcn::Combobox::Item::Component.new(value:, label:)) { label } %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<%= render(Shadcn::Combobox::Separator::Component.new) %>
|
|
15
|
+
<%= render(Shadcn::Combobox::Group::Component.new) do %>
|
|
16
|
+
<%= render(Shadcn::Combobox::Label::Component.new) { "Databases" } %>
|
|
17
|
+
<% [ %w[pg PostgreSQL], %w[sqlite SQLite] ].each do |value, label| %>
|
|
18
|
+
<%= render(Shadcn::Combobox::Item::Component.new(value:, label:)) { label } %>
|
|
19
|
+
<% end %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
22
|
+
<%= render(Shadcn::Combobox::Empty::Component.new) { "Nothing found." } %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<%# Multiple selection: the chips box in place of the field, one chip per chosen
|
|
2
|
+
value, and `multiple: true` on the root.
|
|
3
|
+
|
|
4
|
+
Two of the values start chosen, so the preview covers the server-rendered
|
|
5
|
+
side as well as the controller's — the ticks in the list, the hidden inputs
|
|
6
|
+
and the chips all have to agree before a single click happens. %>
|
|
7
|
+
<%
|
|
8
|
+
languages = [ %w[js JavaScript], %w[rb Ruby], %w[py Python], %w[go Go], %w[rs Rust], %w[ex Elixir] ]
|
|
9
|
+
chosen = %w[rb go]
|
|
10
|
+
%>
|
|
11
|
+
<div class="w-80">
|
|
12
|
+
<%= render(Shadcn::Combobox::Component.new(name: "project[languages]", value: chosen, multiple: true)) do |combobox| %>
|
|
13
|
+
<% combobox.with_combobox_chips do %>
|
|
14
|
+
<% chosen.each do |value| %>
|
|
15
|
+
<%= render(Shadcn::Combobox::Chip::Component.new(value:)) { languages.to_h[value] } %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<%= render(Shadcn::Combobox::ChipInput::Component.new(placeholder: "Add a language…")) %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% combobox.with_combobox_content do %>
|
|
20
|
+
<%= render(Shadcn::Combobox::List::Component.new) do %>
|
|
21
|
+
<% languages.each do |value, label| %>
|
|
22
|
+
<%= render(Shadcn::Combobox::Item::Component.new(value:, label:, selected: chosen.include?(value))) { label } %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
<%= render(Shadcn::Combobox::Empty::Component.new) { "No language found." } %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<% end %>
|
|
28
|
+
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%# Upstream's "Clear". The chevron hides itself while the X is there —
|
|
2
|
+
`group-has-data-[slot=combobox-clear]/input-group:hidden`, upstream's own
|
|
3
|
+
class, which works here because the port renders the same `data-slot`. %>
|
|
4
|
+
<div class="w-64">
|
|
5
|
+
<%= render(Shadcn::Combobox::Component.new(name: "project[framework]", value: "rails", label: "Rails")) do |combobox| %>
|
|
6
|
+
<% combobox.with_combobox_input(placeholder: "Search a framework…", value: "Rails", show_clear: true) %>
|
|
7
|
+
<% combobox.with_combobox_content do %>
|
|
8
|
+
<%= render(Shadcn::Combobox::List::Component.new) do %>
|
|
9
|
+
<% [ %w[rails Rails], %w[hotwire Hotwire], %w[sinatra Sinatra], %w[hanami Hanami] ].each do |value, label| %>
|
|
10
|
+
<%= render(Shadcn::Combobox::Item::Component.new(value:, label:, selected: value == "rails")) { label } %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<%= render(Shadcn::Combobox::Empty::Component.new) { "Nothing found." } %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Combobox
|
|
5
|
+
module Trigger
|
|
6
|
+
# The chevron beside the field. Upstream renders it *through*
|
|
7
|
+
# `InputGroupButton` with `asChild`, so the button's own classes land on
|
|
8
|
+
# it — here that is a subclass, which is this gem's mapping for `asChild`.
|
|
9
|
+
class Component < Shadcn::InputGroup::Button::Component
|
|
10
|
+
ICON_CLASSES = "pointer-events-none size-4 text-muted-foreground"
|
|
11
|
+
|
|
12
|
+
# `input-group-button`, not `combobox-trigger`: upstream's wrapper sets
|
|
13
|
+
# the slot last and `asChild` merges the two into one element, so that
|
|
14
|
+
# is the name the rendered button carries. Confirmed on the page rather
|
|
15
|
+
# than reasoned about.
|
|
16
|
+
slot_name :"input-group-button"
|
|
17
|
+
|
|
18
|
+
def initialize(**attributes)
|
|
19
|
+
super(variant: :ghost, size: :"icon-xs", **attributes)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def element_attributes(**defaults)
|
|
23
|
+
super(**{
|
|
24
|
+
role: "combobox",
|
|
25
|
+
# Named, where upstream's is not. Its rendered trigger is a
|
|
26
|
+
# `role="combobox"` with a chevron in it and no text at all, and a
|
|
27
|
+
# role that takes a name and has none is what axe calls a
|
|
28
|
+
# `button-name` violation — the same correction Select, Checkbox and
|
|
29
|
+
# Switch needed here.
|
|
30
|
+
"aria-label" => shadcn_t("combobox.toggle"),
|
|
31
|
+
"aria-haspopup" => "dialog",
|
|
32
|
+
"aria-expanded" => "false",
|
|
33
|
+
tabindex: "0",
|
|
34
|
+
"data-shadcn--combobox-target" => "trigger",
|
|
35
|
+
"data-action" => "click->shadcn--combobox#toggle"
|
|
36
|
+
}.merge(defaults))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def css_classes(extra = nil)
|
|
40
|
+
super([
|
|
41
|
+
"[&_svg:not([class*='size-'])]:size-4",
|
|
42
|
+
"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent",
|
|
43
|
+
extra
|
|
44
|
+
].compact.join(" "))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call
|
|
48
|
+
render_element(body: render(Icon::Component.new("chevron-down", class: ICON_CLASSES,
|
|
49
|
+
"data-slot": "combobox-trigger-icon")))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# The parts of the Combobox family that are an element with a `data-slot` and
|
|
5
|
+
# fixed classes.
|
|
6
|
+
module Combobox
|
|
7
|
+
extend Parts
|
|
8
|
+
|
|
9
|
+
part :value, slot: "combobox-value", tag: :span
|
|
10
|
+
part :group, slot: "combobox-group"
|
|
11
|
+
part :label, slot: "combobox-label",
|
|
12
|
+
classes: "px-2 py-1.5 text-xs text-muted-foreground pointer-coarse:px-3 " \
|
|
13
|
+
"pointer-coarse:py-2 pointer-coarse:text-sm"
|
|
14
|
+
part :separator, slot: "combobox-separator", classes: "-mx-1 my-1 h-px bg-border"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
module Command
|
|
5
|
+
# Port of registry/new-york-v4/ui/command.tsx, whose behaviour is `cmdk` —
|
|
6
|
+
# 1,091 lines of TSX plus a 162-line fuzzy scorer, over four Radix packages,
|
|
7
|
+
# vendored at `vendor/cmdk/`.
|
|
8
|
+
#
|
|
9
|
+
# Every class is in the TSX, and unlike `chart.tsx` its selectors reach a
|
|
10
|
+
# DOM this port renders: `[cmdk-item]`, `[cmdk-group-heading]`,
|
|
11
|
+
# `[cmdk-input-wrapper]` are how shadcn spaces the palette out, so the parts
|
|
12
|
+
# carry those bare attributes and upstream's own rules apply. See
|
|
13
|
+
# [features/command.md](../../../../.claude/docs/features/command.md).
|
|
14
|
+
class Component < ApplicationViewComponent
|
|
15
|
+
default_tag :div
|
|
16
|
+
slot_name :command
|
|
17
|
+
|
|
18
|
+
style do
|
|
19
|
+
base { "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground" }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
renders_one :command_input, ->(**options) { Input::Component.new(ids:, **options) }
|
|
23
|
+
renders_one :command_list, ->(**options) { List::Component.new(ids:, **options) }
|
|
24
|
+
|
|
25
|
+
attr_reader :label
|
|
26
|
+
|
|
27
|
+
# `label:` names the listbox, as cmdk's own `label` prop does
|
|
28
|
+
# (`vendor/cmdk/index.tsx:350`): a list of options is a control, and a
|
|
29
|
+
# control with no name is one a screen reader reads as nothing.
|
|
30
|
+
def initialize(label: nil, **attributes)
|
|
31
|
+
@label = label
|
|
32
|
+
super(**attributes)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The three ids the ARIA wiring needs, decided once here and handed to the
|
|
36
|
+
# parts: an input points `aria-controls` at the list, and the list is
|
|
37
|
+
# named by a label that may not exist yet when the input renders.
|
|
38
|
+
def ids
|
|
39
|
+
@ids ||= { input: generated(:input), list: generated(:list), label: generated(:label) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def element_attributes(**defaults)
|
|
43
|
+
super(**{
|
|
44
|
+
"cmdk-root" => "",
|
|
45
|
+
"data-controller" => "shadcn--command"
|
|
46
|
+
}.merge(defaults))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def call
|
|
50
|
+
render_element(body: safe_join([ hidden_label, command_input, command_list, content ].compact))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# cmdk renders the label as a visually hidden element when it is given a
|
|
56
|
+
# string (index.tsx:591-596). Without it `aria-labelledby` on the input
|
|
57
|
+
# points at nothing, which is worse than no label at all.
|
|
58
|
+
def hidden_label
|
|
59
|
+
return unless label.present?
|
|
60
|
+
|
|
61
|
+
tag.label(label, id: ids[:label], for: ids[:input], class: "sr-only")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def generated(part) = "shadcn-command-#{part}-#{SecureRandom.hex(4)}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|