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,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuSubButton component - button within submenu item
|
|
5
|
+
class SidebarMenuSubButtonComponent < BaseComponent
|
|
6
|
+
SIZES = {
|
|
7
|
+
sm: "text-xs",
|
|
8
|
+
md: "text-sm"
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
BASE_CLASSES = "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring 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 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground"
|
|
12
|
+
ACTIVE_CLASSES = "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground"
|
|
13
|
+
|
|
14
|
+
def initialize(size: :md, is_active: false, href: nil, **options)
|
|
15
|
+
super(**options)
|
|
16
|
+
@size = size.to_sym
|
|
17
|
+
@is_active = is_active
|
|
18
|
+
@href = href
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
if @href
|
|
23
|
+
content_tag(:a, content, button_attributes.merge(href: @href))
|
|
24
|
+
else
|
|
25
|
+
content_tag(:button, content, button_attributes.merge(type: "button"))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def button_attributes
|
|
32
|
+
attrs = {
|
|
33
|
+
class: cn(BASE_CLASSES, ACTIVE_CLASSES, SIZES[@size], class_name),
|
|
34
|
+
"data-sidebar": "menu-sub-button",
|
|
35
|
+
"data-size": @size,
|
|
36
|
+
"data-active": @is_active
|
|
37
|
+
}
|
|
38
|
+
attrs.merge!(html_options)
|
|
39
|
+
attrs.merge!(build_data)
|
|
40
|
+
attrs.compact
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuSub component - submenu container
|
|
5
|
+
class SidebarMenuSubComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5"
|
|
7
|
+
COLLAPSED_CLASSES = "group-data-[collapsible=icon]:hidden"
|
|
8
|
+
|
|
9
|
+
renders_many :items, lambda { |**options|
|
|
10
|
+
SidebarMenuSubItemComponent.new(**options)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
content_tag(:ul, sub_content, sub_attributes)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def sub_content
|
|
20
|
+
items.any? ? safe_join(items) : content
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def sub_attributes
|
|
24
|
+
attrs = {
|
|
25
|
+
class: cn(BASE_CLASSES, COLLAPSED_CLASSES, class_name),
|
|
26
|
+
"data-sidebar": "menu-sub"
|
|
27
|
+
}
|
|
28
|
+
attrs.merge!(html_options)
|
|
29
|
+
attrs.merge!(build_data)
|
|
30
|
+
attrs.compact
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarMenuSubItem component - submenu item
|
|
5
|
+
class SidebarMenuSubItemComponent < BaseComponent
|
|
6
|
+
renders_one :button, lambda { |**options|
|
|
7
|
+
SidebarMenuSubButtonComponent.new(**options)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
content_tag(:li, item_content, item_attributes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def item_content
|
|
17
|
+
button || content
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def item_attributes
|
|
21
|
+
attrs = {
|
|
22
|
+
"data-sidebar": "menu-sub-item"
|
|
23
|
+
}
|
|
24
|
+
attrs[:class] = class_name if class_name.present?
|
|
25
|
+
attrs.merge!(html_options)
|
|
26
|
+
attrs.merge!(build_data)
|
|
27
|
+
attrs.compact
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarProvider component - wrapper that provides sidebar context and state
|
|
5
|
+
class SidebarProviderComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar"
|
|
7
|
+
|
|
8
|
+
renders_one :sidebar, lambda { |**options|
|
|
9
|
+
SidebarComponent.new(**options)
|
|
10
|
+
}
|
|
11
|
+
renders_one :inset, lambda { |**options|
|
|
12
|
+
SidebarInsetComponent.new(**options)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def initialize(default_open: true, open: nil, keyboard_shortcut: "b", **options)
|
|
16
|
+
super(**options)
|
|
17
|
+
@default_open = default_open
|
|
18
|
+
@open = open
|
|
19
|
+
@keyboard_shortcut = keyboard_shortcut
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
content_tag(:div, provider_content, provider_attributes)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def provider_content
|
|
29
|
+
safe_join([sidebar, inset, content].compact)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def provider_attributes
|
|
33
|
+
attrs = {
|
|
34
|
+
class: cn(BASE_CLASSES, class_name),
|
|
35
|
+
style: sidebar_style,
|
|
36
|
+
"data-controller": "shadcn--sidebar",
|
|
37
|
+
"data-shadcn--sidebar-open-value": initial_open_state,
|
|
38
|
+
"data-shadcn--sidebar-keyboard-shortcut-value": @keyboard_shortcut
|
|
39
|
+
}
|
|
40
|
+
attrs.merge!(html_options)
|
|
41
|
+
attrs.merge!(build_data)
|
|
42
|
+
attrs.compact
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def sidebar_style
|
|
46
|
+
[
|
|
47
|
+
"--sidebar-width: 16rem",
|
|
48
|
+
"--sidebar-width-icon: 3rem"
|
|
49
|
+
].join("; ")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def initial_open_state
|
|
53
|
+
# Use explicit open prop if provided, otherwise use default_open
|
|
54
|
+
@open.nil? ? @default_open : @open
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarRail component - thin interactive rail for expanding collapsed sidebar
|
|
5
|
+
class SidebarRailComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex"
|
|
7
|
+
HOVER_EXPAND_CLASSES = "[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize [[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar [[data-side=left][data-collapsible=offcanvas]_&]:-right-2 [[data-side=right][data-collapsible=offcanvas]_&]:-left-2"
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
content_tag(:button, nil, rail_attributes)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def rail_attributes
|
|
16
|
+
attrs = {
|
|
17
|
+
type: "button",
|
|
18
|
+
class: cn(BASE_CLASSES, HOVER_EXPAND_CLASSES, class_name),
|
|
19
|
+
tabindex: "-1",
|
|
20
|
+
"aria-label": "Toggle Sidebar",
|
|
21
|
+
title: "Toggle Sidebar",
|
|
22
|
+
"data-sidebar": "rail",
|
|
23
|
+
"data-action": "click->shadcn--sidebar#toggle"
|
|
24
|
+
}
|
|
25
|
+
attrs.merge!(html_options)
|
|
26
|
+
attrs.merge!(build_data)
|
|
27
|
+
attrs.compact
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarSeparator component - visual separator for sidebar
|
|
5
|
+
class SidebarSeparatorComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "mx-2 w-auto bg-sidebar-border"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:hr, nil, separator_attributes)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def separator_attributes
|
|
15
|
+
attrs = {
|
|
16
|
+
class: cn(BASE_CLASSES, class_name),
|
|
17
|
+
"data-sidebar": "separator"
|
|
18
|
+
}
|
|
19
|
+
attrs.merge!(html_options)
|
|
20
|
+
attrs.merge!(build_data)
|
|
21
|
+
attrs.compact
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# SidebarTrigger component - button to toggle sidebar open/closed
|
|
5
|
+
class SidebarTriggerComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "h-7 w-7"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
render ButtonComponent.new(
|
|
10
|
+
variant: :ghost,
|
|
11
|
+
size: :icon,
|
|
12
|
+
class_name: cn(BASE_CLASSES, class_name),
|
|
13
|
+
data: { sidebar: "trigger", action: "click->shadcn--sidebar#toggle" },
|
|
14
|
+
**html_options
|
|
15
|
+
) do
|
|
16
|
+
trigger_content
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def trigger_content
|
|
23
|
+
if content?
|
|
24
|
+
content
|
|
25
|
+
else
|
|
26
|
+
default_icon
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def default_icon
|
|
31
|
+
# PanelLeft icon from Lucide
|
|
32
|
+
content_tag(:svg, nil,
|
|
33
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
34
|
+
width: "24",
|
|
35
|
+
height: "24",
|
|
36
|
+
viewBox: "0 0 24 24",
|
|
37
|
+
fill: "none",
|
|
38
|
+
stroke: "currentColor",
|
|
39
|
+
"stroke-width": "2",
|
|
40
|
+
"stroke-linecap": "round",
|
|
41
|
+
"stroke-linejoin": "round",
|
|
42
|
+
class: "size-4"
|
|
43
|
+
) do
|
|
44
|
+
safe_join([
|
|
45
|
+
content_tag(:rect, nil, width: "18", height: "18", x: "3", y: "3", rx: "2"),
|
|
46
|
+
content_tag(:path, nil, d: "M9 3v18")
|
|
47
|
+
])
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Skeleton component for loading placeholders
|
|
5
|
+
# Matches shadcn/ui Skeleton component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic skeleton
|
|
8
|
+
# <%= render Shadcn::SkeletonComponent.new(class_name: "h-4 w-[250px]") %>
|
|
9
|
+
#
|
|
10
|
+
# @example Circle skeleton (for avatars)
|
|
11
|
+
# <%= render Shadcn::SkeletonComponent.new(class_name: "h-12 w-12 rounded-full") %>
|
|
12
|
+
#
|
|
13
|
+
# @example Card skeleton
|
|
14
|
+
# <div class="flex flex-col space-y-3">
|
|
15
|
+
# <%= render Shadcn::SkeletonComponent.new(class_name: "h-[125px] w-[250px] rounded-xl") %>
|
|
16
|
+
# <div class="space-y-2">
|
|
17
|
+
# <%= render Shadcn::SkeletonComponent.new(class_name: "h-4 w-[250px]") %>
|
|
18
|
+
# <%= render Shadcn::SkeletonComponent.new(class_name: "h-4 w-[200px]") %>
|
|
19
|
+
# </div>
|
|
20
|
+
# </div>
|
|
21
|
+
#
|
|
22
|
+
class SkeletonComponent < BaseComponent
|
|
23
|
+
BASE_CLASSES = "animate-pulse rounded-md bg-primary/10"
|
|
24
|
+
|
|
25
|
+
def call
|
|
26
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Slider component for selecting values within a range
|
|
5
|
+
# Uses a native range input with custom styling for reliability
|
|
6
|
+
# Matches shadcn/ui Slider component appearance
|
|
7
|
+
#
|
|
8
|
+
# @example Basic usage
|
|
9
|
+
# <%= render Shadcn::SliderComponent.new(name: "volume", value: 50, max: 100) %>
|
|
10
|
+
#
|
|
11
|
+
# @example With step
|
|
12
|
+
# <%= render Shadcn::SliderComponent.new(name: "rating", value: 3, min: 1, max: 5, step: 1) %>
|
|
13
|
+
#
|
|
14
|
+
class SliderComponent < BaseComponent
|
|
15
|
+
BASE_CLASSES = "shadcn-slider w-full h-1.5 rounded-full appearance-none cursor-pointer bg-primary/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
|
|
16
|
+
|
|
17
|
+
# @param name [String] Input name attribute
|
|
18
|
+
# @param value [Numeric] Current value
|
|
19
|
+
# @param min [Numeric] Minimum value
|
|
20
|
+
# @param max [Numeric] Maximum value
|
|
21
|
+
# @param step [Numeric] Step increment
|
|
22
|
+
# @param disabled [Boolean] Whether slider is disabled
|
|
23
|
+
def initialize(
|
|
24
|
+
name: nil,
|
|
25
|
+
value: 0,
|
|
26
|
+
min: 0,
|
|
27
|
+
max: 100,
|
|
28
|
+
step: 1,
|
|
29
|
+
disabled: false,
|
|
30
|
+
**options
|
|
31
|
+
)
|
|
32
|
+
super(**options)
|
|
33
|
+
@name = name
|
|
34
|
+
@value = value.to_f
|
|
35
|
+
@min = min.to_f
|
|
36
|
+
@max = max.to_f
|
|
37
|
+
@step = step.to_f
|
|
38
|
+
@disabled = disabled
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def call
|
|
42
|
+
tag(:input, input_attributes)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def input_attributes
|
|
48
|
+
attrs = {
|
|
49
|
+
type: "range",
|
|
50
|
+
name: @name,
|
|
51
|
+
value: @value,
|
|
52
|
+
min: @min,
|
|
53
|
+
max: @max,
|
|
54
|
+
step: @step,
|
|
55
|
+
disabled: @disabled ? true : nil,
|
|
56
|
+
class: merge_classes(BASE_CLASSES),
|
|
57
|
+
style: slider_style,
|
|
58
|
+
"data-controller": "shadcn--slider",
|
|
59
|
+
"data-action": "input->shadcn--slider#updateStyle"
|
|
60
|
+
}
|
|
61
|
+
attrs.merge!(html_options)
|
|
62
|
+
attrs.merge!(build_data)
|
|
63
|
+
attrs.compact
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def slider_style
|
|
67
|
+
# CSS custom property for the fill percentage
|
|
68
|
+
"--slider-fill: #{percentage}%"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def percentage
|
|
72
|
+
return 0 if @max == @min
|
|
73
|
+
((@value - @min) / (@max - @min) * 100).round(2)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Spinner component for loading states
|
|
5
|
+
# Matches shadcn/ui Spinner component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic spinner
|
|
8
|
+
# <%= render Shadcn::SpinnerComponent.new %>
|
|
9
|
+
#
|
|
10
|
+
# @example With size
|
|
11
|
+
# <%= render Shadcn::SpinnerComponent.new(size: :sm) %>
|
|
12
|
+
# <%= render Shadcn::SpinnerComponent.new(size: :lg) %>
|
|
13
|
+
#
|
|
14
|
+
# @example Custom color
|
|
15
|
+
# <%= render Shadcn::SpinnerComponent.new(class_name: "text-primary") %>
|
|
16
|
+
#
|
|
17
|
+
class SpinnerComponent < BaseComponent
|
|
18
|
+
SIZES = {
|
|
19
|
+
sm: "h-4 w-4",
|
|
20
|
+
default: "h-6 w-6",
|
|
21
|
+
lg: "h-8 w-8",
|
|
22
|
+
xl: "h-12 w-12"
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
BASE_CLASSES = "animate-spin text-muted-foreground"
|
|
26
|
+
|
|
27
|
+
# @param size [Symbol] Spinner size (:sm, :default, :lg, :xl)
|
|
28
|
+
def initialize(size: :default, **options)
|
|
29
|
+
super(**options)
|
|
30
|
+
@size = size.to_sym
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
tag.svg(**spinner_attributes) do
|
|
35
|
+
safe_join([
|
|
36
|
+
tag.circle(
|
|
37
|
+
class: "opacity-25",
|
|
38
|
+
cx: "12",
|
|
39
|
+
cy: "12",
|
|
40
|
+
r: "10",
|
|
41
|
+
stroke: "currentColor",
|
|
42
|
+
"stroke-width": "4",
|
|
43
|
+
fill: "none"
|
|
44
|
+
),
|
|
45
|
+
tag.path(
|
|
46
|
+
class: "opacity-75",
|
|
47
|
+
fill: "currentColor",
|
|
48
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
49
|
+
)
|
|
50
|
+
])
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def spinner_attributes
|
|
57
|
+
{
|
|
58
|
+
class: cn(BASE_CLASSES, SIZES[@size], class_name),
|
|
59
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
60
|
+
fill: "none",
|
|
61
|
+
viewBox: "0 0 24 24",
|
|
62
|
+
role: "status",
|
|
63
|
+
"aria-label": "Loading"
|
|
64
|
+
}.merge(html_options).merge(build_data).compact
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Switch component for toggle inputs
|
|
5
|
+
# Uses a custom button element with hidden input for form submission
|
|
6
|
+
# Requires Stimulus controller for interactivity
|
|
7
|
+
#
|
|
8
|
+
# @example Basic switch
|
|
9
|
+
# <%= render Shadcn::SwitchComponent.new(name: "notifications") %>
|
|
10
|
+
#
|
|
11
|
+
# @example With integrated label
|
|
12
|
+
# <%= render Shadcn::SwitchComponent.new(name: "dark_mode") { "Enable dark mode" } %>
|
|
13
|
+
#
|
|
14
|
+
# @example Checked by default
|
|
15
|
+
# <%= render Shadcn::SwitchComponent.new(name: "active", checked: true) %>
|
|
16
|
+
#
|
|
17
|
+
class SwitchComponent < BaseComponent
|
|
18
|
+
BASE_CLASSES = [
|
|
19
|
+
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full",
|
|
20
|
+
"border-2 border-transparent shadow-sm transition-colors",
|
|
21
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
22
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
23
|
+
"data-[state=checked]:bg-primary data-[state=unchecked]:bg-input"
|
|
24
|
+
].join(" ")
|
|
25
|
+
|
|
26
|
+
THUMB_CLASSES = [
|
|
27
|
+
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0",
|
|
28
|
+
"transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
|
|
29
|
+
].join(" ")
|
|
30
|
+
|
|
31
|
+
# @param name [String, nil] Input name attribute
|
|
32
|
+
# @param id [String, nil] Input id attribute
|
|
33
|
+
# @param value [String] Value when checked
|
|
34
|
+
# @param checked [Boolean] Whether switch is on
|
|
35
|
+
# @param disabled [Boolean] Whether switch is disabled
|
|
36
|
+
# @param required [Boolean] Whether switch is required
|
|
37
|
+
def initialize(
|
|
38
|
+
name: nil,
|
|
39
|
+
id: nil,
|
|
40
|
+
value: "1",
|
|
41
|
+
checked: false,
|
|
42
|
+
disabled: false,
|
|
43
|
+
required: false,
|
|
44
|
+
**options
|
|
45
|
+
)
|
|
46
|
+
super(**options)
|
|
47
|
+
@name = name
|
|
48
|
+
@id = id || (name ? "switch-#{name}" : nil)
|
|
49
|
+
@value = value
|
|
50
|
+
@checked = checked
|
|
51
|
+
@disabled = disabled
|
|
52
|
+
@required = required
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def call
|
|
56
|
+
if content.present?
|
|
57
|
+
# Render with integrated label
|
|
58
|
+
content_tag(:label, class: "flex items-center gap-3 cursor-pointer") do
|
|
59
|
+
safe_join([
|
|
60
|
+
switch_wrapper,
|
|
61
|
+
content_tag(:span, content, class: "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70")
|
|
62
|
+
])
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
switch_wrapper
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def switch_wrapper
|
|
72
|
+
content_tag(:span, wrapper_attributes) do
|
|
73
|
+
safe_join([
|
|
74
|
+
hidden_unchecked_input,
|
|
75
|
+
hidden_input,
|
|
76
|
+
switch_button
|
|
77
|
+
].compact)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def wrapper_attributes
|
|
82
|
+
{
|
|
83
|
+
class: "inline-flex items-center",
|
|
84
|
+
"data-controller": "shadcn--switch",
|
|
85
|
+
"data-shadcn--switch-checked-value": @checked.to_s
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def hidden_unchecked_input
|
|
90
|
+
# Rails convention: hidden input with "0" for unchecked state
|
|
91
|
+
return unless @name
|
|
92
|
+
|
|
93
|
+
tag(:input,
|
|
94
|
+
type: "hidden",
|
|
95
|
+
name: @name,
|
|
96
|
+
value: "0",
|
|
97
|
+
autocomplete: "off"
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def hidden_input
|
|
102
|
+
return unless @name
|
|
103
|
+
|
|
104
|
+
tag(:input,
|
|
105
|
+
type: "checkbox",
|
|
106
|
+
name: @name,
|
|
107
|
+
id: @id,
|
|
108
|
+
value: @value,
|
|
109
|
+
checked: @checked || nil,
|
|
110
|
+
disabled: @disabled || nil,
|
|
111
|
+
required: @required || nil,
|
|
112
|
+
class: "sr-only",
|
|
113
|
+
"data-shadcn--switch-target": "input",
|
|
114
|
+
tabindex: "-1"
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def switch_button
|
|
119
|
+
content_tag(:button, switch_thumb, switch_button_attributes)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def switch_thumb
|
|
123
|
+
content_tag(:span, "", class: THUMB_CLASSES, "data-state": state, "data-shadcn--switch-target": "thumb")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def state
|
|
127
|
+
@checked ? "checked" : "unchecked"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def switch_button_attributes
|
|
131
|
+
attrs = {
|
|
132
|
+
type: "button",
|
|
133
|
+
role: "switch",
|
|
134
|
+
class: cn(BASE_CLASSES, class_name),
|
|
135
|
+
disabled: @disabled || nil,
|
|
136
|
+
"aria-checked": @checked.to_s,
|
|
137
|
+
"aria-required": @required ? "true" : nil,
|
|
138
|
+
"data-state": state,
|
|
139
|
+
"data-shadcn--switch-target": "button",
|
|
140
|
+
"data-action": "click->shadcn--switch#toggle keydown->shadcn--switch#handleKeydown",
|
|
141
|
+
tabindex: "0"
|
|
142
|
+
}
|
|
143
|
+
attrs.merge!(html_options.except(:class))
|
|
144
|
+
attrs.compact
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table Body component
|
|
5
|
+
class TableBodyComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "[&_tr:last-child]:border-0"
|
|
7
|
+
|
|
8
|
+
renders_many :rows, lambda { |**options, &block|
|
|
9
|
+
TableRowComponent.new(**options, &block)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:tbody, safe_join([rows, content].compact.flatten), class: merge_classes(BASE_CLASSES))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table Caption component
|
|
5
|
+
class TableCaptionComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "mt-4 text-sm text-muted-foreground"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:caption, content, class: merge_classes(BASE_CLASSES), **html_options)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table Cell component
|
|
5
|
+
class TableCellComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:td, content, class: merge_classes(BASE_CLASSES), **html_options)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|