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,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table component for displaying tabular data
|
|
5
|
+
# Matches shadcn/ui Table component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic table
|
|
8
|
+
# <%= render Shadcn::TableComponent.new do |table| %>
|
|
9
|
+
# <% table.with_header do |header| %>
|
|
10
|
+
# <% header.with_row do |row| %>
|
|
11
|
+
# <% row.with_head { "Name" } %>
|
|
12
|
+
# <% row.with_head { "Email" } %>
|
|
13
|
+
# <% row.with_head { "Role" } %>
|
|
14
|
+
# <% end %>
|
|
15
|
+
# <% end %>
|
|
16
|
+
# <% table.with_body do |body| %>
|
|
17
|
+
# <% @users.each do |user| %>
|
|
18
|
+
# <% body.with_row do |row| %>
|
|
19
|
+
# <% row.with_cell { user.name } %>
|
|
20
|
+
# <% row.with_cell { user.email } %>
|
|
21
|
+
# <% row.with_cell { user.role } %>
|
|
22
|
+
# <% end %>
|
|
23
|
+
# <% end %>
|
|
24
|
+
# <% end %>
|
|
25
|
+
# <% end %>
|
|
26
|
+
#
|
|
27
|
+
class TableComponent < BaseComponent
|
|
28
|
+
BASE_CLASSES = "w-full caption-bottom text-sm"
|
|
29
|
+
|
|
30
|
+
renders_one :caption, lambda { |**options|
|
|
31
|
+
TableCaptionComponent.new(**options)
|
|
32
|
+
}
|
|
33
|
+
renders_one :header, lambda { |**options|
|
|
34
|
+
TableHeaderComponent.new(**options)
|
|
35
|
+
}
|
|
36
|
+
renders_one :body, lambda { |**options|
|
|
37
|
+
TableBodyComponent.new(**options)
|
|
38
|
+
}
|
|
39
|
+
renders_one :footer, lambda { |**options|
|
|
40
|
+
TableFooterComponent.new(**options)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def call
|
|
44
|
+
content_tag(:div, table_element, class: "relative w-full overflow-auto")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def table_element
|
|
50
|
+
content_tag(:table, table_content, class: merge_classes(BASE_CLASSES))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def table_content
|
|
54
|
+
safe_join([caption, header, body, footer, content].compact)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table Footer component
|
|
5
|
+
class TableFooterComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0"
|
|
7
|
+
|
|
8
|
+
renders_many :rows, lambda { |**options, &block|
|
|
9
|
+
TableRowComponent.new(**options, &block)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:tfoot, 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 Head component
|
|
5
|
+
class TableHeadComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:th, content, class: merge_classes(BASE_CLASSES), **html_options)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table Header component
|
|
5
|
+
class TableHeaderComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "[&_tr]:border-b"
|
|
7
|
+
|
|
8
|
+
renders_many :rows, lambda { |**options, &block|
|
|
9
|
+
TableRowComponent.new(**options, &block)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:thead, safe_join([rows, content].compact.flatten), class: merge_classes(BASE_CLASSES))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Table Row component
|
|
5
|
+
class TableRowComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted"
|
|
7
|
+
|
|
8
|
+
renders_many :heads, lambda { |**options, &block|
|
|
9
|
+
TableHeadComponent.new(**options, &block)
|
|
10
|
+
}
|
|
11
|
+
renders_many :cells, lambda { |**options, &block|
|
|
12
|
+
TableCellComponent.new(**options, &block)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# @param selected [Boolean] Whether row is selected
|
|
16
|
+
def initialize(selected: false, **options, &block)
|
|
17
|
+
super(**options, &block)
|
|
18
|
+
@selected = selected
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
content_tag(:tr, row_content, row_attributes)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def row_content
|
|
28
|
+
safe_join([heads, cells, content].compact.flatten)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def row_attributes
|
|
32
|
+
attrs = {
|
|
33
|
+
class: merge_classes(BASE_CLASSES),
|
|
34
|
+
"data-state": @selected ? "selected" : nil
|
|
35
|
+
}
|
|
36
|
+
attrs.merge!(html_options)
|
|
37
|
+
attrs.compact
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Tabs component for tabbed interfaces
|
|
5
|
+
# Matches shadcn/ui Tabs component
|
|
6
|
+
# Uses Stimulus for interactivity
|
|
7
|
+
#
|
|
8
|
+
# @example Basic tabs
|
|
9
|
+
# <%= render Shadcn::TabsComponent.new(default_value: "account") do |tabs| %>
|
|
10
|
+
# <% tabs.with_list do |list| %>
|
|
11
|
+
# <% list.with_trigger(value: "account") { "Account" } %>
|
|
12
|
+
# <% list.with_trigger(value: "password") { "Password" } %>
|
|
13
|
+
# <% end %>
|
|
14
|
+
# <% tabs.with_panel(value: "account") do %>
|
|
15
|
+
# Account settings content
|
|
16
|
+
# <% end %>
|
|
17
|
+
# <% tabs.with_panel(value: "password") do %>
|
|
18
|
+
# Password settings content
|
|
19
|
+
# <% end %>
|
|
20
|
+
# <% end %>
|
|
21
|
+
#
|
|
22
|
+
# @example Tabs with URL sync
|
|
23
|
+
# <%= render Shadcn::TabsComponent.new(default_value: "account", url_param: "tab") do |tabs| %>
|
|
24
|
+
# <% tabs.with_list do |list| %>
|
|
25
|
+
# <% list.with_trigger(value: "account") { "Account" } %>
|
|
26
|
+
# <% list.with_trigger(value: "password") { "Password" } %>
|
|
27
|
+
# <% end %>
|
|
28
|
+
# <% tabs.with_panel(value: "account") do %>
|
|
29
|
+
# Account settings content
|
|
30
|
+
# <% end %>
|
|
31
|
+
# <% tabs.with_panel(value: "password") do %>
|
|
32
|
+
# Password settings content
|
|
33
|
+
# <% end %>
|
|
34
|
+
# <% end %>
|
|
35
|
+
# # URL will update to ?tab=account or ?tab=password when tabs are clicked
|
|
36
|
+
#
|
|
37
|
+
class TabsComponent < BaseComponent
|
|
38
|
+
renders_one :list, lambda { |**options|
|
|
39
|
+
TabsListComponent.new(**options)
|
|
40
|
+
}
|
|
41
|
+
renders_many :panels, lambda { |value:, **options, &block|
|
|
42
|
+
TabsContentComponent.new(value: value, **options, &block)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# @param default_value [String] The value of the initially active tab
|
|
46
|
+
# @param orientation [Symbol] Orientation (:horizontal, :vertical)
|
|
47
|
+
# @param url_param [String, nil] Query parameter name to sync active tab with URL (e.g., "tab")
|
|
48
|
+
def initialize(default_value: nil, orientation: :horizontal, url_param: nil, **options)
|
|
49
|
+
super(**options)
|
|
50
|
+
@default_value = default_value
|
|
51
|
+
@orientation = orientation
|
|
52
|
+
@url_param = url_param
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def call
|
|
56
|
+
content_tag(:div, tabs_content, tabs_attributes)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def tabs_content
|
|
62
|
+
safe_join([list, panels, content].compact.flatten)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def tabs_attributes
|
|
66
|
+
attrs = {
|
|
67
|
+
class: class_name,
|
|
68
|
+
"data-controller": "shadcn--tabs",
|
|
69
|
+
"data-shadcn--tabs-default-value": @default_value,
|
|
70
|
+
"data-shadcn--tabs-url-param-value": @url_param,
|
|
71
|
+
"data-orientation": @orientation.to_s
|
|
72
|
+
}
|
|
73
|
+
attrs.merge!(html_options)
|
|
74
|
+
attrs.merge!(build_data)
|
|
75
|
+
attrs.compact
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Tabs Content component
|
|
5
|
+
class TabsContentComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
7
|
+
|
|
8
|
+
# @param value [String] The value that identifies this tab panel
|
|
9
|
+
def initialize(value:, **options)
|
|
10
|
+
super(**options)
|
|
11
|
+
@value = value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
content_tag(:div, content, content_attributes)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def content_attributes
|
|
21
|
+
{
|
|
22
|
+
class: merge_classes(BASE_CLASSES),
|
|
23
|
+
role: "tabpanel",
|
|
24
|
+
"data-shadcn--tabs-target": "content",
|
|
25
|
+
"data-value": @value,
|
|
26
|
+
"data-state": "inactive",
|
|
27
|
+
hidden: true,
|
|
28
|
+
tabindex: "0"
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Tabs List component
|
|
5
|
+
class TabsListComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground"
|
|
7
|
+
|
|
8
|
+
renders_many :triggers, lambda { |value:, **options, &block|
|
|
9
|
+
TabsTriggerComponent.new(value: value, **options, &block)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def call
|
|
13
|
+
content_tag(:div, list_content, list_attributes)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def list_content
|
|
19
|
+
safe_join([triggers, content].compact.flatten)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def list_attributes
|
|
23
|
+
{
|
|
24
|
+
class: merge_classes(BASE_CLASSES),
|
|
25
|
+
role: "tablist",
|
|
26
|
+
"data-shadcn--tabs-target": "list"
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Tabs Trigger component
|
|
5
|
+
class TabsTriggerComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow"
|
|
7
|
+
|
|
8
|
+
# @param value [String] The value that identifies this tab
|
|
9
|
+
# @param disabled [Boolean] Whether the tab is disabled
|
|
10
|
+
def initialize(value:, disabled: false, **options)
|
|
11
|
+
super(**options)
|
|
12
|
+
@value = value
|
|
13
|
+
@disabled = disabled
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
content_tag(:button, content, trigger_attributes)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def trigger_attributes
|
|
23
|
+
{
|
|
24
|
+
type: "button",
|
|
25
|
+
role: "tab",
|
|
26
|
+
class: merge_classes(BASE_CLASSES),
|
|
27
|
+
disabled: @disabled || nil,
|
|
28
|
+
"data-shadcn--tabs-target": "trigger",
|
|
29
|
+
"data-value": @value,
|
|
30
|
+
"data-state": "inactive",
|
|
31
|
+
"data-action": "click->shadcn--tabs#selectTab",
|
|
32
|
+
"aria-selected": "false",
|
|
33
|
+
tabindex: "-1"
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Textarea component for multi-line text input
|
|
5
|
+
# Matches shadcn/ui Textarea component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic textarea
|
|
8
|
+
# <%= render Shadcn::TextareaComponent.new(name: "bio", placeholder: "Tell us about yourself") %>
|
|
9
|
+
#
|
|
10
|
+
# @example With rows
|
|
11
|
+
# <%= render Shadcn::TextareaComponent.new(name: "message", rows: 6) %>
|
|
12
|
+
#
|
|
13
|
+
class TextareaComponent < BaseComponent
|
|
14
|
+
BASE_CLASSES = "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm"
|
|
15
|
+
|
|
16
|
+
# @param name [String, nil] Textarea name attribute
|
|
17
|
+
# @param id [String, nil] Textarea id attribute
|
|
18
|
+
# @param value [String, nil] Initial value
|
|
19
|
+
# @param placeholder [String, nil] Placeholder text
|
|
20
|
+
# @param rows [Integer] Number of visible rows
|
|
21
|
+
# @param cols [Integer, nil] Number of visible columns
|
|
22
|
+
# @param disabled [Boolean] Whether textarea is disabled
|
|
23
|
+
# @param required [Boolean] Whether textarea is required
|
|
24
|
+
# @param readonly [Boolean] Whether textarea is readonly
|
|
25
|
+
# @param autofocus [Boolean] Whether to autofocus
|
|
26
|
+
# @param minlength [Integer, nil] Minimum length
|
|
27
|
+
# @param maxlength [Integer, nil] Maximum length
|
|
28
|
+
def initialize(
|
|
29
|
+
name: nil,
|
|
30
|
+
id: nil,
|
|
31
|
+
value: nil,
|
|
32
|
+
placeholder: nil,
|
|
33
|
+
rows: 3,
|
|
34
|
+
cols: nil,
|
|
35
|
+
disabled: false,
|
|
36
|
+
required: false,
|
|
37
|
+
readonly: false,
|
|
38
|
+
autofocus: false,
|
|
39
|
+
minlength: nil,
|
|
40
|
+
maxlength: nil,
|
|
41
|
+
**options
|
|
42
|
+
)
|
|
43
|
+
super(**options)
|
|
44
|
+
@name = name
|
|
45
|
+
@id = id
|
|
46
|
+
@value = value
|
|
47
|
+
@placeholder = placeholder
|
|
48
|
+
@rows = rows
|
|
49
|
+
@cols = cols
|
|
50
|
+
@disabled = disabled
|
|
51
|
+
@required = required
|
|
52
|
+
@readonly = readonly
|
|
53
|
+
@autofocus = autofocus
|
|
54
|
+
@minlength = minlength
|
|
55
|
+
@maxlength = maxlength
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def call
|
|
59
|
+
content_tag(:textarea, @value || content, textarea_attributes)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def textarea_attributes
|
|
65
|
+
attrs = {
|
|
66
|
+
name: @name,
|
|
67
|
+
id: @id,
|
|
68
|
+
placeholder: @placeholder,
|
|
69
|
+
rows: @rows,
|
|
70
|
+
cols: @cols,
|
|
71
|
+
disabled: @disabled || nil,
|
|
72
|
+
required: @required || nil,
|
|
73
|
+
readonly: @readonly || nil,
|
|
74
|
+
autofocus: @autofocus || nil,
|
|
75
|
+
minlength: @minlength,
|
|
76
|
+
maxlength: @maxlength,
|
|
77
|
+
class: merge_classes(BASE_CLASSES)
|
|
78
|
+
}
|
|
79
|
+
attrs.merge!(html_options)
|
|
80
|
+
attrs.merge!(build_data)
|
|
81
|
+
attrs.compact
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Toast Action component
|
|
5
|
+
class ToastActionComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive"
|
|
7
|
+
|
|
8
|
+
# @param alt_text [String] Alternative text for accessibility
|
|
9
|
+
def initialize(alt_text:, **options, &block)
|
|
10
|
+
super(**options, &block)
|
|
11
|
+
@alt_text = alt_text
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES), "aria-label": @alt_text)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Toast component for notifications
|
|
5
|
+
# Matches shadcn/ui Toast component
|
|
6
|
+
# Uses Stimulus for interactivity
|
|
7
|
+
#
|
|
8
|
+
# @example Basic toast (typically rendered via JavaScript/Turbo)
|
|
9
|
+
# <%= render Shadcn::ToastComponent.new(variant: :default) do |toast| %>
|
|
10
|
+
# <% toast.with_title { "Scheduled" } %>
|
|
11
|
+
# <% toast.with_description { "Your message has been scheduled." } %>
|
|
12
|
+
# <% end %>
|
|
13
|
+
#
|
|
14
|
+
# @example Destructive toast
|
|
15
|
+
# <%= render Shadcn::ToastComponent.new(variant: :destructive) do |toast| %>
|
|
16
|
+
# <% toast.with_title { "Error" } %>
|
|
17
|
+
# <% toast.with_description { "Something went wrong." } %>
|
|
18
|
+
# <% toast.with_action(alt_text: "Try again") do %>
|
|
19
|
+
# <%= render Shadcn::ButtonComponent.new(variant: :outline, size: :sm) { "Try again" } %>
|
|
20
|
+
# <% end %>
|
|
21
|
+
# <% end %>
|
|
22
|
+
#
|
|
23
|
+
class ToastComponent < BaseComponent
|
|
24
|
+
VARIANTS = {
|
|
25
|
+
default: "border bg-background text-foreground",
|
|
26
|
+
destructive: "destructive group border-destructive bg-destructive text-destructive-foreground"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
BASE_CLASSES = "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full"
|
|
30
|
+
|
|
31
|
+
renders_one :title, lambda { |**options|
|
|
32
|
+
ToastTitleComponent.new(**options)
|
|
33
|
+
}
|
|
34
|
+
renders_one :description, lambda { |**options|
|
|
35
|
+
ToastDescriptionComponent.new(**options)
|
|
36
|
+
}
|
|
37
|
+
renders_one :action, lambda { |alt_text:, **options, &block|
|
|
38
|
+
ToastActionComponent.new(alt_text: alt_text, **options, &block)
|
|
39
|
+
}
|
|
40
|
+
renders_one :close
|
|
41
|
+
|
|
42
|
+
# @param variant [Symbol] Toast variant (:default, :destructive)
|
|
43
|
+
# @param duration [Integer] Auto-dismiss duration in ms (0 for no auto-dismiss)
|
|
44
|
+
# @param open [Boolean] Whether toast is visible
|
|
45
|
+
def initialize(variant: :default, duration: 5000, open: true, **options)
|
|
46
|
+
super(**options)
|
|
47
|
+
@variant = variant.to_sym
|
|
48
|
+
@duration = duration
|
|
49
|
+
@open = open
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def call
|
|
53
|
+
content_tag(:li, toast_content, toast_attributes)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def toast_content
|
|
59
|
+
safe_join([
|
|
60
|
+
content_wrapper,
|
|
61
|
+
action,
|
|
62
|
+
close_button
|
|
63
|
+
].compact)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def content_wrapper
|
|
67
|
+
content_tag(:div, safe_join([title, description, content].compact), class: "grid gap-1")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def close_button
|
|
71
|
+
close || default_close_button
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def default_close_button
|
|
75
|
+
content_tag(:button, close_icon, {
|
|
76
|
+
type: "button",
|
|
77
|
+
class: "absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
|
78
|
+
"data-action": "click->shadcn--toast#close",
|
|
79
|
+
"aria-label": "Close"
|
|
80
|
+
})
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def close_icon
|
|
84
|
+
content_tag(:svg,
|
|
85
|
+
content_tag(:path, nil, d: "M18 6 6 18M6 6l12 12", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round"),
|
|
86
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
87
|
+
width: "16",
|
|
88
|
+
height: "16",
|
|
89
|
+
viewBox: "0 0 24 24",
|
|
90
|
+
fill: "none",
|
|
91
|
+
class: "h-4 w-4"
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def toast_classes
|
|
96
|
+
cn(BASE_CLASSES, VARIANTS[@variant], class_name)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def toast_attributes
|
|
100
|
+
attrs = {
|
|
101
|
+
class: toast_classes,
|
|
102
|
+
role: "status",
|
|
103
|
+
"aria-live": "polite",
|
|
104
|
+
"data-controller": "shadcn--toast",
|
|
105
|
+
"data-shadcn--toast-duration-value": @duration,
|
|
106
|
+
"data-shadcn--toast-open-value": @open.to_s,
|
|
107
|
+
"data-state": @open ? "open" : "closed"
|
|
108
|
+
}
|
|
109
|
+
attrs.merge!(html_options)
|
|
110
|
+
attrs.merge!(build_data)
|
|
111
|
+
attrs.compact
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Toast Description component
|
|
5
|
+
class ToastDescriptionComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "text-sm opacity-90"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Toast Title component
|
|
5
|
+
class ToastTitleComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "text-sm font-semibold [&+div]:text-xs"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:div, content, class: merge_classes(BASE_CLASSES))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Toast Viewport (container for all toasts)
|
|
5
|
+
class ToastViewportComponent < BaseComponent
|
|
6
|
+
BASE_CLASSES = "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]"
|
|
7
|
+
|
|
8
|
+
def call
|
|
9
|
+
content_tag(:ol, content, class: merge_classes(BASE_CLASSES), tabindex: "-1", "data-shadcn--toaster-target": "viewport")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Toggle component for a two-state button
|
|
5
|
+
# Matches shadcn/ui Toggle component
|
|
6
|
+
#
|
|
7
|
+
# @example Basic usage
|
|
8
|
+
# <%= render Shadcn::ToggleComponent.new(aria_label: "Toggle bold") do %>
|
|
9
|
+
# <svg>...</svg>
|
|
10
|
+
# <% end %>
|
|
11
|
+
#
|
|
12
|
+
# @example Outline variant
|
|
13
|
+
# <%= render Shadcn::ToggleComponent.new(variant: :outline, pressed: true) do %>
|
|
14
|
+
# Italic
|
|
15
|
+
# <% end %>
|
|
16
|
+
#
|
|
17
|
+
class ToggleComponent < BaseComponent
|
|
18
|
+
VARIANTS = {
|
|
19
|
+
default: "bg-transparent",
|
|
20
|
+
outline: "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground"
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
SIZES = {
|
|
24
|
+
sm: "h-8 px-2",
|
|
25
|
+
default: "h-9 px-3",
|
|
26
|
+
lg: "h-10 px-3"
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
BASE_CLASSES = "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground"
|
|
30
|
+
|
|
31
|
+
# @param variant [Symbol] :default or :outline
|
|
32
|
+
# @param size [Symbol] :sm, :default, or :lg
|
|
33
|
+
# @param pressed [Boolean] Initial pressed state
|
|
34
|
+
# @param disabled [Boolean] Whether toggle is disabled
|
|
35
|
+
# @param aria_label [String] Accessibility label
|
|
36
|
+
def initialize(
|
|
37
|
+
variant: :default,
|
|
38
|
+
size: :default,
|
|
39
|
+
pressed: false,
|
|
40
|
+
disabled: false,
|
|
41
|
+
aria_label: nil,
|
|
42
|
+
**options
|
|
43
|
+
)
|
|
44
|
+
super(**options)
|
|
45
|
+
@variant = variant
|
|
46
|
+
@size = size
|
|
47
|
+
@pressed = pressed
|
|
48
|
+
@disabled = disabled
|
|
49
|
+
@aria_label = aria_label
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def call
|
|
53
|
+
content_tag(:button, toggle_attributes) do
|
|
54
|
+
content
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def toggle_attributes
|
|
61
|
+
attrs = {
|
|
62
|
+
type: "button",
|
|
63
|
+
class: cn(BASE_CLASSES, VARIANTS[@variant], SIZES[@size], class_name),
|
|
64
|
+
disabled: @disabled || nil,
|
|
65
|
+
"aria-pressed": @pressed.to_s,
|
|
66
|
+
"aria-label": @aria_label,
|
|
67
|
+
"data-state": @pressed ? "on" : "off",
|
|
68
|
+
"data-controller": "shadcn--toggle",
|
|
69
|
+
"data-action": "click->shadcn--toggle#toggle",
|
|
70
|
+
"data-shadcn--toggle-pressed-value": @pressed.to_s
|
|
71
|
+
}
|
|
72
|
+
attrs.merge!(html_options)
|
|
73
|
+
attrs.merge!(build_data)
|
|
74
|
+
attrs.compact
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|