shadcn-rails 0.1.0
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/.dockerignore +40 -0
- data/CHANGELOG.md +54 -0
- data/CLAUDE.md +463 -0
- data/PROGRESS.md +485 -0
- data/README.md +1483 -0
- data/Rakefile +29 -0
- data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +13 -0
- data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +46 -0
- data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +111 -0
- data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +27 -0
- data/__tests__/controllers/accordion_controller.test.js +904 -0
- data/__tests__/controllers/calendar_controller.test.js +1370 -0
- data/__tests__/controllers/carousel_controller.test.js +912 -0
- data/__tests__/controllers/checkbox_controller.test.js +454 -0
- data/__tests__/controllers/collapsible_controller.test.js +407 -0
- data/__tests__/controllers/combobox_controller.test.js +966 -0
- data/__tests__/controllers/context_menu_controller.test.js +627 -0
- data/__tests__/controllers/date_picker_controller.test.js +636 -0
- data/__tests__/controllers/dialog_controller.test.js +878 -0
- data/__tests__/controllers/drawer_controller.test.js +995 -0
- data/__tests__/controllers/menubar_controller.test.js +736 -0
- data/__tests__/controllers/navigation_menu_controller.test.js +598 -0
- data/__tests__/controllers/popover_controller.test.js +1007 -0
- data/__tests__/controllers/radio_group_controller.test.js +640 -0
- data/__tests__/controllers/resizable_controller.test.js +680 -0
- data/__tests__/controllers/select_controller.test.js +674 -0
- data/__tests__/controllers/sheet_controller.test.js +986 -0
- data/__tests__/controllers/slider_controller.test.js +1036 -0
- data/__tests__/controllers/switch_controller.test.js +424 -0
- data/__tests__/controllers/tabs_controller.test.js +907 -0
- data/__tests__/controllers/toggle_group_controller.test.js +839 -0
- data/__tests__/controllers/tooltip_controller.test.js +808 -0
- data/__tests__/helpers/stimulus-test-helper.js +203 -0
- data/app/assets/config/manifest.js +1 -0
- data/app/assets/javascripts/shadcn/controllers/accordion_controller.d.ts +53 -0
- data/app/assets/javascripts/shadcn/controllers/accordion_controller.js +140 -0
- data/app/assets/javascripts/shadcn/controllers/avatar_controller.d.ts +22 -0
- data/app/assets/javascripts/shadcn/controllers/avatar_controller.js +26 -0
- data/app/assets/javascripts/shadcn/controllers/calendar_controller.js +592 -0
- data/app/assets/javascripts/shadcn/controllers/carousel_controller.js +263 -0
- data/app/assets/javascripts/shadcn/controllers/checkbox_controller.d.ts +31 -0
- data/app/assets/javascripts/shadcn/controllers/checkbox_controller.js +48 -0
- data/app/assets/javascripts/shadcn/controllers/collapsible_controller.d.ts +43 -0
- data/app/assets/javascripts/shadcn/controllers/collapsible_controller.js +73 -0
- data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +234 -0
- data/app/assets/javascripts/shadcn/controllers/command_controller.js +141 -0
- data/app/assets/javascripts/shadcn/controllers/command_dialog_controller.js +162 -0
- data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +202 -0
- data/app/assets/javascripts/shadcn/controllers/date_picker_controller.js +282 -0
- data/app/assets/javascripts/shadcn/controllers/dialog_controller.d.ts +67 -0
- data/app/assets/javascripts/shadcn/controllers/dialog_controller.js +187 -0
- data/app/assets/javascripts/shadcn/controllers/drawer_controller.d.ts +58 -0
- data/app/assets/javascripts/shadcn/controllers/drawer_controller.js +112 -0
- data/app/assets/javascripts/shadcn/controllers/dropdown_controller.d.ts +83 -0
- data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +225 -0
- data/app/assets/javascripts/shadcn/controllers/hover_card_controller.d.ts +59 -0
- data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +143 -0
- data/app/assets/javascripts/shadcn/controllers/input_otp_controller.d.ts +44 -0
- data/app/assets/javascripts/shadcn/controllers/input_otp_controller.js +206 -0
- data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +323 -0
- data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +251 -0
- data/app/assets/javascripts/shadcn/controllers/popover_controller.d.ts +56 -0
- data/app/assets/javascripts/shadcn/controllers/popover_controller.js +141 -0
- data/app/assets/javascripts/shadcn/controllers/radio_group_controller.d.ts +47 -0
- data/app/assets/javascripts/shadcn/controllers/radio_group_controller.js +108 -0
- data/app/assets/javascripts/shadcn/controllers/resizable_controller.js +272 -0
- data/app/assets/javascripts/shadcn/controllers/scroll_area_controller.d.ts +44 -0
- data/app/assets/javascripts/shadcn/controllers/scroll_area_controller.js +74 -0
- data/app/assets/javascripts/shadcn/controllers/select_controller.d.ts +84 -0
- data/app/assets/javascripts/shadcn/controllers/select_controller.js +222 -0
- data/app/assets/javascripts/shadcn/controllers/sheet_controller.d.ts +60 -0
- data/app/assets/javascripts/shadcn/controllers/sheet_controller.js +151 -0
- data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +148 -0
- data/app/assets/javascripts/shadcn/controllers/slider_controller.d.ts +102 -0
- data/app/assets/javascripts/shadcn/controllers/slider_controller.js +364 -0
- data/app/assets/javascripts/shadcn/controllers/switch_controller.d.ts +46 -0
- data/app/assets/javascripts/shadcn/controllers/switch_controller.js +78 -0
- data/app/assets/javascripts/shadcn/controllers/tabs_controller.d.ts +51 -0
- data/app/assets/javascripts/shadcn/controllers/tabs_controller.js +126 -0
- data/app/assets/javascripts/shadcn/controllers/toast_controller.d.ts +37 -0
- data/app/assets/javascripts/shadcn/controllers/toast_controller.js +58 -0
- data/app/assets/javascripts/shadcn/controllers/toggle_controller.d.ts +27 -0
- data/app/assets/javascripts/shadcn/controllers/toggle_controller.js +42 -0
- data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.d.ts +44 -0
- data/app/assets/javascripts/shadcn/controllers/toggle_group_controller.js +68 -0
- data/app/assets/javascripts/shadcn/controllers/tooltip_controller.d.ts +56 -0
- data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +117 -0
- data/app/assets/javascripts/shadcn/index.d.ts +74 -0
- data/app/assets/javascripts/shadcn/index.js +133 -0
- data/app/assets/stylesheets/.keep +0 -0
- data/app/assets/stylesheets/shadcn/base.css +445 -0
- data/app/assets/stylesheets/shadcn/components.css +513 -0
- data/app/assets/stylesheets/shadcn/index.css +18 -0
- data/app/assets/stylesheets/shadcn/themes/gray.css +68 -0
- data/app/assets/stylesheets/shadcn/themes/slate.css +68 -0
- data/app/assets/stylesheets/shadcn/themes/stone.css +68 -0
- data/app/assets/stylesheets/shadcn/themes/zinc.css +68 -0
- data/app/components/shadcn/accordion_component.rb +63 -0
- data/app/components/shadcn/accordion_content_component.rb +29 -0
- data/app/components/shadcn/accordion_item_component.rb +40 -0
- data/app/components/shadcn/accordion_trigger_component.rb +49 -0
- data/app/components/shadcn/alert_component.rb +75 -0
- data/app/components/shadcn/alert_description_component.rb +12 -0
- data/app/components/shadcn/alert_dialog_action_component.rb +24 -0
- data/app/components/shadcn/alert_dialog_cancel_component.rb +24 -0
- data/app/components/shadcn/alert_dialog_component.rb +71 -0
- data/app/components/shadcn/alert_dialog_content_component.rb +57 -0
- data/app/components/shadcn/alert_dialog_description_component.rb +12 -0
- data/app/components/shadcn/alert_dialog_footer_component.rb +19 -0
- data/app/components/shadcn/alert_dialog_header_component.rb +19 -0
- data/app/components/shadcn/alert_dialog_title_component.rb +12 -0
- data/app/components/shadcn/alert_title_component.rb +12 -0
- data/app/components/shadcn/aspect_ratio_component.rb +49 -0
- data/app/components/shadcn/avatar_component.rb +107 -0
- data/app/components/shadcn/avatar_fallback_component.rb +17 -0
- data/app/components/shadcn/badge_component.rb +49 -0
- data/app/components/shadcn/base_component.rb +100 -0
- data/app/components/shadcn/breadcrumb_component.rb +70 -0
- data/app/components/shadcn/breadcrumb_item_component.rb +50 -0
- data/app/components/shadcn/button_component.rb +141 -0
- data/app/components/shadcn/button_group_component.rb +69 -0
- data/app/components/shadcn/calendar_component.rb +337 -0
- data/app/components/shadcn/card_action_component.rb +10 -0
- data/app/components/shadcn/card_component.rb +63 -0
- data/app/components/shadcn/card_content_component.rb +19 -0
- data/app/components/shadcn/card_description_component.rb +12 -0
- data/app/components/shadcn/card_footer_component.rb +12 -0
- data/app/components/shadcn/card_header_component.rb +24 -0
- data/app/components/shadcn/card_title_component.rb +18 -0
- data/app/components/shadcn/carousel_component.rb +275 -0
- data/app/components/shadcn/checkbox_component.rb +103 -0
- data/app/components/shadcn/collapsible_component.rb +66 -0
- data/app/components/shadcn/collapsible_content_component.rb +28 -0
- data/app/components/shadcn/combobox_component.rb +322 -0
- data/app/components/shadcn/command_component.rb +52 -0
- data/app/components/shadcn/command_dialog_component.rb +76 -0
- data/app/components/shadcn/command_empty_component.rb +12 -0
- data/app/components/shadcn/command_group_component.rb +34 -0
- data/app/components/shadcn/command_input_component.rb +59 -0
- data/app/components/shadcn/command_item_component.rb +48 -0
- data/app/components/shadcn/command_list_component.rb +38 -0
- data/app/components/shadcn/command_separator_component.rb +12 -0
- data/app/components/shadcn/command_shortcut_component.rb +12 -0
- data/app/components/shadcn/context_menu_component.rb +64 -0
- data/app/components/shadcn/context_menu_content_component.rb +44 -0
- data/app/components/shadcn/context_menu_item_component.rb +63 -0
- data/app/components/shadcn/context_menu_label_component.rb +18 -0
- data/app/components/shadcn/context_menu_separator_component.rb +12 -0
- data/app/components/shadcn/context_menu_shortcut_component.rb +12 -0
- data/app/components/shadcn/date_picker_component.rb +368 -0
- data/app/components/shadcn/dialog_component.rb +77 -0
- data/app/components/shadcn/dialog_content_component.rb +91 -0
- data/app/components/shadcn/dialog_description_component.rb +12 -0
- data/app/components/shadcn/dialog_footer_component.rb +12 -0
- data/app/components/shadcn/dialog_header_component.rb +19 -0
- data/app/components/shadcn/dialog_title_component.rb +12 -0
- data/app/components/shadcn/drawer_component.rb +72 -0
- data/app/components/shadcn/drawer_content_component.rb +76 -0
- data/app/components/shadcn/drawer_description_component.rb +12 -0
- data/app/components/shadcn/drawer_footer_component.rb +12 -0
- data/app/components/shadcn/drawer_header_component.rb +19 -0
- data/app/components/shadcn/drawer_title_component.rb +12 -0
- data/app/components/shadcn/dropdown_menu_component.rb +75 -0
- data/app/components/shadcn/dropdown_menu_content_component.rb +49 -0
- data/app/components/shadcn/dropdown_menu_group_component.rb +10 -0
- data/app/components/shadcn/dropdown_menu_item_component.rb +63 -0
- data/app/components/shadcn/dropdown_menu_label_component.rb +18 -0
- data/app/components/shadcn/dropdown_menu_separator_component.rb +12 -0
- data/app/components/shadcn/dropdown_menu_shortcut_component.rb +12 -0
- data/app/components/shadcn/empty_component.rb +48 -0
- data/app/components/shadcn/empty_content_component.rb +12 -0
- data/app/components/shadcn/empty_description_component.rb +12 -0
- data/app/components/shadcn/empty_header_component.rb +29 -0
- data/app/components/shadcn/empty_media_component.rb +21 -0
- data/app/components/shadcn/empty_title_component.rb +12 -0
- data/app/components/shadcn/field_component.rb +113 -0
- data/app/components/shadcn/hover_card_component.rb +64 -0
- data/app/components/shadcn/hover_card_content_component.rb +36 -0
- data/app/components/shadcn/input_component.rb +108 -0
- data/app/components/shadcn/input_group_component.rb +70 -0
- data/app/components/shadcn/input_otp_component.rb +183 -0
- data/app/components/shadcn/item_actions_component.rb +12 -0
- data/app/components/shadcn/item_component.rb +98 -0
- data/app/components/shadcn/item_content_component.rb +24 -0
- data/app/components/shadcn/item_description_component.rb +12 -0
- data/app/components/shadcn/item_footer_component.rb +12 -0
- data/app/components/shadcn/item_group_component.rb +24 -0
- data/app/components/shadcn/item_header_component.rb +12 -0
- data/app/components/shadcn/item_media_component.rb +22 -0
- data/app/components/shadcn/item_separator_component.rb +12 -0
- data/app/components/shadcn/item_title_component.rb +12 -0
- data/app/components/shadcn/kbd_component.rb +36 -0
- data/app/components/shadcn/label_component.rb +49 -0
- data/app/components/shadcn/menubar_checkbox_item_component.rb +76 -0
- data/app/components/shadcn/menubar_component.rb +56 -0
- data/app/components/shadcn/menubar_content_component.rb +64 -0
- data/app/components/shadcn/menubar_item_component.rb +65 -0
- data/app/components/shadcn/menubar_label_component.rb +27 -0
- data/app/components/shadcn/menubar_menu_component.rb +34 -0
- data/app/components/shadcn/menubar_radio_group_component.rb +42 -0
- data/app/components/shadcn/menubar_radio_item_component.rb +76 -0
- data/app/components/shadcn/menubar_separator_component.rb +22 -0
- data/app/components/shadcn/menubar_shortcut_component.rb +21 -0
- data/app/components/shadcn/menubar_sub_component.rb +38 -0
- data/app/components/shadcn/menubar_sub_content_component.rb +45 -0
- data/app/components/shadcn/menubar_sub_trigger_component.rb +59 -0
- data/app/components/shadcn/menubar_trigger_component.rb +31 -0
- data/app/components/shadcn/native_select_component.rb +150 -0
- data/app/components/shadcn/navigation_menu_component.rb +76 -0
- data/app/components/shadcn/navigation_menu_content_component.rb +30 -0
- data/app/components/shadcn/navigation_menu_item_component.rb +39 -0
- data/app/components/shadcn/navigation_menu_link_component.rb +38 -0
- data/app/components/shadcn/navigation_menu_list_component.rb +29 -0
- data/app/components/shadcn/navigation_menu_trigger_component.rb +59 -0
- data/app/components/shadcn/pagination_component.rb +195 -0
- data/app/components/shadcn/pagination_content_component.rb +47 -0
- data/app/components/shadcn/pagination_ellipsis_component.rb +30 -0
- data/app/components/shadcn/pagination_item_component.rb +53 -0
- data/app/components/shadcn/pagination_next_component.rb +48 -0
- data/app/components/shadcn/pagination_previous_component.rb +48 -0
- data/app/components/shadcn/popover_component.rb +76 -0
- data/app/components/shadcn/popover_content_component.rb +25 -0
- data/app/components/shadcn/progress_component.rb +77 -0
- data/app/components/shadcn/radio_group_component.rb +129 -0
- data/app/components/shadcn/radio_group_item_component.rb +109 -0
- data/app/components/shadcn/resizable_handle_component.rb +98 -0
- data/app/components/shadcn/resizable_panel_component.rb +56 -0
- data/app/components/shadcn/resizable_panel_group_component.rb +94 -0
- data/app/components/shadcn/scroll_area_component.rb +110 -0
- data/app/components/shadcn/select_component.rb +151 -0
- data/app/components/shadcn/select_group_component.rb +32 -0
- data/app/components/shadcn/select_item_component.rb +59 -0
- data/app/components/shadcn/select_separator_component.rb +12 -0
- data/app/components/shadcn/separator_component.rb +54 -0
- data/app/components/shadcn/sheet_component.rb +82 -0
- data/app/components/shadcn/sheet_content_component.rb +95 -0
- data/app/components/shadcn/sheet_description_component.rb +12 -0
- data/app/components/shadcn/sheet_footer_component.rb +12 -0
- data/app/components/shadcn/sheet_header_component.rb +19 -0
- data/app/components/shadcn/sheet_title_component.rb +12 -0
- data/app/components/shadcn/sidebar_component.rb +180 -0
- data/app/components/shadcn/sidebar_content_component.rb +32 -0
- data/app/components/shadcn/sidebar_footer_component.rb +24 -0
- data/app/components/shadcn/sidebar_group_action_component.rb +26 -0
- data/app/components/shadcn/sidebar_group_component.rb +38 -0
- data/app/components/shadcn/sidebar_group_content_component.rb +32 -0
- data/app/components/shadcn/sidebar_group_label_component.rb +25 -0
- data/app/components/shadcn/sidebar_header_component.rb +24 -0
- data/app/components/shadcn/sidebar_inset_component.rb +25 -0
- data/app/components/shadcn/sidebar_menu_action_component.rb +37 -0
- data/app/components/shadcn/sidebar_menu_badge_component.rb +25 -0
- data/app/components/shadcn/sidebar_menu_button_component.rb +52 -0
- data/app/components/shadcn/sidebar_menu_component.rb +32 -0
- data/app/components/shadcn/sidebar_menu_item_component.rb +41 -0
- data/app/components/shadcn/sidebar_menu_skeleton_component.rb +46 -0
- data/app/components/shadcn/sidebar_menu_sub_button_component.rb +43 -0
- data/app/components/shadcn/sidebar_menu_sub_component.rb +33 -0
- data/app/components/shadcn/sidebar_menu_sub_item_component.rb +30 -0
- data/app/components/shadcn/sidebar_provider_component.rb +57 -0
- data/app/components/shadcn/sidebar_rail_component.rb +30 -0
- data/app/components/shadcn/sidebar_separator_component.rb +24 -0
- data/app/components/shadcn/sidebar_trigger_component.rb +51 -0
- data/app/components/shadcn/skeleton_component.rb +29 -0
- data/app/components/shadcn/slider_component.rb +76 -0
- data/app/components/shadcn/spinner_component.rb +67 -0
- data/app/components/shadcn/switch_component.rb +147 -0
- data/app/components/shadcn/table_body_component.rb +16 -0
- data/app/components/shadcn/table_caption_component.rb +12 -0
- data/app/components/shadcn/table_cell_component.rb +12 -0
- data/app/components/shadcn/table_component.rb +57 -0
- data/app/components/shadcn/table_footer_component.rb +16 -0
- data/app/components/shadcn/table_head_component.rb +12 -0
- data/app/components/shadcn/table_header_component.rb +16 -0
- data/app/components/shadcn/table_row_component.rb +40 -0
- data/app/components/shadcn/tabs_component.rb +78 -0
- data/app/components/shadcn/tabs_content_component.rb +32 -0
- data/app/components/shadcn/tabs_list_component.rb +30 -0
- data/app/components/shadcn/tabs_trigger_component.rb +37 -0
- data/app/components/shadcn/textarea_component.rb +84 -0
- data/app/components/shadcn/toast_action_component.rb +18 -0
- data/app/components/shadcn/toast_component.rb +114 -0
- data/app/components/shadcn/toast_description_component.rb +12 -0
- data/app/components/shadcn/toast_title_component.rb +12 -0
- data/app/components/shadcn/toast_viewport_component.rb +12 -0
- data/app/components/shadcn/toggle_component.rb +77 -0
- data/app/components/shadcn/toggle_group_component.rb +96 -0
- data/app/components/shadcn/toggle_group_item_component.rb +62 -0
- data/app/components/shadcn/tooltip_component.rb +89 -0
- data/app/components/shadcn/typography_component.rb +112 -0
- data/babel.config.cjs +5 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/config/importmap.rb +5 -0
- data/fly.toml +26 -0
- data/jest.config.js +19 -0
- data/jest.setup.js +8 -0
- data/lib/generators/shadcn/component/component_generator.rb +188 -0
- data/lib/generators/shadcn/install/install_generator.rb +140 -0
- data/lib/generators/shadcn/install/templates/initializer.rb.tt +35 -0
- data/lib/generators/shadcn/install/templates/shadcn.yml.tt +35 -0
- data/lib/generators/shadcn/theme/theme_generator.rb +128 -0
- data/lib/shadcn/rails/class_merger.rb +228 -0
- data/lib/shadcn/rails/configuration.rb +341 -0
- data/lib/shadcn/rails/engine.rb +59 -0
- data/lib/shadcn/rails/helpers/class_name_helper.rb +35 -0
- data/lib/shadcn/rails/helpers/component_helper.rb +60 -0
- data/lib/shadcn/rails/helpers/pagination_helper.rb +187 -0
- data/lib/shadcn/rails/version.rb +7 -0
- data/lib/shadcn/rails.rb +179 -0
- data/package-lock.json +7415 -0
- data/package.json +68 -0
- data/rollup.config.js +29 -0
- data/sig/shadcn/rails.rbs +6 -0
- metadata +526 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Radio Group component
|
|
5
|
+
# Group of mutually exclusive radio items
|
|
6
|
+
class MenubarRadioGroupComponent < BaseComponent
|
|
7
|
+
renders_many :items, lambda { |**options, &block|
|
|
8
|
+
MenubarRadioItemComponent.new(**options, &block)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
# @param value [String] Currently selected value
|
|
12
|
+
def initialize(value: nil, **options, &block)
|
|
13
|
+
super(**options, &block)
|
|
14
|
+
@value = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
content_tag(:div, group_content, group_attributes)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def group_content
|
|
24
|
+
if items.any?
|
|
25
|
+
safe_join(items)
|
|
26
|
+
else
|
|
27
|
+
content
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def group_attributes
|
|
32
|
+
attrs = {
|
|
33
|
+
class: class_name,
|
|
34
|
+
role: "group",
|
|
35
|
+
"data-value": @value
|
|
36
|
+
}
|
|
37
|
+
attrs.merge!(html_options)
|
|
38
|
+
attrs.merge!(build_data)
|
|
39
|
+
attrs.compact
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Radio Item component
|
|
5
|
+
# A radio button within a radio group
|
|
6
|
+
class MenubarRadioItemComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
|
8
|
+
|
|
9
|
+
renders_one :shortcut, lambda { |**options|
|
|
10
|
+
MenubarShortcutComponent.new(**options)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# @param value [String] Value of this radio item
|
|
14
|
+
# @param checked [Boolean] Whether item is selected
|
|
15
|
+
# @param disabled [Boolean] Whether item is disabled
|
|
16
|
+
def initialize(value: nil, checked: false, disabled: false, **options, &block)
|
|
17
|
+
super(**options, &block)
|
|
18
|
+
@value = value
|
|
19
|
+
@checked = checked
|
|
20
|
+
@disabled = disabled
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
content_tag(:div, item_content, item_attributes)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def item_content
|
|
30
|
+
safe_join([
|
|
31
|
+
radio_indicator,
|
|
32
|
+
content,
|
|
33
|
+
shortcut
|
|
34
|
+
].compact)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def radio_indicator
|
|
38
|
+
content_tag(:span, radio_icon, class: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def radio_icon
|
|
42
|
+
return "" unless @checked
|
|
43
|
+
|
|
44
|
+
content_tag(:svg, circle_svg, {
|
|
45
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
46
|
+
width: "16",
|
|
47
|
+
height: "16",
|
|
48
|
+
viewBox: "0 0 24 24",
|
|
49
|
+
fill: "currentColor",
|
|
50
|
+
stroke: "none",
|
|
51
|
+
class: "h-4 w-4"
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def circle_svg
|
|
56
|
+
content_tag(:circle, "", cx: "12", cy: "12", r: "6")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def item_attributes
|
|
60
|
+
attrs = {
|
|
61
|
+
class: cn(BASE_CLASSES, class_name),
|
|
62
|
+
role: "menuitemradio",
|
|
63
|
+
"aria-checked": @checked.to_s,
|
|
64
|
+
tabindex: @disabled ? nil : "-1",
|
|
65
|
+
"data-disabled": @disabled ? "" : nil,
|
|
66
|
+
"data-state": @checked ? "checked" : "unchecked",
|
|
67
|
+
"data-value": @value,
|
|
68
|
+
"data-shadcn--menubar-target": "item",
|
|
69
|
+
"data-action": "click->shadcn--menubar#selectRadio"
|
|
70
|
+
}
|
|
71
|
+
attrs.merge!(html_options)
|
|
72
|
+
attrs.merge!(build_data)
|
|
73
|
+
attrs.compact
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Separator component
|
|
5
|
+
# Visual separator between menu items
|
|
6
|
+
class MenubarSeparatorComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "-mx-1 my-1 h-px bg-muted"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:div, "", separator_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def separator_attributes
|
|
16
|
+
{
|
|
17
|
+
class: cn(BASE_CLASSES, class_name),
|
|
18
|
+
role: "separator"
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Shortcut component
|
|
5
|
+
# Displays keyboard shortcuts next to menu items
|
|
6
|
+
class MenubarShortcutComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "ml-auto text-xs tracking-widest text-muted-foreground"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:span, content, shortcut_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def shortcut_attributes
|
|
16
|
+
{
|
|
17
|
+
class: cn(BASE_CLASSES, class_name)
|
|
18
|
+
}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Sub component
|
|
5
|
+
# Creates nested/submenu functionality
|
|
6
|
+
class MenubarSubComponent < BaseComponent
|
|
7
|
+
renders_one :trigger, lambda { |**options, &block|
|
|
8
|
+
MenubarSubTriggerComponent.new(**options, &block)
|
|
9
|
+
}
|
|
10
|
+
# Note: Named content_slot because 'content' is a reserved ViewComponent method
|
|
11
|
+
renders_one :content_slot, lambda { |**options|
|
|
12
|
+
MenubarSubContentComponent.new(**options)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# Alias for more intuitive API
|
|
16
|
+
alias_method :with_content, :with_content_slot
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
content_tag(:div, sub_content, sub_attributes)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def sub_content
|
|
25
|
+
safe_join([trigger, content_slot, content].compact)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def sub_attributes
|
|
29
|
+
attrs = {
|
|
30
|
+
class: cn("relative", class_name),
|
|
31
|
+
"data-shadcn--menubar-target": "sub"
|
|
32
|
+
}
|
|
33
|
+
attrs.merge!(html_options)
|
|
34
|
+
attrs.merge!(build_data)
|
|
35
|
+
attrs.compact
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Sub Content component
|
|
5
|
+
# Container for submenu items
|
|
6
|
+
class MenubarSubContentComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
|
|
8
|
+
|
|
9
|
+
renders_many :items, lambda { |**options, &block|
|
|
10
|
+
MenubarItemComponent.new(**options, &block)
|
|
11
|
+
}
|
|
12
|
+
renders_many :separators, lambda { |**options|
|
|
13
|
+
MenubarSeparatorComponent.new(**options)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
content_tag(:div, sub_content, content_attributes)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def sub_content
|
|
23
|
+
if items.any? || separators.any?
|
|
24
|
+
safe_join([items, separators, content].flatten.compact)
|
|
25
|
+
else
|
|
26
|
+
content
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def content_attributes
|
|
31
|
+
attrs = {
|
|
32
|
+
class: cn(BASE_CLASSES, class_name),
|
|
33
|
+
role: "menu",
|
|
34
|
+
"aria-orientation": "vertical",
|
|
35
|
+
"data-state": "closed",
|
|
36
|
+
"data-shadcn--menubar-target": "subContent",
|
|
37
|
+
"data-action": "mouseenter->shadcn--menubar#cancelCloseSubTimer mouseleave->shadcn--menubar#startCloseSubTimer",
|
|
38
|
+
hidden: true
|
|
39
|
+
}
|
|
40
|
+
attrs.merge!(html_options)
|
|
41
|
+
attrs.merge!(build_data)
|
|
42
|
+
attrs.compact
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Sub Trigger component
|
|
5
|
+
# Button that opens a submenu
|
|
6
|
+
class MenubarSubTriggerComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground"
|
|
8
|
+
|
|
9
|
+
# @param inset [Boolean] Whether to add left padding
|
|
10
|
+
def initialize(inset: false, **options, &block)
|
|
11
|
+
super(**options, &block)
|
|
12
|
+
@inset = inset
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
content_tag(:div, trigger_content, trigger_attributes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def trigger_content
|
|
22
|
+
safe_join([content, chevron_icon])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def chevron_icon
|
|
26
|
+
content_tag(:svg, chevron_path, {
|
|
27
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
28
|
+
width: "16",
|
|
29
|
+
height: "16",
|
|
30
|
+
viewBox: "0 0 24 24",
|
|
31
|
+
fill: "none",
|
|
32
|
+
stroke: "currentColor",
|
|
33
|
+
"stroke-width": "2",
|
|
34
|
+
"stroke-linecap": "round",
|
|
35
|
+
"stroke-linejoin": "round",
|
|
36
|
+
class: "ml-auto h-4 w-4"
|
|
37
|
+
})
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def chevron_path
|
|
41
|
+
content_tag(:polyline, "", points: "9 18 15 12 9 6")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def trigger_attributes
|
|
45
|
+
attrs = {
|
|
46
|
+
class: cn(BASE_CLASSES, @inset ? "pl-8" : "", class_name),
|
|
47
|
+
role: "menuitem",
|
|
48
|
+
"aria-haspopup": "menu",
|
|
49
|
+
"aria-expanded": "false",
|
|
50
|
+
"data-state": "closed",
|
|
51
|
+
"data-shadcn--menubar-target": "subTrigger",
|
|
52
|
+
"data-action": "mouseenter->shadcn--menubar#openSub mouseleave->shadcn--menubar#startCloseSubTimer"
|
|
53
|
+
}
|
|
54
|
+
attrs.merge!(html_options)
|
|
55
|
+
attrs.merge!(build_data)
|
|
56
|
+
attrs.compact
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Trigger component
|
|
5
|
+
# Button that opens the menu dropdown
|
|
6
|
+
class MenubarTriggerComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "flex cursor-default select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:button, content, trigger_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def trigger_attributes
|
|
16
|
+
attrs = {
|
|
17
|
+
class: cn(BASE_CLASSES, class_name),
|
|
18
|
+
type: "button",
|
|
19
|
+
role: "menuitem",
|
|
20
|
+
"aria-haspopup": "menu",
|
|
21
|
+
"aria-expanded": "false",
|
|
22
|
+
"data-state": "closed",
|
|
23
|
+
"data-shadcn--menubar-target": "trigger",
|
|
24
|
+
"data-action": "click->shadcn--menubar#toggle mouseenter->shadcn--menubar#hoverOpen"
|
|
25
|
+
}
|
|
26
|
+
attrs.merge!(html_options)
|
|
27
|
+
attrs.merge!(build_data)
|
|
28
|
+
attrs.compact
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Native Select component for styled native HTML selects
|
|
5
|
+
# Matches shadcn/ui Native Select component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic select
|
|
8
|
+
# <%= render Shadcn::NativeSelectComponent.new(name: "country") do |select| %>
|
|
9
|
+
# <% select.with_option(value: "", disabled: true, selected: true) { "Select a country" } %>
|
|
10
|
+
# <% select.with_option(value: "us") { "United States" } %>
|
|
11
|
+
# <% select.with_option(value: "uk") { "United Kingdom" } %>
|
|
12
|
+
# <% select.with_option(value: "ca") { "Canada" } %>
|
|
13
|
+
# <% end %>
|
|
14
|
+
#
|
|
15
|
+
# @example With optgroups
|
|
16
|
+
# <%= render Shadcn::NativeSelectComponent.new(name: "car") do |select| %>
|
|
17
|
+
# <% select.with_optgroup(label: "Swedish Cars") do |group| %>
|
|
18
|
+
# <% group.with_option(value: "volvo") { "Volvo" } %>
|
|
19
|
+
# <% group.with_option(value: "saab") { "Saab" } %>
|
|
20
|
+
# <% end %>
|
|
21
|
+
# <% select.with_optgroup(label: "German Cars") do |group| %>
|
|
22
|
+
# <% group.with_option(value: "mercedes") { "Mercedes" } %>
|
|
23
|
+
# <% group.with_option(value: "audi") { "Audi" } %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
#
|
|
27
|
+
# @example Disabled
|
|
28
|
+
# <%= render Shadcn::NativeSelectComponent.new(name: "status", disabled: true) do |select| %>
|
|
29
|
+
# <% select.with_option(value: "active") { "Active" } %>
|
|
30
|
+
# <% end %>
|
|
31
|
+
#
|
|
32
|
+
class NativeSelectComponent < BaseComponent
|
|
33
|
+
# Select wrapper classes for positioning the chevron icon
|
|
34
|
+
WRAPPER_CLASSES = "relative"
|
|
35
|
+
|
|
36
|
+
# Native select element classes
|
|
37
|
+
SELECT_CLASSES = "h-9 w-full cursor-pointer appearance-none rounded-md border border-input bg-transparent pl-3 pr-8 text-sm shadow-sm focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
|
38
|
+
|
|
39
|
+
# Chevron icon classes (positioned absolutely)
|
|
40
|
+
CHEVRON_CLASSES = "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground"
|
|
41
|
+
|
|
42
|
+
# Option slot
|
|
43
|
+
renders_many :options, "OptionComponent"
|
|
44
|
+
|
|
45
|
+
# Optgroup slot
|
|
46
|
+
renders_many :optgroups, "OptgroupComponent"
|
|
47
|
+
|
|
48
|
+
# @param name [String, nil] Select name attribute
|
|
49
|
+
# @param id [String, nil] Select ID attribute
|
|
50
|
+
# @param disabled [Boolean] Whether the select is disabled
|
|
51
|
+
# @param required [Boolean] Whether the select is required
|
|
52
|
+
def initialize(name: nil, id: nil, disabled: false, required: false, **options)
|
|
53
|
+
super(**options)
|
|
54
|
+
@name = name
|
|
55
|
+
@id = id
|
|
56
|
+
@disabled = disabled
|
|
57
|
+
@required = required
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def call
|
|
61
|
+
tag.div(class: WRAPPER_CLASSES) do
|
|
62
|
+
safe_join([
|
|
63
|
+
tag.select(**select_attributes) do
|
|
64
|
+
if optgroups.any?
|
|
65
|
+
safe_join(optgroups)
|
|
66
|
+
else
|
|
67
|
+
safe_join(options)
|
|
68
|
+
end
|
|
69
|
+
end,
|
|
70
|
+
chevron_icon
|
|
71
|
+
])
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def chevron_icon
|
|
76
|
+
tag.svg(
|
|
77
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
78
|
+
width: "16",
|
|
79
|
+
height: "16",
|
|
80
|
+
viewBox: "0 0 24 24",
|
|
81
|
+
fill: "none",
|
|
82
|
+
stroke: "currentColor",
|
|
83
|
+
stroke_width: "2",
|
|
84
|
+
stroke_linecap: "round",
|
|
85
|
+
stroke_linejoin: "round",
|
|
86
|
+
class: CHEVRON_CLASSES
|
|
87
|
+
) do
|
|
88
|
+
tag.path(d: "m6 9 6 6 6-6")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def select_attributes
|
|
95
|
+
{
|
|
96
|
+
name: @name,
|
|
97
|
+
id: @id,
|
|
98
|
+
disabled: @disabled || nil,
|
|
99
|
+
required: @required || nil,
|
|
100
|
+
class: merge_classes(SELECT_CLASSES)
|
|
101
|
+
}.merge(html_options).merge(build_data).compact
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Option subcomponent
|
|
105
|
+
class OptionComponent < BaseComponent
|
|
106
|
+
# @param value [String] Option value
|
|
107
|
+
# @param disabled [Boolean] Whether the option is disabled
|
|
108
|
+
# @param selected [Boolean] Whether the option is selected
|
|
109
|
+
def initialize(value: nil, disabled: false, selected: false, **options)
|
|
110
|
+
super(**options)
|
|
111
|
+
@value = value
|
|
112
|
+
@disabled = disabled
|
|
113
|
+
@selected = selected
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def call
|
|
117
|
+
tag.option(content, **option_attributes)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def option_attributes
|
|
123
|
+
{
|
|
124
|
+
value: @value,
|
|
125
|
+
disabled: @disabled || nil,
|
|
126
|
+
selected: @selected || nil
|
|
127
|
+
}.merge(html_options).merge(build_data).compact
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Optgroup subcomponent
|
|
132
|
+
class OptgroupComponent < BaseComponent
|
|
133
|
+
renders_many :options, OptionComponent
|
|
134
|
+
|
|
135
|
+
# @param label [String] Optgroup label
|
|
136
|
+
# @param disabled [Boolean] Whether the optgroup is disabled
|
|
137
|
+
def initialize(label:, disabled: false, **options)
|
|
138
|
+
super(**options)
|
|
139
|
+
@label = label
|
|
140
|
+
@disabled = disabled
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def call
|
|
144
|
+
tag.optgroup(label: @label, disabled: @disabled || nil, **html_options.merge(build_data).compact) do
|
|
145
|
+
safe_join(options)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# NavigationMenu component
|
|
5
|
+
# Matches shadcn/ui NavigationMenu component
|
|
6
|
+
# A collection of links for navigating websites
|
|
7
|
+
#
|
|
8
|
+
# @example Basic navigation menu
|
|
9
|
+
# <%= render Shadcn::NavigationMenuComponent.new do |nav| %>
|
|
10
|
+
# <% nav.with_list do |list| %>
|
|
11
|
+
# <% list.with_item do |item| %>
|
|
12
|
+
# <% item.with_trigger { "Getting Started" } %>
|
|
13
|
+
# <% item.with_content do %>
|
|
14
|
+
# <ul class="grid gap-3 p-4 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
|
15
|
+
# <li class="row-span-3">Featured content</li>
|
|
16
|
+
# <li><a href="#">Introduction</a></li>
|
|
17
|
+
# <li><a href="#">Installation</a></li>
|
|
18
|
+
# </ul>
|
|
19
|
+
# <% end %>
|
|
20
|
+
# <% end %>
|
|
21
|
+
# <% list.with_item do |item| %>
|
|
22
|
+
# <% item.with_link(href: "/docs") { "Documentation" } %>
|
|
23
|
+
# <% end %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
#
|
|
27
|
+
class NavigationMenuComponent < BaseComponent
|
|
28
|
+
BASE_CLASSES = "relative z-10 flex max-w-max flex-1 items-center justify-center"
|
|
29
|
+
|
|
30
|
+
renders_one :list, lambda { |**options|
|
|
31
|
+
NavigationMenuListComponent.new(**options)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def call
|
|
35
|
+
content_tag(:nav, navigation_content, navigation_attributes)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def navigation_content
|
|
41
|
+
safe_join([list, viewport].compact)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def viewport
|
|
45
|
+
content_tag(:div, viewport_inner, class: "absolute left-0 top-full flex justify-center")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def viewport_inner
|
|
49
|
+
content_tag(:div, "", viewport_attributes)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def viewport_attributes
|
|
53
|
+
{
|
|
54
|
+
class: cn(
|
|
55
|
+
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow",
|
|
56
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90",
|
|
57
|
+
"md:w-[var(--radix-navigation-menu-viewport-width)]"
|
|
58
|
+
),
|
|
59
|
+
"data-shadcn--navigation-menu-target": "viewport",
|
|
60
|
+
"data-state": "closed",
|
|
61
|
+
hidden: true
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def navigation_attributes
|
|
66
|
+
attrs = {
|
|
67
|
+
class: cn(BASE_CLASSES, class_name),
|
|
68
|
+
"data-controller": "shadcn--navigation-menu",
|
|
69
|
+
"aria-label": "Main"
|
|
70
|
+
}
|
|
71
|
+
attrs.merge!(html_options)
|
|
72
|
+
attrs.merge!(build_data)
|
|
73
|
+
attrs.compact
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Content area that appears when a navigation trigger is activated
|
|
5
|
+
class NavigationMenuContentComponent < BaseComponent
|
|
6
|
+
CONTENT_CLASSES = [
|
|
7
|
+
"left-0 top-0 w-full",
|
|
8
|
+
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out",
|
|
9
|
+
"data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out",
|
|
10
|
+
"data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52",
|
|
11
|
+
"data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52",
|
|
12
|
+
"md:absolute md:w-auto"
|
|
13
|
+
].join(" ").freeze
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
content_tag(:div, content, content_attributes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def content_attributes
|
|
22
|
+
{
|
|
23
|
+
class: cn(CONTENT_CLASSES, class_name),
|
|
24
|
+
"data-shadcn--navigation-menu-target": "content",
|
|
25
|
+
"data-state": "closed",
|
|
26
|
+
hidden: true
|
|
27
|
+
}.merge(html_options).compact
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Individual item in the navigation menu
|
|
5
|
+
class NavigationMenuItemComponent < BaseComponent
|
|
6
|
+
renders_one :trigger, lambda { |**options, &block|
|
|
7
|
+
NavigationMenuTriggerComponent.new(**options, &block)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
renders_one :dropdown, lambda { |**options, &block|
|
|
11
|
+
NavigationMenuContentComponent.new(**options, &block)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
renders_one :link, lambda { |href:, active: false, **options, &block|
|
|
15
|
+
NavigationMenuLinkComponent.new(href: href, active: active, **options, &block)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
content_tag(:li, item_content, item_attributes)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def item_content
|
|
25
|
+
if link
|
|
26
|
+
link
|
|
27
|
+
else
|
|
28
|
+
safe_join([trigger, dropdown].compact)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def item_attributes
|
|
33
|
+
{
|
|
34
|
+
class: cn("relative", class_name),
|
|
35
|
+
"data-shadcn--navigation-menu-target": "item"
|
|
36
|
+
}.merge(html_options).compact
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# A link within the navigation menu (for items without dropdowns)
|
|
5
|
+
class NavigationMenuLinkComponent < BaseComponent
|
|
6
|
+
LINK_CLASSES = [
|
|
7
|
+
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2",
|
|
8
|
+
"text-sm font-medium transition-colors",
|
|
9
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
10
|
+
"focus:bg-accent focus:text-accent-foreground focus:outline-none",
|
|
11
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
12
|
+
].join(" ").freeze
|
|
13
|
+
|
|
14
|
+
ACTIVE_CLASSES = "bg-accent/50"
|
|
15
|
+
|
|
16
|
+
# @param href [String] The link destination
|
|
17
|
+
# @param active [Boolean] Whether this link is currently active
|
|
18
|
+
def initialize(href:, active: false, **options)
|
|
19
|
+
super(**options)
|
|
20
|
+
@href = href
|
|
21
|
+
@active = active
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call
|
|
25
|
+
content_tag(:a, content, link_attributes)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def link_attributes
|
|
31
|
+
{
|
|
32
|
+
class: cn(LINK_CLASSES, @active && ACTIVE_CLASSES, class_name),
|
|
33
|
+
href: @href,
|
|
34
|
+
"data-active": @active ? "true" : nil
|
|
35
|
+
}.merge(html_options).compact
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|