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,66 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { uniqueId } from "shadcn/id"
|
|
3
|
+
import { FloatingLayer } from "shadcn/floating"
|
|
4
|
+
import { trapFocus, focusFirst } from "shadcn/focus"
|
|
5
|
+
|
|
6
|
+
// Radix's Popover: a `role="dialog"` layer anchored to its trigger.
|
|
7
|
+
export default class extends Controller {
|
|
8
|
+
static targets = [ "trigger", "content", "anchor" ]
|
|
9
|
+
static values = {
|
|
10
|
+
open: Boolean,
|
|
11
|
+
side: { type: String, default: "bottom" },
|
|
12
|
+
align: { type: String, default: "center" },
|
|
13
|
+
sideOffset: { type: Number, default: 4 },
|
|
14
|
+
alignOffset: { type: Number, default: 0 },
|
|
15
|
+
modal: Boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
connect() {
|
|
19
|
+
if (!this.hasContentTarget || !this.hasTriggerTarget) return
|
|
20
|
+
|
|
21
|
+
this.contentTarget.hidden = true
|
|
22
|
+
this.contentTarget.id ||= uniqueId("shadcn-popover")
|
|
23
|
+
|
|
24
|
+
// The id is generated here, so the server has nothing to point this at.
|
|
25
|
+
this.triggerTarget.setAttribute("aria-controls", this.contentTarget.id)
|
|
26
|
+
this.triggerTarget.dataset.state = "closed"
|
|
27
|
+
|
|
28
|
+
this.layer = new FloatingLayer({
|
|
29
|
+
trigger: this.hasAnchorTarget ? this.anchorTarget : this.triggerTarget,
|
|
30
|
+
content: this.contentTarget,
|
|
31
|
+
prefix: "popover",
|
|
32
|
+
side: this.sideValue,
|
|
33
|
+
align: this.alignValue,
|
|
34
|
+
sideOffset: this.sideOffsetValue,
|
|
35
|
+
alignOffset: this.alignOffsetValue,
|
|
36
|
+
onOpen: () => {
|
|
37
|
+
this.triggerTarget.setAttribute("aria-expanded", "true")
|
|
38
|
+
this.releaseFocus = trapFocus(this.contentTarget, this.triggerTarget)
|
|
39
|
+
focusFirst(this.contentTarget)
|
|
40
|
+
},
|
|
41
|
+
onClose: () => {
|
|
42
|
+
this.triggerTarget.setAttribute("aria-expanded", "false")
|
|
43
|
+
if (this.releaseFocus) this.releaseFocus()
|
|
44
|
+
this.releaseFocus = null
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
if (this.openValue) this.layer.show()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
disconnect() {
|
|
52
|
+
if (this.layer) this.layer.destroy()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
toggle() {
|
|
56
|
+
this.layer.toggle()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
open() {
|
|
60
|
+
this.layer.show()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
close() {
|
|
64
|
+
this.layer.hide()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
import { directionAwareKey, readDirection } from "shadcn/direction"
|
|
3
|
+
|
|
4
|
+
// Radix's RadioGroup: `role="radiogroup"` with roving tabindex over its items
|
|
5
|
+
// and arrow-key navigation that also selects, per the ARIA radio pattern.
|
|
6
|
+
export default class extends Controller {
|
|
7
|
+
static targets = [ "item", "input" ]
|
|
8
|
+
static values = { value: String, disabled: Boolean }
|
|
9
|
+
|
|
10
|
+
connect() {
|
|
11
|
+
this.render()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
select(event) {
|
|
15
|
+
const item = event.currentTarget
|
|
16
|
+
if (item.disabled || this.disabledValue) return
|
|
17
|
+
|
|
18
|
+
this.valueValue = item.dataset.value || ""
|
|
19
|
+
this.render()
|
|
20
|
+
item.focus({ preventScroll: true })
|
|
21
|
+
this.dispatch("change", { detail: { value: this.valueValue } })
|
|
22
|
+
|
|
23
|
+
if (this.hasInputTarget) {
|
|
24
|
+
this.inputTarget.dispatchEvent(new Event("change", { bubbles: true }))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
keydown(event) {
|
|
29
|
+
const keys = {
|
|
30
|
+
ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1
|
|
31
|
+
}
|
|
32
|
+
const step = keys[directionAwareKey(event.key, readDirection(this.element))]
|
|
33
|
+
if (!step) return
|
|
34
|
+
|
|
35
|
+
event.preventDefault()
|
|
36
|
+
|
|
37
|
+
const items = this.enabledItems
|
|
38
|
+
if (items.length === 0) return
|
|
39
|
+
|
|
40
|
+
const current = items.indexOf(event.currentTarget)
|
|
41
|
+
const next = items[(current + step + items.length) % items.length]
|
|
42
|
+
next.click()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get enabledItems() {
|
|
46
|
+
return this.itemTargets.filter((item) => !item.disabled)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
render() {
|
|
50
|
+
const items = this.itemTargets
|
|
51
|
+
const selected = items.find((item) => item.dataset.value === this.valueValue)
|
|
52
|
+
|
|
53
|
+
items.forEach((item) => {
|
|
54
|
+
const checked = item === selected
|
|
55
|
+
item.dataset.state = checked ? "checked" : "unchecked"
|
|
56
|
+
item.setAttribute("aria-checked", String(checked))
|
|
57
|
+
// Roving tabindex: only the selected item (or the first one) is tabbable.
|
|
58
|
+
const tabbable = selected ? checked : item === this.enabledItems[0]
|
|
59
|
+
item.setAttribute("tabindex", tabbable ? "0" : "-1")
|
|
60
|
+
|
|
61
|
+
const indicator = item.querySelector("[data-slot='radio-group-indicator']")
|
|
62
|
+
if (!indicator) return
|
|
63
|
+
indicator.hidden = !checked
|
|
64
|
+
indicator.dataset.state = checked ? "checked" : "unchecked"
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
if (this.hasInputTarget) this.inputTarget.value = this.valueValue
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Panels are shares of a flex container, so the layout is the browser's: this
|
|
4
|
+
// only moves two numbers when a handle is dragged, and the browser lays the
|
|
5
|
+
// group out again.
|
|
6
|
+
//
|
|
7
|
+
// The reference is `react-resizable-panels`, 2,252 lines — most of which is
|
|
8
|
+
// state, persistence and conditional panels. The arithmetic underneath is a
|
|
9
|
+
// delta in pixels turned into a percentage of the group.
|
|
10
|
+
export default class extends Controller {
|
|
11
|
+
static targets = [ "panel", "handle" ]
|
|
12
|
+
static values = { orientation: { type: String, default: "horizontal" } }
|
|
13
|
+
|
|
14
|
+
// The package's own step for the arrow keys, and its own answer for Home and
|
|
15
|
+
// End: push the separator all the way (`react-resizable-panels.js`, the
|
|
16
|
+
// keydown switch).
|
|
17
|
+
static STEP = 5
|
|
18
|
+
static ALL = 100
|
|
19
|
+
|
|
20
|
+
connect() {
|
|
21
|
+
this.publish()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
press(event) {
|
|
25
|
+
const handle = event.currentTarget
|
|
26
|
+
if (handle.getAttribute("aria-disabled") === "true") return
|
|
27
|
+
|
|
28
|
+
event.preventDefault()
|
|
29
|
+
handle.setPointerCapture(event.pointerId)
|
|
30
|
+
handle.dataset.separator = "active"
|
|
31
|
+
|
|
32
|
+
this.drag = {
|
|
33
|
+
handle,
|
|
34
|
+
from: this.positionOf(event),
|
|
35
|
+
sizes: this.neighbours(handle).map((panel) => this.shareOf(panel))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.onMove = (moved) => this.move(moved)
|
|
39
|
+
this.onUp = () => this.release()
|
|
40
|
+
handle.addEventListener("pointermove", this.onMove)
|
|
41
|
+
handle.addEventListener("pointerup", this.onUp)
|
|
42
|
+
handle.addEventListener("pointercancel", this.onUp)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
move(event) {
|
|
46
|
+
if (!this.drag) return
|
|
47
|
+
|
|
48
|
+
// In percent, because that is what a share is. The group's own size is the
|
|
49
|
+
// whole, so a drag of 40px in a 400px group is ten points.
|
|
50
|
+
const travelled = (this.positionOf(event) - this.drag.from) / this.extent * 100
|
|
51
|
+
|
|
52
|
+
this.resize(this.drag.handle, travelled, this.drag.sizes)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
release() {
|
|
56
|
+
if (!this.drag) return
|
|
57
|
+
|
|
58
|
+
const { handle } = this.drag
|
|
59
|
+
handle.removeEventListener("pointermove", this.onMove)
|
|
60
|
+
handle.removeEventListener("pointerup", this.onUp)
|
|
61
|
+
handle.removeEventListener("pointercancel", this.onUp)
|
|
62
|
+
handle.dataset.separator = "inactive"
|
|
63
|
+
this.drag = null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The separator pattern: the arrows that lie along the group's own axis move
|
|
67
|
+
// it, and the ones across it do nothing — which is why the orientation is a
|
|
68
|
+
// value rather than something read from the class.
|
|
69
|
+
keydown(event) {
|
|
70
|
+
const handle = event.currentTarget
|
|
71
|
+
if (handle.getAttribute("aria-disabled") === "true") return
|
|
72
|
+
|
|
73
|
+
const horizontal = this.orientationValue === "horizontal"
|
|
74
|
+
const steps = {
|
|
75
|
+
ArrowLeft: horizontal ? -this.constructor.STEP : 0,
|
|
76
|
+
ArrowRight: horizontal ? this.constructor.STEP : 0,
|
|
77
|
+
ArrowUp: horizontal ? 0 : -this.constructor.STEP,
|
|
78
|
+
ArrowDown: horizontal ? 0 : this.constructor.STEP,
|
|
79
|
+
Home: -this.constructor.ALL,
|
|
80
|
+
End: this.constructor.ALL
|
|
81
|
+
}
|
|
82
|
+
const step = steps[event.key]
|
|
83
|
+
if (!step) return
|
|
84
|
+
|
|
85
|
+
event.preventDefault()
|
|
86
|
+
this.resize(handle, step, this.neighbours(handle).map((panel) => this.shareOf(panel)))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Two panels and one number: what one gives the other takes, so the group
|
|
90
|
+
// always adds up to what it added up to before.
|
|
91
|
+
resize(handle, delta, [ before, after ]) {
|
|
92
|
+
const [ first, second ] = this.neighbours(handle)
|
|
93
|
+
if (!first || !second) return
|
|
94
|
+
|
|
95
|
+
const total = before + after
|
|
96
|
+
const wanted = before + delta
|
|
97
|
+
const size = Math.min(
|
|
98
|
+
Math.max(wanted, this.limits(first).min, total - this.limits(second).max),
|
|
99
|
+
this.limits(first).max,
|
|
100
|
+
total - this.limits(second).min
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
first.style.flexGrow = String(this.round(size))
|
|
104
|
+
second.style.flexGrow = String(this.round(total - size))
|
|
105
|
+
this.publish()
|
|
106
|
+
this.dispatch("resize", { detail: { sizes: this.panelTargets.map((panel) => this.shareOf(panel)) } })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// `aria-valuenow` is the share of the panel a separator controls, and it is
|
|
110
|
+
// written from here rather than rendered: the server would have to know how
|
|
111
|
+
// many panels the caller put in the block, and it does not.
|
|
112
|
+
publish() {
|
|
113
|
+
for (const handle of this.handleTargets) {
|
|
114
|
+
const [ first ] = this.neighbours(handle)
|
|
115
|
+
if (!first) continue
|
|
116
|
+
|
|
117
|
+
handle.setAttribute("aria-controls", first.id)
|
|
118
|
+
handle.setAttribute("aria-valuenow", String(Math.round(this.shareOf(first))))
|
|
119
|
+
handle.setAttribute("aria-valuemin", String(this.limits(first).min))
|
|
120
|
+
handle.setAttribute("aria-valuemax", String(this.limits(first).max))
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// The panels either side of a handle, in DOM order — which is what "either
|
|
125
|
+
// side" means in a flex row that may also be a column.
|
|
126
|
+
neighbours(handle) {
|
|
127
|
+
const parts = [ ...this.element.children ]
|
|
128
|
+
const at = parts.indexOf(handle)
|
|
129
|
+
|
|
130
|
+
return [ parts[at - 1], parts[at + 1] ].map((part) => (part?.matches("[data-shadcn--resizable-target=panel]") ? part : null))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
limits(panel) {
|
|
134
|
+
return {
|
|
135
|
+
min: Number(panel.dataset.minSize ?? 0),
|
|
136
|
+
max: Number(panel.dataset.maxSize ?? 100)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// A share read from the layout rather than from the style, so a group whose
|
|
141
|
+
// panels were never given sizes still has numbers to move: `flex-grow: 1`
|
|
142
|
+
// each renders as equal shares, and this is what they are.
|
|
143
|
+
shareOf(panel) {
|
|
144
|
+
const sizes = this.panelTargets.map((each) => this.lengthOf(each))
|
|
145
|
+
const total = sizes.reduce((sum, size) => sum + size, 0)
|
|
146
|
+
|
|
147
|
+
return total ? this.lengthOf(panel) / total * 100 : 0
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
lengthOf(element) {
|
|
151
|
+
const box = element.getBoundingClientRect()
|
|
152
|
+
|
|
153
|
+
return this.orientationValue === "horizontal" ? box.width : box.height
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get extent() {
|
|
157
|
+
return this.lengthOf(this.element) || 1
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
positionOf(event) {
|
|
161
|
+
return this.orientationValue === "horizontal" ? event.clientX : event.clientY
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
round(value) {
|
|
165
|
+
return Math.round(value * 100) / 100
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
// Radix's ScrollArea (vendor/radix/ui/scroll-area.tsx): native scrollbars
|
|
4
|
+
// hidden, a drawn one in their place.
|
|
5
|
+
//
|
|
6
|
+
// The layout is CSS. This computes two numbers per axis — how long the thumb is
|
|
7
|
+
// and how far along it sits — and publishes the first as the custom property
|
|
8
|
+
// the thumb's width or height reads. That split is Radix's, measured on the
|
|
9
|
+
// live demo, and it is why 1,189 lines of TSX come down to this: the scrollbar
|
|
10
|
+
// is positioned by a stylesheet, not by script.
|
|
11
|
+
//
|
|
12
|
+
// Two of Radix's four `type` strategies are here: `hover`, its default, and
|
|
13
|
+
// `always`. `scroll` and `auto` are not — see features/scroll-area.md.
|
|
14
|
+
|
|
15
|
+
// Radix's own, and the comment is Radix's too: "minimum of 18 matches macOS
|
|
16
|
+
// minimum" (scroll-area.tsx:1065).
|
|
17
|
+
const MIN_THUMB = 18
|
|
18
|
+
|
|
19
|
+
export default class extends Controller {
|
|
20
|
+
static targets = [ "viewport", "content", "scrollbar", "corner" ]
|
|
21
|
+
static values = {
|
|
22
|
+
type: { type: String, default: "hover" },
|
|
23
|
+
hideDelay: { type: Number, default: 600 }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
connect() {
|
|
27
|
+
if (!this.hasViewportTarget) return
|
|
28
|
+
|
|
29
|
+
this.dragging = null
|
|
30
|
+
this.hideTimer = null
|
|
31
|
+
|
|
32
|
+
this.onScroll = () => this.render()
|
|
33
|
+
this.viewportTarget.addEventListener("scroll", this.onScroll, { passive: true })
|
|
34
|
+
|
|
35
|
+
// Content and viewport both change the numbers, and neither change is an
|
|
36
|
+
// event: a row growing is not a scroll.
|
|
37
|
+
this.resizes = new ResizeObserver(() => this.render())
|
|
38
|
+
this.resizes.observe(this.viewportTarget)
|
|
39
|
+
if (this.hasContentTarget) this.resizes.observe(this.contentTarget)
|
|
40
|
+
|
|
41
|
+
this.render()
|
|
42
|
+
if (this.typeValue === "always") this.show()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
disconnect() {
|
|
46
|
+
this.viewportTarget?.removeEventListener("scroll", this.onScroll)
|
|
47
|
+
this.resizes?.disconnect()
|
|
48
|
+
if (this.hideTimer) clearTimeout(this.hideTimer)
|
|
49
|
+
this.endDrag()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ------------------------------------------------------------ visibility
|
|
53
|
+
|
|
54
|
+
pointerEnter() {
|
|
55
|
+
if (this.typeValue !== "hover") return
|
|
56
|
+
|
|
57
|
+
if (this.hideTimer) clearTimeout(this.hideTimer)
|
|
58
|
+
this.show()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pointerLeave() {
|
|
62
|
+
if (this.typeValue !== "hover" || this.dragging) return
|
|
63
|
+
|
|
64
|
+
this.hideTimer = setTimeout(() => this.hide(), this.hideDelayValue)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
show() {
|
|
68
|
+
for (const bar of this.scrollbarTargets) {
|
|
69
|
+
// A bar for an axis with nothing to scroll stays hidden however the
|
|
70
|
+
// pointer moves — showing it would draw a full-length thumb that cannot
|
|
71
|
+
// go anywhere.
|
|
72
|
+
bar.dataset.state = this.overflows(this.axisOf(bar)) ? "visible" : "hidden"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
hide() {
|
|
77
|
+
for (const bar of this.scrollbarTargets) bar.dataset.state = "hidden"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// --------------------------------------------------------------- geometry
|
|
81
|
+
|
|
82
|
+
render() {
|
|
83
|
+
for (const bar of this.scrollbarTargets) this.renderBar(bar)
|
|
84
|
+
|
|
85
|
+
if (this.typeValue === "always") this.show()
|
|
86
|
+
else if (this.scrollbarTargets.some((bar) => bar.dataset.state === "visible")) this.show()
|
|
87
|
+
|
|
88
|
+
this.renderCorner()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
renderBar(bar) {
|
|
92
|
+
const axis = this.axisOf(bar)
|
|
93
|
+
const sizes = this.sizes(axis, bar)
|
|
94
|
+
const thumb = bar.querySelector("[data-slot=scroll-area-thumb]")
|
|
95
|
+
|
|
96
|
+
if (!thumb) return
|
|
97
|
+
|
|
98
|
+
const thumbSize = this.thumbSize(sizes)
|
|
99
|
+
const offset = this.thumbOffset(sizes, thumbSize)
|
|
100
|
+
|
|
101
|
+
if (axis === "horizontal") {
|
|
102
|
+
bar.style.setProperty("--radix-scroll-area-thumb-width", `${thumbSize}px`)
|
|
103
|
+
thumb.style.width = "var(--radix-scroll-area-thumb-width)"
|
|
104
|
+
thumb.style.transform = `translate3d(${offset}px, 0, 0)`
|
|
105
|
+
} else {
|
|
106
|
+
bar.style.setProperty("--radix-scroll-area-thumb-height", `${thumbSize}px`)
|
|
107
|
+
thumb.style.height = "var(--radix-scroll-area-thumb-height)"
|
|
108
|
+
thumb.style.transform = `translate3d(0, ${offset}px, 0)`
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Radix's `getThumbSize` (scroll-area.tsx:1061): the track's length times the
|
|
113
|
+
// fraction of the content on screen, never below 18px — past that the thumb
|
|
114
|
+
// stops being a proportion and starts being a handle.
|
|
115
|
+
thumbSize({ viewport, content, track }) {
|
|
116
|
+
const ratio = content > 0 ? viewport / content : 0
|
|
117
|
+
|
|
118
|
+
return Math.max(track * ratio, MIN_THUMB)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// `getThumbOffsetFromScroll` (:1087), which is a linear scale from how far
|
|
122
|
+
// the content has scrolled to how far the thumb can travel.
|
|
123
|
+
thumbOffset({ viewport, content, track, scroll }, thumbSize) {
|
|
124
|
+
const maxScroll = content - viewport
|
|
125
|
+
const maxThumb = track - thumbSize
|
|
126
|
+
|
|
127
|
+
if (maxScroll <= 0 || maxThumb <= 0) return 0
|
|
128
|
+
|
|
129
|
+
return this.clamp(scroll, 0, maxScroll) * (maxThumb / maxScroll)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
sizes(axis, bar) {
|
|
133
|
+
const viewport = this.viewportTarget
|
|
134
|
+
const horizontal = axis === "horizontal"
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
viewport: horizontal ? viewport.clientWidth : viewport.clientHeight,
|
|
138
|
+
content: horizontal ? viewport.scrollWidth : viewport.scrollHeight,
|
|
139
|
+
scroll: horizontal ? viewport.scrollLeft : viewport.scrollTop,
|
|
140
|
+
// The track is the bar minus its own padding, which is where the 1px of
|
|
141
|
+
// `p-px` goes.
|
|
142
|
+
track: this.trackSize(bar, horizontal)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
trackSize(bar, horizontal) {
|
|
147
|
+
const style = window.getComputedStyle(bar)
|
|
148
|
+
const padding = horizontal
|
|
149
|
+
? parseFloat(style.paddingLeft) + parseFloat(style.paddingRight)
|
|
150
|
+
: parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)
|
|
151
|
+
|
|
152
|
+
return (horizontal ? bar.clientWidth : bar.clientHeight) - (padding || 0)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
overflows(axis) {
|
|
156
|
+
const { viewport, content } = this.sizes(axis, this.scrollbarFor(axis) || this.element)
|
|
157
|
+
|
|
158
|
+
return content - viewport > 1
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// The two bars stop short of each other by exactly the other's thickness, and
|
|
162
|
+
// the corner fills what is left. Published as custom properties because that
|
|
163
|
+
// is where the bars' inline `bottom` and `right` already look.
|
|
164
|
+
renderCorner() {
|
|
165
|
+
if (!this.hasCornerTarget) return
|
|
166
|
+
|
|
167
|
+
const horizontal = this.scrollbarFor("horizontal")
|
|
168
|
+
const vertical = this.scrollbarFor("vertical")
|
|
169
|
+
|
|
170
|
+
this.element.style.setProperty(
|
|
171
|
+
"--radix-scroll-area-corner-width", `${vertical ? vertical.offsetWidth : 0}px`
|
|
172
|
+
)
|
|
173
|
+
this.element.style.setProperty(
|
|
174
|
+
"--radix-scroll-area-corner-height", `${horizontal ? horizontal.offsetHeight : 0}px`
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ------------------------------------------------------------------ drag
|
|
179
|
+
|
|
180
|
+
// On the bar rather than the thumb, so a press on the track jumps there —
|
|
181
|
+
// which is what a native scrollbar does and what Radix's
|
|
182
|
+
// `getScrollPositionFromPointer` computes (:1069).
|
|
183
|
+
startDrag(event) {
|
|
184
|
+
if (event.button !== 0) return
|
|
185
|
+
|
|
186
|
+
const bar = event.currentTarget
|
|
187
|
+
const axis = this.axisOf(bar)
|
|
188
|
+
const thumb = bar.querySelector("[data-slot=scroll-area-thumb]")
|
|
189
|
+
|
|
190
|
+
event.preventDefault()
|
|
191
|
+
bar.setPointerCapture(event.pointerId)
|
|
192
|
+
|
|
193
|
+
const thumbRect = thumb.getBoundingClientRect()
|
|
194
|
+
const pointer = axis === "horizontal" ? event.clientX : event.clientY
|
|
195
|
+
const thumbStart = axis === "horizontal" ? thumbRect.left : thumbRect.top
|
|
196
|
+
const thumbLength = axis === "horizontal" ? thumbRect.width : thumbRect.height
|
|
197
|
+
const inside = pointer >= thumbStart && pointer <= thumbStart + thumbLength
|
|
198
|
+
|
|
199
|
+
// Grabbing the thumb keeps the point you took hold of under the pointer;
|
|
200
|
+
// pressing the track centres it on the press instead.
|
|
201
|
+
this.dragging = { bar, axis, grip: inside ? pointer - thumbStart : thumbLength / 2 }
|
|
202
|
+
|
|
203
|
+
this.onMove = (moveEvent) => this.drag(moveEvent)
|
|
204
|
+
this.onUp = () => this.endDrag()
|
|
205
|
+
bar.addEventListener("pointermove", this.onMove)
|
|
206
|
+
bar.addEventListener("pointerup", this.onUp)
|
|
207
|
+
|
|
208
|
+
this.drag(event)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
drag(event) {
|
|
212
|
+
if (!this.dragging) return
|
|
213
|
+
|
|
214
|
+
const { bar, axis, grip } = this.dragging
|
|
215
|
+
const horizontal = axis === "horizontal"
|
|
216
|
+
const sizes = this.sizes(axis, bar)
|
|
217
|
+
const thumbSize = this.thumbSize(sizes)
|
|
218
|
+
const rect = bar.getBoundingClientRect()
|
|
219
|
+
const pointer = (horizontal ? event.clientX - rect.left : event.clientY - rect.top) - grip
|
|
220
|
+
const maxThumb = sizes.track - thumbSize
|
|
221
|
+
const maxScroll = sizes.content - sizes.viewport
|
|
222
|
+
|
|
223
|
+
if (maxThumb <= 0 || maxScroll <= 0) return
|
|
224
|
+
|
|
225
|
+
const scroll = this.clamp(pointer, 0, maxThumb) * (maxScroll / maxThumb)
|
|
226
|
+
|
|
227
|
+
if (horizontal) this.viewportTarget.scrollLeft = scroll
|
|
228
|
+
else this.viewportTarget.scrollTop = scroll
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
endDrag() {
|
|
232
|
+
if (!this.dragging) return
|
|
233
|
+
|
|
234
|
+
const { bar } = this.dragging
|
|
235
|
+
bar.removeEventListener("pointermove", this.onMove)
|
|
236
|
+
bar.removeEventListener("pointerup", this.onUp)
|
|
237
|
+
this.dragging = null
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ----------------------------------------------------------------- helpers
|
|
241
|
+
|
|
242
|
+
axisOf(bar) {
|
|
243
|
+
return bar.dataset.orientation === "horizontal" ? "horizontal" : "vertical"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
scrollbarFor(axis) {
|
|
247
|
+
return this.scrollbarTargets.find((bar) => this.axisOf(bar) === axis)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
clamp(value, min, max) {
|
|
251
|
+
return Math.min(Math.max(value, min), max)
|
|
252
|
+
}
|
|
253
|
+
}
|