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,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item component - a flexible flex container for titles, descriptions, and actions
|
|
5
|
+
# Matches shadcn/ui Item component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic item
|
|
8
|
+
# <%= render Shadcn::ItemComponent.new do |item| %>
|
|
9
|
+
# <% item.with_media(variant: :icon) do %>
|
|
10
|
+
# <svg>...</svg>
|
|
11
|
+
# <% end %>
|
|
12
|
+
# <% item.with_content do |content| %>
|
|
13
|
+
# <% content.with_title { "Item Title" } %>
|
|
14
|
+
# <% content.with_description { "Item description" } %>
|
|
15
|
+
# <% end %>
|
|
16
|
+
# <% item.with_actions do %>
|
|
17
|
+
# <%= render Shadcn::ButtonComponent.new(size: :sm) { "Action" } %>
|
|
18
|
+
# <% end %>
|
|
19
|
+
# <% end %>
|
|
20
|
+
#
|
|
21
|
+
# @example With variants
|
|
22
|
+
# <%= render Shadcn::ItemComponent.new(variant: :outline) do |item| %>
|
|
23
|
+
# ...
|
|
24
|
+
# <% end %>
|
|
25
|
+
#
|
|
26
|
+
class ItemComponent < BaseComponent
|
|
27
|
+
VARIANTS = {
|
|
28
|
+
default: "bg-transparent",
|
|
29
|
+
outline: "border border-border rounded-lg",
|
|
30
|
+
muted: "bg-muted/50 rounded-lg"
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
SIZES = {
|
|
34
|
+
default: "p-4 gap-4",
|
|
35
|
+
sm: "p-3 gap-3"
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
BASE_CLASSES = "flex items-start"
|
|
39
|
+
|
|
40
|
+
# Media slot for icons, images, or avatars
|
|
41
|
+
renders_one :media, lambda { |variant: :default, **options|
|
|
42
|
+
ItemMediaComponent.new(variant: variant, **options)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Content slot for title and description
|
|
46
|
+
renders_one :content_slot, lambda { |**options|
|
|
47
|
+
ItemContentComponent.new(**options)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Alias for more intuitive API
|
|
51
|
+
alias_method :with_content, :with_content_slot
|
|
52
|
+
|
|
53
|
+
# Actions slot for buttons
|
|
54
|
+
renders_one :actions, lambda { |**options|
|
|
55
|
+
ItemActionsComponent.new(**options)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Header slot
|
|
59
|
+
renders_one :header, lambda { |**options|
|
|
60
|
+
ItemHeaderComponent.new(**options)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Footer slot
|
|
64
|
+
renders_one :footer, lambda { |**options|
|
|
65
|
+
ItemFooterComponent.new(**options)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# @param variant [Symbol] :default, :outline, or :muted
|
|
69
|
+
# @param size [Symbol] :default or :sm
|
|
70
|
+
# @param tag [Symbol] HTML tag to use (:div, :li, :a, etc.)
|
|
71
|
+
# @param href [String] URL for link items (uses :a tag automatically)
|
|
72
|
+
def initialize(variant: :default, size: :default, tag: :div, href: nil, **options)
|
|
73
|
+
super(**options)
|
|
74
|
+
@variant = variant.to_sym
|
|
75
|
+
@size = size.to_sym
|
|
76
|
+
@tag = href ? :a : tag
|
|
77
|
+
@href = href
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def call
|
|
81
|
+
content_tag(@tag, item_content, **item_attributes)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def item_content
|
|
87
|
+
safe_join([header, media, content_slot, content, actions, footer].compact)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def item_attributes
|
|
91
|
+
attrs = {
|
|
92
|
+
class: cn(BASE_CLASSES, VARIANTS[@variant], SIZES[@size], class_name)
|
|
93
|
+
}
|
|
94
|
+
attrs[:href] = @href if @href
|
|
95
|
+
attrs.merge(html_options).merge(build_data)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Content component - wrapper for title and description
|
|
5
|
+
class ItemContentComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "flex-1 min-w-0 space-y-1"
|
|
7
|
+
|
|
8
|
+
# Title slot
|
|
9
|
+
renders_one :title, lambda { |**options|
|
|
10
|
+
ItemTitleComponent.new(**options)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Description slot
|
|
14
|
+
renders_one :description, lambda { |**options|
|
|
15
|
+
ItemDescriptionComponent.new(**options)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
content_tag(:div, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data)) do
|
|
20
|
+
safe_join([title, description, content].compact)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Description component
|
|
5
|
+
class ItemDescriptionComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "text-sm text-muted-foreground"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:p, content, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Footer component - display footer content below main content
|
|
5
|
+
class ItemFooterComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "mt-2 w-full"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Group component - container for grouping related items
|
|
5
|
+
class ItemGroupComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "flex flex-col"
|
|
7
|
+
|
|
8
|
+
# Items in the group
|
|
9
|
+
renders_many :items, lambda { |variant: :default, size: :default, **options|
|
|
10
|
+
ItemComponent.new(variant: variant, size: size, **options)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# Separators between items
|
|
14
|
+
renders_many :separators, lambda { |**options|
|
|
15
|
+
ItemSeparatorComponent.new(**options)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
content_tag(:div, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data)) do
|
|
20
|
+
safe_join([items, separators, content].flatten.compact)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Header component - display header content above main content
|
|
5
|
+
class ItemHeaderComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "mb-2"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Media component - displays icons, images, or avatars
|
|
5
|
+
class ItemMediaComponent < BaseComponent
|
|
6
|
+
VARIANTS = {
|
|
7
|
+
default: "shrink-0",
|
|
8
|
+
icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted [&>svg]:size-5 [&>svg]:text-muted-foreground",
|
|
9
|
+
image: "shrink-0 overflow-hidden rounded-lg"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
# @param variant [Symbol] :default, :icon, or :image
|
|
13
|
+
def initialize(variant: :default, **options)
|
|
14
|
+
super(**options)
|
|
15
|
+
@variant = variant.to_sym
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
content_tag(:div, content, class: merge_classes(VARIANTS[@variant]), **html_options.merge(build_data))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Separator component - visual divider between items
|
|
5
|
+
class ItemSeparatorComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "shrink-0 bg-border h-[1px] w-full"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, "", class: merge_classes(BASE_CLASSES), role: "separator", **html_options.merge(build_data))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Item Title component
|
|
5
|
+
class ItemTitleComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "text-sm font-medium leading-none"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Kbd component for displaying keyboard shortcuts
|
|
5
|
+
# Matches shadcn/ui Kbd component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic keyboard shortcut
|
|
8
|
+
# <%= render Shadcn::KbdComponent.new { "⌘" } %>
|
|
9
|
+
#
|
|
10
|
+
# @example Multiple keys
|
|
11
|
+
# <%= render Shadcn::KbdComponent.new { "⌘K" } %>
|
|
12
|
+
#
|
|
13
|
+
# @example With key combination
|
|
14
|
+
# <span class="flex items-center gap-1">
|
|
15
|
+
# <%= render Shadcn::KbdComponent.new { "Ctrl" } %>
|
|
16
|
+
# <span>+</span>
|
|
17
|
+
# <%= render Shadcn::KbdComponent.new { "C" } %>
|
|
18
|
+
# </span>
|
|
19
|
+
#
|
|
20
|
+
class KbdComponent < BaseComponent
|
|
21
|
+
BASE_CLASSES = "pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100"
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
tag.kbd(content, **kbd_attributes)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def kbd_attributes
|
|
30
|
+
attrs = { class: merge_classes(BASE_CLASSES) }
|
|
31
|
+
attrs.merge!(html_options)
|
|
32
|
+
attrs.merge!(build_data)
|
|
33
|
+
attrs.compact
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Label component for form fields
|
|
5
|
+
# Matches shadcn/ui Label component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic label
|
|
8
|
+
# <%= render Shadcn::LabelComponent.new(for: "email") { "Email Address" } %>
|
|
9
|
+
#
|
|
10
|
+
# @example Required field label
|
|
11
|
+
# <%= render Shadcn::LabelComponent.new(for: "name", required: true) { "Name" } %>
|
|
12
|
+
#
|
|
13
|
+
# @example With custom styling
|
|
14
|
+
# <%= render Shadcn::LabelComponent.new(for: "bio", class_name: "text-lg") { "Biography" } %>
|
|
15
|
+
#
|
|
16
|
+
class LabelComponent < BaseComponent
|
|
17
|
+
BASE_CLASSES = "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
18
|
+
|
|
19
|
+
# @param for [String, nil] The ID of the input this label is for
|
|
20
|
+
# @param required [Boolean] Whether to show a required indicator
|
|
21
|
+
def initialize(for: nil, required: false, **options)
|
|
22
|
+
super(**options)
|
|
23
|
+
@for = binding.local_variable_get(:for)
|
|
24
|
+
@required = required
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call
|
|
28
|
+
label_text = content
|
|
29
|
+
label_text = safe_join([label_text, required_indicator]) if @required
|
|
30
|
+
tag.label(label_text, **label_attributes)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def required_indicator
|
|
36
|
+
content_tag(:span, " *", class: "text-destructive", "aria-hidden": "true")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def label_attributes
|
|
40
|
+
attrs = {
|
|
41
|
+
for: @for,
|
|
42
|
+
class: merge_classes(BASE_CLASSES)
|
|
43
|
+
}
|
|
44
|
+
attrs.merge!(html_options)
|
|
45
|
+
attrs.merge!(build_data)
|
|
46
|
+
attrs.compact
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Checkbox Item component
|
|
5
|
+
# A menu item that can be checked/unchecked
|
|
6
|
+
class MenubarCheckboxItemComponent < 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 checked [Boolean] Whether item is checked
|
|
14
|
+
# @param disabled [Boolean] Whether item is disabled
|
|
15
|
+
def initialize(checked: false, disabled: false, **options, &block)
|
|
16
|
+
super(**options, &block)
|
|
17
|
+
@checked = checked
|
|
18
|
+
@disabled = disabled
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
content_tag(:div, item_content, item_attributes)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def item_content
|
|
28
|
+
safe_join([
|
|
29
|
+
check_indicator,
|
|
30
|
+
content,
|
|
31
|
+
shortcut
|
|
32
|
+
].compact)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def check_indicator
|
|
36
|
+
content_tag(:span, check_icon, class: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def check_icon
|
|
40
|
+
return "" unless @checked
|
|
41
|
+
|
|
42
|
+
content_tag(:svg, check_svg_path, {
|
|
43
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
44
|
+
width: "16",
|
|
45
|
+
height: "16",
|
|
46
|
+
viewBox: "0 0 24 24",
|
|
47
|
+
fill: "none",
|
|
48
|
+
stroke: "currentColor",
|
|
49
|
+
"stroke-width": "2",
|
|
50
|
+
"stroke-linecap": "round",
|
|
51
|
+
"stroke-linejoin": "round",
|
|
52
|
+
class: "h-4 w-4"
|
|
53
|
+
})
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def check_svg_path
|
|
57
|
+
content_tag(:polyline, "", points: "20 6 9 17 4 12")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def item_attributes
|
|
61
|
+
attrs = {
|
|
62
|
+
class: cn(BASE_CLASSES, class_name),
|
|
63
|
+
role: "menuitemcheckbox",
|
|
64
|
+
"aria-checked": @checked.to_s,
|
|
65
|
+
tabindex: @disabled ? nil : "-1",
|
|
66
|
+
"data-disabled": @disabled ? "" : nil,
|
|
67
|
+
"data-state": @checked ? "checked" : "unchecked",
|
|
68
|
+
"data-shadcn--menubar-target": "item",
|
|
69
|
+
"data-action": "click->shadcn--menubar#toggleCheckbox"
|
|
70
|
+
}
|
|
71
|
+
attrs.merge!(html_options)
|
|
72
|
+
attrs.merge!(build_data)
|
|
73
|
+
attrs.compact
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar component
|
|
5
|
+
# Matches shadcn/ui Menubar component
|
|
6
|
+
# A visually persistent menu common in desktop applications
|
|
7
|
+
#
|
|
8
|
+
# @example Basic menubar
|
|
9
|
+
# <%= render Shadcn::MenubarComponent.new do |menubar| %>
|
|
10
|
+
# <% menubar.with_menu do |menu| %>
|
|
11
|
+
# <% menu.with_trigger { "File" } %>
|
|
12
|
+
# <% menu.with_content do |content| %>
|
|
13
|
+
# <% content.with_item(href: "#") { "New Tab" } %>
|
|
14
|
+
# <% content.with_item(href: "#") { "New Window" } %>
|
|
15
|
+
# <% content.with_separator %>
|
|
16
|
+
# <% content.with_item(href: "#") { "Exit" } %>
|
|
17
|
+
# <% end %>
|
|
18
|
+
# <% end %>
|
|
19
|
+
# <% menubar.with_menu do |menu| %>
|
|
20
|
+
# <% menu.with_trigger { "Edit" } %>
|
|
21
|
+
# <% menu.with_content do |content| %>
|
|
22
|
+
# <% content.with_item(href: "#") { "Undo" } %>
|
|
23
|
+
# <% content.with_item(href: "#") { "Redo" } %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
# <% end %>
|
|
27
|
+
#
|
|
28
|
+
class MenubarComponent < BaseComponent
|
|
29
|
+
BASE_CLASSES = "flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-sm"
|
|
30
|
+
|
|
31
|
+
renders_many :menus, lambda { |**options|
|
|
32
|
+
MenubarMenuComponent.new(**options)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
def call
|
|
36
|
+
content_tag(:div, menubar_content, menubar_attributes)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def menubar_content
|
|
42
|
+
safe_join(menus)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def menubar_attributes
|
|
46
|
+
attrs = {
|
|
47
|
+
class: cn(BASE_CLASSES, class_name),
|
|
48
|
+
role: "menubar",
|
|
49
|
+
"data-controller": "shadcn--menubar"
|
|
50
|
+
}
|
|
51
|
+
attrs.merge!(html_options)
|
|
52
|
+
attrs.merge!(build_data)
|
|
53
|
+
attrs.compact
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Content component
|
|
5
|
+
# Container for menu items within a dropdown
|
|
6
|
+
class MenubarContentComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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 :labels, lambda { |**options, &block|
|
|
13
|
+
MenubarLabelComponent.new(**options, &block)
|
|
14
|
+
}
|
|
15
|
+
renders_many :separators, lambda { |**options|
|
|
16
|
+
MenubarSeparatorComponent.new(**options)
|
|
17
|
+
}
|
|
18
|
+
renders_many :checkbox_items, lambda { |**options, &block|
|
|
19
|
+
MenubarCheckboxItemComponent.new(**options, &block)
|
|
20
|
+
}
|
|
21
|
+
renders_many :radio_groups, lambda { |**options, &block|
|
|
22
|
+
MenubarRadioGroupComponent.new(**options, &block)
|
|
23
|
+
}
|
|
24
|
+
renders_many :sub_menus, lambda { |**options, &block|
|
|
25
|
+
MenubarSubComponent.new(**options, &block)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# @param align [Symbol] Content alignment (:start, :center, :end)
|
|
29
|
+
# @param side_offset [Integer] Offset from trigger
|
|
30
|
+
def initialize(align: :start, side_offset: 4, **options)
|
|
31
|
+
super(**options)
|
|
32
|
+
@align = align
|
|
33
|
+
@side_offset = side_offset
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def call
|
|
37
|
+
content_tag(:div, menu_content, content_attributes)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def menu_content
|
|
43
|
+
if items.any? || labels.any? || separators.any? || checkbox_items.any? || radio_groups.any? || sub_menus.any?
|
|
44
|
+
safe_join([labels, items, separators, checkbox_items, radio_groups, sub_menus, content].flatten.compact)
|
|
45
|
+
else
|
|
46
|
+
content
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def content_attributes
|
|
51
|
+
attrs = {
|
|
52
|
+
class: cn(BASE_CLASSES, class_name),
|
|
53
|
+
role: "menu",
|
|
54
|
+
"aria-orientation": "vertical",
|
|
55
|
+
"data-state": "closed",
|
|
56
|
+
"data-shadcn--menubar-target": "content",
|
|
57
|
+
hidden: true
|
|
58
|
+
}
|
|
59
|
+
attrs.merge!(html_options)
|
|
60
|
+
attrs.merge!(build_data)
|
|
61
|
+
attrs.compact
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Item component
|
|
5
|
+
# Individual menu item within menubar content
|
|
6
|
+
class MenubarItemComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "relative 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-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
|
8
|
+
|
|
9
|
+
VARIANTS = {
|
|
10
|
+
default: "",
|
|
11
|
+
destructive: "text-destructive focus:bg-destructive focus:text-destructive-foreground"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
renders_one :shortcut, lambda { |**options|
|
|
15
|
+
MenubarShortcutComponent.new(**options)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# @param href [String, nil] Link URL
|
|
19
|
+
# @param variant [Symbol] Item variant (:default, :destructive)
|
|
20
|
+
# @param disabled [Boolean] Whether item is disabled
|
|
21
|
+
# @param inset [Boolean] Whether to add left padding for icons
|
|
22
|
+
def initialize(href: nil, variant: :default, disabled: false, inset: false, **options, &block)
|
|
23
|
+
super(**options, &block)
|
|
24
|
+
@href = href
|
|
25
|
+
@variant = variant.to_sym
|
|
26
|
+
@disabled = disabled
|
|
27
|
+
@inset = inset
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def call
|
|
31
|
+
tag_name = @href ? :a : :div
|
|
32
|
+
content_tag(tag_name, item_content, item_attributes)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def item_content
|
|
38
|
+
safe_join([content, shortcut].compact)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def item_classes
|
|
42
|
+
cn(
|
|
43
|
+
BASE_CLASSES,
|
|
44
|
+
VARIANTS[@variant],
|
|
45
|
+
@inset ? "pl-8" : "",
|
|
46
|
+
class_name
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def item_attributes
|
|
51
|
+
attrs = {
|
|
52
|
+
class: item_classes,
|
|
53
|
+
role: "menuitem",
|
|
54
|
+
tabindex: @disabled ? nil : "-1",
|
|
55
|
+
href: @href,
|
|
56
|
+
"data-disabled": @disabled ? "" : nil,
|
|
57
|
+
"data-shadcn--menubar-target": "item",
|
|
58
|
+
"data-action": "click->shadcn--menubar#selectItem"
|
|
59
|
+
}
|
|
60
|
+
attrs.merge!(html_options)
|
|
61
|
+
attrs.merge!(build_data)
|
|
62
|
+
attrs.compact
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Label component
|
|
5
|
+
# Label for grouping menu items
|
|
6
|
+
class MenubarLabelComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "px-2 py-1.5 text-sm font-semibold"
|
|
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, content, label_attributes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def label_attributes
|
|
22
|
+
{
|
|
23
|
+
class: cn(BASE_CLASSES, @inset ? "pl-8" : "", class_name)
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Menubar Menu component
|
|
5
|
+
# Individual menu section within a menubar
|
|
6
|
+
class MenubarMenuComponent < BaseComponent
|
|
7
|
+
renders_one :trigger, lambda { |**options|
|
|
8
|
+
MenubarTriggerComponent.new(**options)
|
|
9
|
+
}
|
|
10
|
+
renders_one :menu, lambda { |**options|
|
|
11
|
+
MenubarContentComponent.new(**options)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
content_tag(:div, menu_body, menu_attributes)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def menu_body
|
|
21
|
+
safe_join([trigger, menu].compact)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def menu_attributes
|
|
25
|
+
attrs = {
|
|
26
|
+
class: cn("relative", class_name),
|
|
27
|
+
"data-shadcn--menubar-target": "menu"
|
|
28
|
+
}
|
|
29
|
+
attrs.merge!(html_options)
|
|
30
|
+
attrs.merge!(build_data)
|
|
31
|
+
attrs.compact
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|