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,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Sidebar component for application layouts
|
|
5
|
+
# Matches shadcn/ui Sidebar component
|
|
6
|
+
# A composable, themeable sidebar with collapsible state
|
|
7
|
+
#
|
|
8
|
+
# @example Basic sidebar
|
|
9
|
+
# <%= render Shadcn::SidebarComponent.new do |sidebar| %>
|
|
10
|
+
# <% sidebar.with_header do %>
|
|
11
|
+
# <div class="flex items-center gap-2 px-4">
|
|
12
|
+
# <span class="font-semibold">App Name</span>
|
|
13
|
+
# </div>
|
|
14
|
+
# <% end %>
|
|
15
|
+
# <% sidebar.with_sidebar_content do |sc| %>
|
|
16
|
+
# <% sc.with_group do |group| %>
|
|
17
|
+
# <% group.with_label { "Platform" } %>
|
|
18
|
+
# <% group.with_group_content do |gc| %>
|
|
19
|
+
# <% gc.with_menu do |menu| %>
|
|
20
|
+
# <% menu.with_item do |item| %>
|
|
21
|
+
# <% item.with_button(href: "/dashboard") do %>
|
|
22
|
+
# Dashboard
|
|
23
|
+
# <% end %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
# <% end %>
|
|
27
|
+
# <% end %>
|
|
28
|
+
# <% end %>
|
|
29
|
+
# <% sidebar.with_footer do %>
|
|
30
|
+
# User info here
|
|
31
|
+
# <% end %>
|
|
32
|
+
# <% end %>
|
|
33
|
+
#
|
|
34
|
+
class SidebarComponent < BaseComponent
|
|
35
|
+
SIDEBAR_WIDTH = "16rem"
|
|
36
|
+
SIDEBAR_WIDTH_MOBILE = "18rem"
|
|
37
|
+
SIDEBAR_WIDTH_ICON = "3rem"
|
|
38
|
+
|
|
39
|
+
SIDES = {
|
|
40
|
+
left: "left",
|
|
41
|
+
right: "right"
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
VARIANTS = {
|
|
45
|
+
sidebar: "sidebar",
|
|
46
|
+
floating: "floating",
|
|
47
|
+
inset: "inset"
|
|
48
|
+
}.freeze
|
|
49
|
+
|
|
50
|
+
COLLAPSIBLE_MODES = {
|
|
51
|
+
offcanvas: "offcanvas",
|
|
52
|
+
icon: "icon",
|
|
53
|
+
none: "none"
|
|
54
|
+
}.freeze
|
|
55
|
+
|
|
56
|
+
# Slots
|
|
57
|
+
renders_one :header, lambda { |**options|
|
|
58
|
+
SidebarHeaderComponent.new(**options)
|
|
59
|
+
}
|
|
60
|
+
renders_one :sidebar_content, lambda { |**options|
|
|
61
|
+
SidebarContentComponent.new(**options)
|
|
62
|
+
}
|
|
63
|
+
renders_one :footer, lambda { |**options|
|
|
64
|
+
SidebarFooterComponent.new(**options)
|
|
65
|
+
}
|
|
66
|
+
renders_one :rail, lambda { |**options|
|
|
67
|
+
SidebarRailComponent.new(**options)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# @param side [Symbol] Side to show sidebar (:left, :right)
|
|
71
|
+
# @param variant [Symbol] Visual style (:sidebar, :floating, :inset)
|
|
72
|
+
# @param collapsible [Symbol] Collapse mode (:offcanvas, :icon, :none)
|
|
73
|
+
# @param default_open [Boolean] Whether sidebar starts open
|
|
74
|
+
def initialize(
|
|
75
|
+
side: :left,
|
|
76
|
+
variant: :sidebar,
|
|
77
|
+
collapsible: :offcanvas,
|
|
78
|
+
default_open: true,
|
|
79
|
+
**options
|
|
80
|
+
)
|
|
81
|
+
super(**options)
|
|
82
|
+
@side = side.to_sym
|
|
83
|
+
@variant = variant.to_sym
|
|
84
|
+
@collapsible = collapsible.to_sym
|
|
85
|
+
@default_open = default_open
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def call
|
|
89
|
+
content_tag(:aside, sidebar_content, sidebar_attributes)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def sidebar_content
|
|
95
|
+
safe_join([
|
|
96
|
+
sidebar_inner
|
|
97
|
+
].compact)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def sidebar_inner
|
|
101
|
+
content_tag(:div, inner_content, inner_attributes)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def inner_content
|
|
105
|
+
safe_join([header, sidebar_content, footer, rail].compact)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def inner_attributes
|
|
109
|
+
{
|
|
110
|
+
class: cn(
|
|
111
|
+
"flex h-full w-full flex-col bg-sidebar",
|
|
112
|
+
"group-data-[collapsible=icon]:overflow-hidden"
|
|
113
|
+
),
|
|
114
|
+
"data-shadcn--sidebar-target": "inner"
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def sidebar_attributes
|
|
119
|
+
attrs = {
|
|
120
|
+
class: cn(sidebar_classes, class_name),
|
|
121
|
+
"data-controller": "shadcn--sidebar",
|
|
122
|
+
"data-side": @side.to_s,
|
|
123
|
+
"data-variant": @variant.to_s,
|
|
124
|
+
"data-collapsible": @collapsible.to_s,
|
|
125
|
+
"data-state": @default_open ? "expanded" : "collapsed",
|
|
126
|
+
"data-shadcn--sidebar-open-value": @default_open.to_s,
|
|
127
|
+
"data-shadcn--sidebar-side-value": @side.to_s,
|
|
128
|
+
"data-shadcn--sidebar-variant-value": @variant.to_s,
|
|
129
|
+
"data-shadcn--sidebar-collapsible-value": @collapsible.to_s,
|
|
130
|
+
"data-action": "keydown.ctrl+b@window->shadcn--sidebar#toggle keydown.meta+b@window->shadcn--sidebar#toggle"
|
|
131
|
+
}
|
|
132
|
+
attrs.merge!(html_options)
|
|
133
|
+
attrs.merge!(build_data)
|
|
134
|
+
attrs.compact
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def sidebar_classes
|
|
138
|
+
cn(
|
|
139
|
+
# Base styles
|
|
140
|
+
"group peer hidden md:block text-sidebar-foreground",
|
|
141
|
+
# Fixed positioning for sidebar variant
|
|
142
|
+
@variant == :sidebar && "fixed inset-y-0 z-10 h-svh",
|
|
143
|
+
# Side-specific positioning
|
|
144
|
+
@side == :left && "left-0",
|
|
145
|
+
@side == :right && "right-0",
|
|
146
|
+
# Width handling
|
|
147
|
+
"w-[--sidebar-width]",
|
|
148
|
+
# Transition
|
|
149
|
+
"transition-[left,right,width] duration-200 ease-linear",
|
|
150
|
+
# Collapsed state styles
|
|
151
|
+
"group-data-[collapsible=offcanvas]:w-0",
|
|
152
|
+
"group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
|
|
153
|
+
# Variant-specific styles
|
|
154
|
+
variant_classes,
|
|
155
|
+
# Side-specific collapsed offset
|
|
156
|
+
collapsed_offset_classes
|
|
157
|
+
)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def variant_classes
|
|
161
|
+
case @variant
|
|
162
|
+
when :floating
|
|
163
|
+
"p-2"
|
|
164
|
+
when :inset
|
|
165
|
+
"border-r bg-transparent"
|
|
166
|
+
else
|
|
167
|
+
"border-r"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def collapsed_offset_classes
|
|
172
|
+
case @side
|
|
173
|
+
when :left
|
|
174
|
+
"group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
|
175
|
+
when :right
|
|
176
|
+
"group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarContent component - scrollable content area for sidebar
|
|
5
|
+
class SidebarContentComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden"
|
|
7
|
+
|
|
8
|
+
renders_many :groups, lambda { |**options|
|
|
9
|
+
SidebarGroupComponent.new(**options)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:div, content_structure, content_attributes)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def content_structure
|
|
19
|
+
groups.any? ? safe_join(groups) : content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def content_attributes
|
|
23
|
+
attrs = {
|
|
24
|
+
class: cn(BASE_CLASSES, class_name),
|
|
25
|
+
"data-sidebar": "content"
|
|
26
|
+
}
|
|
27
|
+
attrs.merge!(html_options)
|
|
28
|
+
attrs.merge!(build_data)
|
|
29
|
+
attrs.compact
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarFooter component - sticky footer section for sidebar
|
|
5
|
+
class SidebarFooterComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "flex flex-col gap-2 p-2"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, footer_attributes)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def footer_attributes
|
|
15
|
+
attrs = {
|
|
16
|
+
class: cn(BASE_CLASSES, class_name),
|
|
17
|
+
"data-sidebar": "footer"
|
|
18
|
+
}
|
|
19
|
+
attrs.merge!(html_options)
|
|
20
|
+
attrs.merge!(build_data)
|
|
21
|
+
attrs.compact
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarGroupAction component - action button for a sidebar group
|
|
5
|
+
class SidebarGroupActionComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
|
7
|
+
COLLAPSED_CLASSES = "group-data-[collapsible=icon]:hidden"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:button, content, action_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def action_attributes
|
|
16
|
+
attrs = {
|
|
17
|
+
type: "button",
|
|
18
|
+
class: cn(BASE_CLASSES, COLLAPSED_CLASSES, class_name),
|
|
19
|
+
"data-sidebar": "group-action"
|
|
20
|
+
}
|
|
21
|
+
attrs.merge!(html_options)
|
|
22
|
+
attrs.merge!(build_data)
|
|
23
|
+
attrs.compact
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarGroup component - groups related sidebar items
|
|
5
|
+
class SidebarGroupComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "relative flex w-full min-w-0 flex-col p-2"
|
|
7
|
+
|
|
8
|
+
renders_one :label, lambda { |**options|
|
|
9
|
+
SidebarGroupLabelComponent.new(**options)
|
|
10
|
+
}
|
|
11
|
+
renders_one :action, lambda { |**options|
|
|
12
|
+
SidebarGroupActionComponent.new(**options)
|
|
13
|
+
}
|
|
14
|
+
renders_one :group_content, lambda { |**options|
|
|
15
|
+
SidebarGroupContentComponent.new(**options)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
content_tag(:div, group_content_structure, group_attributes)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def group_content_structure
|
|
25
|
+
safe_join([label, action, group_content, content].compact)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def group_attributes
|
|
29
|
+
attrs = {
|
|
30
|
+
class: cn(BASE_CLASSES, class_name),
|
|
31
|
+
"data-sidebar": "group"
|
|
32
|
+
}
|
|
33
|
+
attrs.merge!(html_options)
|
|
34
|
+
attrs.merge!(build_data)
|
|
35
|
+
attrs.compact
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarGroupContent component - content container for a sidebar group
|
|
5
|
+
class SidebarGroupContentComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "w-full text-sm"
|
|
7
|
+
|
|
8
|
+
renders_many :menus, lambda { |**options|
|
|
9
|
+
SidebarMenuComponent.new(**options)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:div, content_structure, content_attributes)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def content_structure
|
|
19
|
+
menus.any? ? safe_join(menus) : content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def content_attributes
|
|
23
|
+
attrs = {
|
|
24
|
+
class: cn(BASE_CLASSES, class_name),
|
|
25
|
+
"data-sidebar": "group-content"
|
|
26
|
+
}
|
|
27
|
+
attrs.merge!(html_options)
|
|
28
|
+
attrs.merge!(build_data)
|
|
29
|
+
attrs.compact
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarGroupLabel component - label for a sidebar group
|
|
5
|
+
class SidebarGroupLabelComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0"
|
|
7
|
+
COLLAPSED_CLASSES = "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:div, content, label_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def label_attributes
|
|
16
|
+
attrs = {
|
|
17
|
+
class: cn(BASE_CLASSES, COLLAPSED_CLASSES, class_name),
|
|
18
|
+
"data-sidebar": "group-label"
|
|
19
|
+
}
|
|
20
|
+
attrs.merge!(html_options)
|
|
21
|
+
attrs.merge!(build_data)
|
|
22
|
+
attrs.compact
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarHeader component - sticky header section for sidebar
|
|
5
|
+
class SidebarHeaderComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "flex flex-col gap-2 p-2"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, header_attributes)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def header_attributes
|
|
15
|
+
attrs = {
|
|
16
|
+
class: cn(BASE_CLASSES, class_name),
|
|
17
|
+
"data-sidebar": "header"
|
|
18
|
+
}
|
|
19
|
+
attrs.merge!(html_options)
|
|
20
|
+
attrs.merge!(build_data)
|
|
21
|
+
attrs.compact
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarInset component - main content area next to sidebar
|
|
5
|
+
class SidebarInsetComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "relative flex min-h-svh flex-1 flex-col bg-background"
|
|
7
|
+
PEER_CLASSES = "peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:main, content, inset_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def inset_attributes
|
|
16
|
+
attrs = {
|
|
17
|
+
class: cn(BASE_CLASSES, PEER_CLASSES, class_name),
|
|
18
|
+
"data-sidebar": "inset"
|
|
19
|
+
}
|
|
20
|
+
attrs.merge!(html_options)
|
|
21
|
+
attrs.merge!(build_data)
|
|
22
|
+
attrs.compact
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuAction component - action button within menu item
|
|
5
|
+
class SidebarMenuActionComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0"
|
|
7
|
+
COLLAPSED_CLASSES = "group-data-[collapsible=icon]:hidden"
|
|
8
|
+
SHOW_ON_HOVER_CLASSES = "after:absolute after:-inset-2 after:md:hidden group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0"
|
|
9
|
+
|
|
10
|
+
def initialize(show_on_hover: false, **options)
|
|
11
|
+
super(**options)
|
|
12
|
+
@show_on_hover = show_on_hover
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
content_tag(:button, content, action_attributes)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def action_attributes
|
|
22
|
+
attrs = {
|
|
23
|
+
type: "button",
|
|
24
|
+
class: cn(
|
|
25
|
+
BASE_CLASSES,
|
|
26
|
+
COLLAPSED_CLASSES,
|
|
27
|
+
@show_on_hover ? SHOW_ON_HOVER_CLASSES : nil,
|
|
28
|
+
class_name
|
|
29
|
+
),
|
|
30
|
+
"data-sidebar": "menu-action"
|
|
31
|
+
}
|
|
32
|
+
attrs.merge!(html_options)
|
|
33
|
+
attrs.merge!(build_data)
|
|
34
|
+
attrs.compact
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuBadge component - badge within menu item
|
|
5
|
+
class SidebarMenuBadgeComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none"
|
|
7
|
+
COLLAPSED_CLASSES = "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-data-[collapsible=icon]:hidden"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:span, content, badge_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def badge_attributes
|
|
16
|
+
attrs = {
|
|
17
|
+
class: cn(BASE_CLASSES, COLLAPSED_CLASSES, class_name),
|
|
18
|
+
"data-sidebar": "menu-badge"
|
|
19
|
+
}
|
|
20
|
+
attrs.merge!(html_options)
|
|
21
|
+
attrs.merge!(build_data)
|
|
22
|
+
attrs.compact
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuButton component - button/link within menu item
|
|
5
|
+
class SidebarMenuButtonComponent < BaseComponent
|
|
6
|
+
VARIANTS = {
|
|
7
|
+
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
8
|
+
outline: "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
SIZES = {
|
|
12
|
+
default: "h-8 text-sm",
|
|
13
|
+
sm: "h-7 text-xs",
|
|
14
|
+
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0"
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
BASE_CLASSES = "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0"
|
|
18
|
+
|
|
19
|
+
def initialize(variant: :default, size: :default, is_active: false, as_child: false, tooltip: nil, href: nil, **options)
|
|
20
|
+
super(**options)
|
|
21
|
+
@variant = variant.to_sym
|
|
22
|
+
@size = size.to_sym
|
|
23
|
+
@is_active = is_active
|
|
24
|
+
@as_child = as_child
|
|
25
|
+
@tooltip = tooltip
|
|
26
|
+
@href = href
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def call
|
|
30
|
+
if @href
|
|
31
|
+
content_tag(:a, content, button_attributes.merge(href: @href))
|
|
32
|
+
else
|
|
33
|
+
content_tag(:button, content, button_attributes.merge(type: "button"))
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def button_attributes
|
|
40
|
+
attrs = {
|
|
41
|
+
class: cn(BASE_CLASSES, VARIANTS[@variant], SIZES[@size], class_name),
|
|
42
|
+
"data-sidebar": "menu-button",
|
|
43
|
+
"data-size": @size,
|
|
44
|
+
"data-active": @is_active
|
|
45
|
+
}
|
|
46
|
+
attrs[:"data-tooltip"] = @tooltip if @tooltip
|
|
47
|
+
attrs.merge!(html_options)
|
|
48
|
+
attrs.merge!(build_data)
|
|
49
|
+
attrs.compact
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenu component - menu container for sidebar items
|
|
5
|
+
class SidebarMenuComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "flex w-full min-w-0 flex-col gap-1"
|
|
7
|
+
|
|
8
|
+
renders_many :items, lambda { |**options|
|
|
9
|
+
SidebarMenuItemComponent.new(**options)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:ul, menu_content, menu_attributes)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def menu_content
|
|
19
|
+
items.any? ? safe_join(items) : content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def menu_attributes
|
|
23
|
+
attrs = {
|
|
24
|
+
class: cn(BASE_CLASSES, class_name),
|
|
25
|
+
"data-sidebar": "menu"
|
|
26
|
+
}
|
|
27
|
+
attrs.merge!(html_options)
|
|
28
|
+
attrs.merge!(build_data)
|
|
29
|
+
attrs.compact
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuItem component - individual menu item
|
|
5
|
+
class SidebarMenuItemComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "group/menu-item relative"
|
|
7
|
+
|
|
8
|
+
renders_one :button, lambda { |**options|
|
|
9
|
+
SidebarMenuButtonComponent.new(**options)
|
|
10
|
+
}
|
|
11
|
+
renders_one :action, lambda { |**options|
|
|
12
|
+
SidebarMenuActionComponent.new(**options)
|
|
13
|
+
}
|
|
14
|
+
renders_one :badge, lambda { |**options|
|
|
15
|
+
SidebarMenuBadgeComponent.new(**options)
|
|
16
|
+
}
|
|
17
|
+
renders_one :sub, lambda { |**options|
|
|
18
|
+
SidebarMenuSubComponent.new(**options)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
content_tag(:li, item_content, item_attributes)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def item_content
|
|
28
|
+
safe_join([button, action, badge, sub, content].compact)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def item_attributes
|
|
32
|
+
attrs = {
|
|
33
|
+
class: cn(BASE_CLASSES, class_name),
|
|
34
|
+
"data-sidebar": "menu-item"
|
|
35
|
+
}
|
|
36
|
+
attrs.merge!(html_options)
|
|
37
|
+
attrs.merge!(build_data)
|
|
38
|
+
attrs.compact
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuSkeleton component - loading skeleton for menu item
|
|
5
|
+
class SidebarMenuSkeletonComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "rounded-md h-8 flex gap-2 px-2 items-center"
|
|
7
|
+
|
|
8
|
+
def initialize(show_icon: false, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@show_icon = show_icon
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
content_tag(:div, skeleton_content, skeleton_attributes)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def skeleton_content
|
|
20
|
+
safe_join([
|
|
21
|
+
@show_icon ? icon_skeleton : nil,
|
|
22
|
+
text_skeleton
|
|
23
|
+
].compact)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def icon_skeleton
|
|
27
|
+
content_tag(:span, "", class: "size-4 rounded-md bg-sidebar-accent animate-pulse")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def text_skeleton
|
|
31
|
+
# Random width between 60-80%
|
|
32
|
+
width = rand(60..80)
|
|
33
|
+
content_tag(:span, "", class: "h-4 flex-1 bg-sidebar-accent animate-pulse rounded-md", style: "max-width: #{width}%")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def skeleton_attributes
|
|
37
|
+
attrs = {
|
|
38
|
+
class: cn(BASE_CLASSES, class_name),
|
|
39
|
+
"data-sidebar": "menu-skeleton"
|
|
40
|
+
}
|
|
41
|
+
attrs.merge!(html_options)
|
|
42
|
+
attrs.merge!(build_data)
|
|
43
|
+
attrs.compact
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|