shadcn-rails 0.1.0 → 0.2.1
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 +4 -4
- data/CHANGELOG.md +69 -2
- data/README.md +102 -1398
- data/__mocks__/@floating-ui/dom.js +67 -0
- data/app/assets/javascripts/shadcn/controllers/base_menu_controller.js +266 -0
- data/app/assets/javascripts/shadcn/controllers/combobox_controller.js +34 -8
- data/app/assets/javascripts/shadcn/controllers/command_controller.js +5 -1
- data/app/assets/javascripts/shadcn/controllers/context_menu_controller.js +64 -135
- data/app/assets/javascripts/shadcn/controllers/dropdown_controller.js +56 -186
- data/app/assets/javascripts/shadcn/controllers/hover_card_controller.js +29 -55
- data/app/assets/javascripts/shadcn/controllers/menubar_controller.js +10 -7
- data/app/assets/javascripts/shadcn/controllers/navigation_menu_controller.js +10 -6
- data/app/assets/javascripts/shadcn/controllers/popover_controller.js +35 -60
- data/app/assets/javascripts/shadcn/controllers/select_controller.js +37 -17
- data/app/assets/javascripts/shadcn/controllers/sidebar_controller.js +24 -14
- data/app/assets/javascripts/shadcn/controllers/tooltip_controller.js +28 -59
- data/app/assets/javascripts/shadcn/index.js +9 -1
- data/app/assets/javascripts/shadcn/utils/floating.js +179 -0
- data/app/assets/stylesheets/shadcn/base.css +32 -0
- data/app/assets/stylesheets/shadcn/components.css +12 -0
- data/app/components/shadcn/accordion_component.html.erb +8 -0
- data/app/components/shadcn/accordion_component.rb +6 -15
- data/app/components/shadcn/alert_component.html.erb +6 -0
- data/app/components/shadcn/alert_component.rb +0 -18
- data/app/components/shadcn/alert_dialog_component.html.erb +12 -0
- data/app/components/shadcn/alert_dialog_component.rb +7 -27
- data/app/components/shadcn/aspect_ratio_component.html.erb +7 -0
- data/app/components/shadcn/aspect_ratio_component.rb +4 -19
- data/app/components/shadcn/avatar_component.html.erb +20 -0
- data/app/components/shadcn/avatar_component.rb +8 -36
- data/app/components/shadcn/badge_component.html.erb +1 -0
- data/app/components/shadcn/badge_component.rb +0 -11
- data/app/components/shadcn/base_component.rb +15 -2
- data/app/components/shadcn/breadcrumb_component.html.erb +5 -0
- data/app/components/shadcn/breadcrumb_component.rb +6 -16
- data/app/components/shadcn/button_component.html.erb +18 -0
- data/app/components/shadcn/button_component.rb +1 -41
- data/app/components/shadcn/card_component.html.erb +8 -0
- data/app/components/shadcn/card_component.rb +2 -6
- data/app/components/shadcn/checkbox_component.html.erb +32 -0
- data/app/components/shadcn/checkbox_component.rb +4 -43
- data/app/components/shadcn/collapsible_component.html.erb +8 -0
- data/app/components/shadcn/collapsible_component.rb +6 -15
- data/app/components/shadcn/command_list_component.rb +29 -14
- data/app/components/shadcn/context_menu_checkbox_item_component.rb +76 -0
- data/app/components/shadcn/context_menu_component.html.erb +11 -0
- data/app/components/shadcn/context_menu_component.rb +6 -26
- data/app/components/shadcn/context_menu_content_component.rb +37 -14
- data/app/components/shadcn/context_menu_item_component.rb +3 -2
- data/app/components/shadcn/context_menu_radio_group_component.rb +42 -0
- data/app/components/shadcn/context_menu_radio_item_component.rb +76 -0
- data/app/components/shadcn/dialog_component.html.erb +14 -0
- data/app/components/shadcn/dialog_component.rb +8 -29
- data/app/components/shadcn/drawer_component.html.erb +12 -0
- data/app/components/shadcn/drawer_component.rb +7 -27
- data/app/components/shadcn/dropdown_menu_checkbox_item_component.rb +76 -0
- data/app/components/shadcn/dropdown_menu_component.html.erb +14 -0
- data/app/components/shadcn/dropdown_menu_component.rb +9 -29
- data/app/components/shadcn/dropdown_menu_content_component.rb +45 -16
- data/app/components/shadcn/dropdown_menu_radio_group_component.rb +42 -0
- data/app/components/shadcn/dropdown_menu_radio_item_component.rb +76 -0
- data/app/components/shadcn/field_component.rb +7 -8
- data/app/components/shadcn/hover_card_component.html.erb +12 -0
- data/app/components/shadcn/hover_card_component.rb +7 -26
- data/app/components/shadcn/input_component.html.erb +18 -0
- data/app/components/shadcn/input_component.rb +2 -27
- data/app/components/shadcn/input_otp_component.rb +3 -3
- data/app/components/shadcn/kbd_component.html.erb +1 -0
- data/app/components/shadcn/kbd_component.rb +3 -10
- data/app/components/shadcn/label_component.html.erb +3 -0
- data/app/components/shadcn/label_component.rb +2 -18
- data/app/components/shadcn/menubar_component.html.erb +6 -0
- data/app/components/shadcn/menubar_component.rb +4 -15
- data/app/components/shadcn/menubar_content_component.rb +45 -20
- data/app/components/shadcn/menubar_sub_content_component.rb +21 -8
- data/app/components/shadcn/native_select_component.html.erb +22 -0
- data/app/components/shadcn/native_select_component.rb +9 -39
- data/app/components/shadcn/navigation_menu_component.html.erb +6 -0
- data/app/components/shadcn/navigation_menu_component.rb +4 -15
- data/app/components/shadcn/pagination_component.html.erb +5 -0
- data/app/components/shadcn/pagination_component.rb +11 -15
- data/app/components/shadcn/popover_component.html.erb +15 -0
- data/app/components/shadcn/popover_component.rb +10 -30
- data/app/components/shadcn/progress_component.html.erb +13 -0
- data/app/components/shadcn/progress_component.rb +6 -26
- data/app/components/shadcn/radio_group_component.html.erb +8 -0
- data/app/components/shadcn/radio_group_component.rb +12 -26
- data/app/components/shadcn/radio_group_item_component.rb +32 -6
- data/app/components/shadcn/resizable_panel_group_component.rb +27 -16
- data/app/components/shadcn/scroll_area_component.html.erb +7 -0
- data/app/components/shadcn/scroll_area_component.rb +4 -16
- data/app/components/shadcn/select_component.html.erb +46 -0
- data/app/components/shadcn/select_component.rb +29 -86
- data/app/components/shadcn/separator_component.html.erb +5 -0
- data/app/components/shadcn/separator_component.rb +6 -14
- data/app/components/shadcn/sheet_component.html.erb +12 -0
- data/app/components/shadcn/sheet_component.rb +7 -27
- data/app/components/shadcn/sidebar_component.rb +2 -2
- data/app/components/shadcn/skeleton_component.html.erb +1 -0
- data/app/components/shadcn/skeleton_component.rb +4 -2
- data/app/components/shadcn/slider_component.html.erb +12 -0
- data/app/components/shadcn/slider_component.rb +2 -21
- data/app/components/shadcn/spinner_component.html.erb +18 -0
- data/app/components/shadcn/spinner_component.rb +2 -30
- data/app/components/shadcn/switch_component.html.erb +72 -0
- data/app/components/shadcn/switch_component.rb +4 -82
- data/app/components/shadcn/table_component.html.erb +9 -0
- data/app/components/shadcn/table_component.rb +2 -10
- data/app/components/shadcn/tabs_component.html.erb +8 -0
- data/app/components/shadcn/tabs_component.rb +4 -17
- data/app/components/shadcn/textarea_component.html.erb +13 -0
- data/app/components/shadcn/textarea_component.rb +6 -22
- data/app/components/shadcn/toast_component.html.erb +36 -0
- data/app/components/shadcn/toast_component.rb +6 -54
- data/app/components/shadcn/toggle_component.html.erb +12 -0
- data/app/components/shadcn/toggle_component.rb +6 -21
- data/app/components/shadcn/toggle_group_component.html.erb +14 -0
- data/app/components/shadcn/toggle_group_component.rb +6 -29
- data/app/components/shadcn/tooltip_component.html.erb +20 -0
- data/app/components/shadcn/tooltip_component.rb +13 -38
- data/lib/generators/shadcn/add/USAGE +24 -0
- data/lib/generators/shadcn/add/add_generator.rb +279 -0
- data/lib/generators/shadcn/install/USAGE +22 -0
- data/lib/generators/shadcn/install/install_generator.rb +8 -3
- data/lib/generators/shadcn/install/templates/initializer.rb.tt +7 -27
- data/lib/generators/shadcn/install/templates/shadcn.yml.tt +15 -31
- data/lib/shadcn/rails/version.rb +1 -1
- metadata +54 -42
- data/.dockerignore +0 -40
- data/CLAUDE.md +0 -463
- data/PROGRESS.md +0 -485
- data/Rakefile +0 -29
- data/__tests__/controllers/__snapshots__/calendar_controller.test.js.snap +0 -13
- data/__tests__/controllers/__snapshots__/popover_controller.test.js.snap +0 -46
- data/__tests__/controllers/__snapshots__/sheet_controller.test.js.snap +0 -111
- data/__tests__/controllers/__snapshots__/tabs_controller.test.js.snap +0 -27
- data/__tests__/controllers/accordion_controller.test.js +0 -904
- data/__tests__/controllers/calendar_controller.test.js +0 -1370
- data/__tests__/controllers/carousel_controller.test.js +0 -912
- data/__tests__/controllers/checkbox_controller.test.js +0 -454
- data/__tests__/controllers/collapsible_controller.test.js +0 -407
- data/__tests__/controllers/combobox_controller.test.js +0 -966
- data/__tests__/controllers/context_menu_controller.test.js +0 -627
- data/__tests__/controllers/date_picker_controller.test.js +0 -636
- data/__tests__/controllers/dialog_controller.test.js +0 -878
- data/__tests__/controllers/drawer_controller.test.js +0 -995
- data/__tests__/controllers/menubar_controller.test.js +0 -736
- data/__tests__/controllers/navigation_menu_controller.test.js +0 -598
- data/__tests__/controllers/popover_controller.test.js +0 -1007
- data/__tests__/controllers/radio_group_controller.test.js +0 -640
- data/__tests__/controllers/resizable_controller.test.js +0 -680
- data/__tests__/controllers/select_controller.test.js +0 -674
- data/__tests__/controllers/sheet_controller.test.js +0 -986
- data/__tests__/controllers/slider_controller.test.js +0 -1036
- data/__tests__/controllers/switch_controller.test.js +0 -424
- data/__tests__/controllers/tabs_controller.test.js +0 -907
- data/__tests__/controllers/toggle_group_controller.test.js +0 -839
- data/__tests__/controllers/tooltip_controller.test.js +0 -808
- data/__tests__/helpers/stimulus-test-helper.js +0 -203
- data/babel.config.cjs +0 -5
- data/bin/console +0 -11
- data/bin/setup +0 -8
- data/jest.config.js +0 -19
- data/jest.setup.js +0 -8
- data/lib/generators/shadcn/component/component_generator.rb +0 -188
- data/lib/generators/shadcn/theme/theme_generator.rb +0 -128
- data/package-lock.json +0 -7415
- data/package.json +0 -68
- data/rollup.config.js +0 -29
|
@@ -34,38 +34,18 @@ module Shadcn
|
|
|
34
34
|
@open = open
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def call
|
|
38
|
-
content_tag(:div, dialog_content, dialog_attributes)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
37
|
private
|
|
42
38
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
trigger_wrapper,
|
|
46
|
-
body
|
|
47
|
-
].compact)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def trigger_wrapper
|
|
51
|
-
return unless trigger
|
|
52
|
-
|
|
53
|
-
content_tag(:div, trigger, {
|
|
54
|
-
"data-shadcn--dialog-target": "trigger",
|
|
55
|
-
"data-action": "click->shadcn--dialog#open"
|
|
56
|
-
})
|
|
39
|
+
def alert_dialog_classes
|
|
40
|
+
class_name
|
|
57
41
|
end
|
|
58
42
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"data-shadcn--dialog-modal-value": "true"
|
|
43
|
+
def alert_dialog_data_attrs
|
|
44
|
+
{
|
|
45
|
+
controller: "shadcn--dialog",
|
|
46
|
+
"shadcn--dialog-open-value": @open.to_s,
|
|
47
|
+
"shadcn--dialog-modal-value": "true"
|
|
65
48
|
}
|
|
66
|
-
attrs.merge!(html_options)
|
|
67
|
-
attrs.merge!(build_data)
|
|
68
|
-
attrs.compact
|
|
69
49
|
end
|
|
70
50
|
end
|
|
71
51
|
end
|
|
@@ -21,29 +21,14 @@ module Shadcn
|
|
|
21
21
|
@ratio = ratio.to_f
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def call
|
|
25
|
-
content_tag(:div, wrapper_attributes) do
|
|
26
|
-
content_tag(:div, content, inner_attributes)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
24
|
private
|
|
31
25
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
class: cn("relative w-full", class_name),
|
|
35
|
-
style: "padding-bottom: #{(1.0 / @ratio) * 100}%;"
|
|
36
|
-
}
|
|
37
|
-
attrs.merge!(html_options)
|
|
38
|
-
attrs.merge!(build_data)
|
|
39
|
-
attrs.compact
|
|
26
|
+
def wrapper_classes
|
|
27
|
+
cn("relative w-full", class_name)
|
|
40
28
|
end
|
|
41
29
|
|
|
42
|
-
def
|
|
43
|
-
{
|
|
44
|
-
class: "absolute inset-0",
|
|
45
|
-
style: "position: absolute; top: 0; right: 0; bottom: 0; left: 0;"
|
|
46
|
-
}
|
|
30
|
+
def wrapper_style
|
|
31
|
+
"padding-bottom: #{(1.0 / @ratio) * 100}%;"
|
|
47
32
|
end
|
|
48
33
|
end
|
|
49
34
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<span class="<%= avatar_classes %>" <%= tag_attributes %>>
|
|
2
|
+
<% if has_image? %>
|
|
3
|
+
<span class="contents" data-controller="shadcn--avatar">
|
|
4
|
+
<img src="<%= @src %>"
|
|
5
|
+
alt="<%= @alt %>"
|
|
6
|
+
class="<%= IMAGE_CLASSES %>"
|
|
7
|
+
data-shadcn--avatar-target="image"
|
|
8
|
+
data-action="error->shadcn--avatar#handleError" />
|
|
9
|
+
<span class="<%= FALLBACK_CLASSES %> hidden" data-shadcn--avatar-target="fallback">
|
|
10
|
+
<%= fallback_text %>
|
|
11
|
+
</span>
|
|
12
|
+
</span>
|
|
13
|
+
<% elsif has_fallback_slot? %>
|
|
14
|
+
<%= fallback %>
|
|
15
|
+
<% else %>
|
|
16
|
+
<span class="<%= FALLBACK_CLASSES %>">
|
|
17
|
+
<%= fallback_text %>
|
|
18
|
+
</span>
|
|
19
|
+
<% end %>
|
|
20
|
+
</span>
|
|
@@ -46,50 +46,22 @@ module Shadcn
|
|
|
46
46
|
@size = size.to_sym
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def call
|
|
50
|
-
content_tag(:span, avatar_content, avatar_attributes)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
49
|
private
|
|
54
50
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
image_with_fallback
|
|
58
|
-
elsif fallback?
|
|
59
|
-
fallback
|
|
60
|
-
else
|
|
61
|
-
fallback_element
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def image_with_fallback
|
|
66
|
-
# Use Stimulus controller to handle image loading errors
|
|
67
|
-
content_tag(:span, class: "contents", data: stimulus_data(controller: "shadcn--avatar")) do
|
|
68
|
-
safe_join([
|
|
69
|
-
tag(:img,
|
|
70
|
-
src: @src,
|
|
71
|
-
alt: @alt,
|
|
72
|
-
class: IMAGE_CLASSES,
|
|
73
|
-
data: { "shadcn--avatar-target": "image", action: "error->shadcn--avatar#handleError" }
|
|
74
|
-
),
|
|
75
|
-
content_tag(:span, @fallback, class: "#{FALLBACK_CLASSES} hidden", data: { "shadcn--avatar-target": "fallback" })
|
|
76
|
-
])
|
|
77
|
-
end
|
|
51
|
+
def avatar_classes
|
|
52
|
+
cn(BASE_CLASSES, SIZES[@size], class_name)
|
|
78
53
|
end
|
|
79
54
|
|
|
80
|
-
def
|
|
81
|
-
|
|
55
|
+
def fallback_text
|
|
56
|
+
@fallback
|
|
82
57
|
end
|
|
83
58
|
|
|
84
|
-
def
|
|
85
|
-
|
|
59
|
+
def has_image?
|
|
60
|
+
@src.present?
|
|
86
61
|
end
|
|
87
62
|
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
attrs.merge!(html_options)
|
|
91
|
-
attrs.merge!(build_data)
|
|
92
|
-
attrs.compact
|
|
63
|
+
def has_fallback_slot?
|
|
64
|
+
fallback?
|
|
93
65
|
end
|
|
94
66
|
|
|
95
67
|
def generate_fallback(alt)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<span class="<%= badge_classes %>" <%= tag_attributes %>><%= content %></span>
|
|
@@ -29,21 +29,10 @@ module Shadcn
|
|
|
29
29
|
@variant = variant.to_sym
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def call
|
|
33
|
-
content_tag(:span, content, badge_attributes)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
32
|
private
|
|
37
33
|
|
|
38
34
|
def badge_classes
|
|
39
35
|
cn(BASE_CLASSES, VARIANTS[@variant], class_name)
|
|
40
36
|
end
|
|
41
|
-
|
|
42
|
-
def badge_attributes
|
|
43
|
-
attrs = { class: badge_classes }
|
|
44
|
-
attrs.merge!(html_options)
|
|
45
|
-
attrs.merge!(build_data)
|
|
46
|
-
attrs.compact
|
|
47
|
-
end
|
|
48
37
|
end
|
|
49
38
|
end
|
|
@@ -10,11 +10,15 @@ module Shadcn
|
|
|
10
10
|
# Common attributes shared by all components
|
|
11
11
|
attr_reader :class_name, :data, :html_options
|
|
12
12
|
|
|
13
|
-
# @param class_name [String, nil] Additional CSS classes
|
|
13
|
+
# @param class_name [String, nil] Additional CSS classes (preferred)
|
|
14
|
+
# @param class [String, nil] Alias for class_name (for Rails-like API)
|
|
14
15
|
# @param data [Hash] Data attributes (will be prefixed with data-)
|
|
15
16
|
# @param html_options [Hash] Additional HTML attributes
|
|
16
17
|
def initialize(class_name: nil, data: {}, **html_options, &block)
|
|
17
|
-
|
|
18
|
+
# Support both class: and class_name: for better Rails compatibility
|
|
19
|
+
# class_name takes precedence if both are provided
|
|
20
|
+
html_class = html_options.delete(:class)
|
|
21
|
+
@class_name = class_name || html_class
|
|
18
22
|
@data = data
|
|
19
23
|
@html_options = html_options
|
|
20
24
|
@constructor_block = block
|
|
@@ -96,5 +100,14 @@ module Shadcn
|
|
|
96
100
|
|
|
97
101
|
classes.split.map { |c| "#{config.tailwind_prefix}#{c}" }.join(" ")
|
|
98
102
|
end
|
|
103
|
+
|
|
104
|
+
# Build HTML attributes string for use in templates
|
|
105
|
+
# Combines html_options and data attributes
|
|
106
|
+
# Uses html_escape_once to avoid double-escaping already-escaped content
|
|
107
|
+
# @return [String] HTML-safe attribute string
|
|
108
|
+
def tag_attributes
|
|
109
|
+
attrs = html_options.merge(build_data)
|
|
110
|
+
attrs.map { |k, v| "#{k}=\"#{ERB::Util.html_escape_once(v)}\"" if v }.compact.join(" ").html_safe
|
|
111
|
+
end
|
|
99
112
|
end
|
|
100
113
|
end
|
|
@@ -21,24 +21,14 @@ module Shadcn
|
|
|
21
21
|
)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def call
|
|
25
|
-
content_tag(:nav, breadcrumb_attributes) do
|
|
26
|
-
content_tag(:ol, class: "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5") do
|
|
27
|
-
safe_join(items_with_separators)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
24
|
private
|
|
33
25
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
attrs.merge!(build_data)
|
|
41
|
-
attrs.compact
|
|
26
|
+
def breadcrumb_classes
|
|
27
|
+
merge_classes("")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def breadcrumb_list_content
|
|
31
|
+
safe_join(items_with_separators)
|
|
42
32
|
end
|
|
43
33
|
|
|
44
34
|
def items_with_separators
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<% if @href %>
|
|
2
|
+
<a href="<%= @href %>"
|
|
3
|
+
class="<%= button_classes %>"
|
|
4
|
+
role="button"
|
|
5
|
+
<%= "aria-disabled=true tabindex=-1" if @disabled %>
|
|
6
|
+
<%= tag_attributes %>>
|
|
7
|
+
<%= button_content %>
|
|
8
|
+
</a>
|
|
9
|
+
<% else %>
|
|
10
|
+
<button type="<%= @type %>"
|
|
11
|
+
class="<%= button_classes %>"
|
|
12
|
+
<%= "disabled" if @disabled || @loading %>
|
|
13
|
+
<%= "aria-disabled=true" if @disabled || @loading %>
|
|
14
|
+
<%= "aria-busy=true" if @loading %>
|
|
15
|
+
<%= tag_attributes %>>
|
|
16
|
+
<%= button_content %>
|
|
17
|
+
</button>
|
|
18
|
+
<% end %>
|
|
@@ -75,24 +75,8 @@ module Shadcn
|
|
|
75
75
|
@loading = loading
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
def call
|
|
79
|
-
if @href
|
|
80
|
-
link_tag
|
|
81
|
-
else
|
|
82
|
-
button_tag
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
78
|
private
|
|
87
79
|
|
|
88
|
-
def button_tag
|
|
89
|
-
content_tag(:button, button_content, button_attributes)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def link_tag
|
|
93
|
-
content_tag(:a, button_content, link_attributes)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
80
|
def button_content
|
|
97
81
|
if @loading
|
|
98
82
|
safe_join([loading_spinner, content])
|
|
@@ -102,7 +86,7 @@ module Shadcn
|
|
|
102
86
|
end
|
|
103
87
|
|
|
104
88
|
def loading_spinner
|
|
105
|
-
|
|
89
|
+
tag.span("", class: "animate-spin h-4 w-4 border-2 border-current border-t-transparent rounded-full", "aria-hidden": true)
|
|
106
90
|
end
|
|
107
91
|
|
|
108
92
|
def button_classes
|
|
@@ -113,29 +97,5 @@ module Shadcn
|
|
|
113
97
|
class_name
|
|
114
98
|
)
|
|
115
99
|
end
|
|
116
|
-
|
|
117
|
-
def button_attributes
|
|
118
|
-
attrs = html_options.merge(
|
|
119
|
-
type: @type,
|
|
120
|
-
class: button_classes,
|
|
121
|
-
disabled: @disabled || @loading || nil,
|
|
122
|
-
"aria-disabled": (@disabled || @loading) ? "true" : nil,
|
|
123
|
-
"aria-busy": @loading ? "true" : nil
|
|
124
|
-
)
|
|
125
|
-
attrs.merge!(build_data)
|
|
126
|
-
attrs.compact
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def link_attributes
|
|
130
|
-
attrs = html_options.merge(
|
|
131
|
-
href: @href,
|
|
132
|
-
class: button_classes,
|
|
133
|
-
role: "button",
|
|
134
|
-
"aria-disabled": @disabled ? "true" : nil,
|
|
135
|
-
tabindex: @disabled ? "-1" : nil
|
|
136
|
-
)
|
|
137
|
-
attrs.merge!(build_data)
|
|
138
|
-
attrs.compact
|
|
139
|
-
end
|
|
140
100
|
end
|
|
141
101
|
end
|
|
@@ -50,14 +50,10 @@ module Shadcn
|
|
|
50
50
|
|
|
51
51
|
BASE_CLASSES = "rounded-xl border bg-card text-card-foreground shadow"
|
|
52
52
|
|
|
53
|
-
def call
|
|
54
|
-
content_tag(:div, card_content, class: merge_classes(BASE_CLASSES), **html_options.merge(build_data))
|
|
55
|
-
end
|
|
56
|
-
|
|
57
53
|
private
|
|
58
54
|
|
|
59
|
-
def
|
|
60
|
-
|
|
55
|
+
def card_classes
|
|
56
|
+
merge_classes(BASE_CLASSES)
|
|
61
57
|
end
|
|
62
58
|
end
|
|
63
59
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<% if has_label? %>
|
|
2
|
+
<label class="flex items-center space-x-2 cursor-pointer">
|
|
3
|
+
<% if @name %>
|
|
4
|
+
<input type="hidden" name="<%= ERB::Util.html_escape_once(@name) %>" value="0" autocomplete="off">
|
|
5
|
+
<% end %>
|
|
6
|
+
<input type="checkbox"
|
|
7
|
+
class="<%= checkbox_classes %>"<% if @name %>
|
|
8
|
+
name="<%= ERB::Util.html_escape_once(@name) %>"<% end %><% if @id %>
|
|
9
|
+
id="<%= ERB::Util.html_escape_once(@id) %>"<% end %><% if @value %>
|
|
10
|
+
value="<%= ERB::Util.html_escape_once(@value) %>"<% end %><% if @checked %>
|
|
11
|
+
checked<% end %><% if @disabled %>
|
|
12
|
+
disabled<% end %><% if @required %>
|
|
13
|
+
required<% end %>
|
|
14
|
+
<%= tag_attributes %>>
|
|
15
|
+
<span class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
16
|
+
<%= content %>
|
|
17
|
+
</span>
|
|
18
|
+
</label>
|
|
19
|
+
<% else %>
|
|
20
|
+
<% if @name %>
|
|
21
|
+
<input type="hidden" name="<%= ERB::Util.html_escape_once(@name) %>" value="0" autocomplete="off">
|
|
22
|
+
<% end %>
|
|
23
|
+
<input type="checkbox"
|
|
24
|
+
class="<%= checkbox_classes %>"<% if @name %>
|
|
25
|
+
name="<%= ERB::Util.html_escape_once(@name) %>"<% end %><% if @id %>
|
|
26
|
+
id="<%= ERB::Util.html_escape_once(@id) %>"<% end %><% if @value %>
|
|
27
|
+
value="<%= ERB::Util.html_escape_once(@value) %>"<% end %><% if @checked %>
|
|
28
|
+
checked<% end %><% if @disabled %>
|
|
29
|
+
disabled<% end %><% if @required %>
|
|
30
|
+
required<% end %>
|
|
31
|
+
<%= tag_attributes %>>
|
|
32
|
+
<% end %>
|
|
@@ -51,53 +51,14 @@ module Shadcn
|
|
|
51
51
|
@required = required
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def call
|
|
55
|
-
if content.present?
|
|
56
|
-
# Render with integrated label
|
|
57
|
-
content_tag(:label, class: "flex items-center space-x-2 cursor-pointer") do
|
|
58
|
-
safe_join([
|
|
59
|
-
hidden_input,
|
|
60
|
-
checkbox_input,
|
|
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
|
-
# Render just the checkbox (for use with external labels)
|
|
66
|
-
safe_join([hidden_input, checkbox_input].compact)
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
54
|
private
|
|
71
55
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
return unless @name
|
|
75
|
-
|
|
76
|
-
tag(:input,
|
|
77
|
-
type: "hidden",
|
|
78
|
-
name: @name,
|
|
79
|
-
value: "0",
|
|
80
|
-
autocomplete: "off"
|
|
81
|
-
)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def checkbox_input
|
|
85
|
-
tag(:input, input_attributes)
|
|
56
|
+
def checkbox_classes
|
|
57
|
+
cn(BASE_CLASSES, class_name)
|
|
86
58
|
end
|
|
87
59
|
|
|
88
|
-
def
|
|
89
|
-
|
|
90
|
-
type: "checkbox",
|
|
91
|
-
name: @name,
|
|
92
|
-
id: @id,
|
|
93
|
-
value: @value,
|
|
94
|
-
class: cn(BASE_CLASSES, class_name),
|
|
95
|
-
disabled: @disabled || nil,
|
|
96
|
-
checked: @checked || nil,
|
|
97
|
-
required: @required || nil
|
|
98
|
-
}
|
|
99
|
-
attrs.merge!(html_options.except(:class))
|
|
100
|
-
attrs.compact
|
|
60
|
+
def has_label?
|
|
61
|
+
content.present?
|
|
101
62
|
end
|
|
102
63
|
end
|
|
103
64
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<div class="<%= collapsible_classes %>"
|
|
2
|
+
data-controller="shadcn--collapsible"
|
|
3
|
+
data-shadcn--collapsible-open-value="<%= @open %>"
|
|
4
|
+
data-shadcn--collapsible-disabled-value="<%= @disabled %>"
|
|
5
|
+
data-state="<%= state %>"
|
|
6
|
+
<%= tag_attributes %>>
|
|
7
|
+
<%= collapsible_content %>
|
|
8
|
+
</div>
|
|
@@ -31,12 +31,12 @@ module Shadcn
|
|
|
31
31
|
@disabled = disabled
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def call
|
|
35
|
-
content_tag(:div, collapsible_content, collapsible_attributes)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
34
|
private
|
|
39
35
|
|
|
36
|
+
def collapsible_classes
|
|
37
|
+
class_name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
40
|
def collapsible_content
|
|
41
41
|
safe_join([trigger_wrapper, body].compact)
|
|
42
42
|
end
|
|
@@ -50,17 +50,8 @@ module Shadcn
|
|
|
50
50
|
})
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
class: class_name,
|
|
56
|
-
"data-controller": "shadcn--collapsible",
|
|
57
|
-
"data-shadcn--collapsible-open-value": @open.to_s,
|
|
58
|
-
"data-shadcn--collapsible-disabled-value": @disabled.to_s,
|
|
59
|
-
"data-state": @open ? "open" : "closed"
|
|
60
|
-
}
|
|
61
|
-
attrs.merge!(html_options)
|
|
62
|
-
attrs.merge!(build_data)
|
|
63
|
-
attrs.compact
|
|
53
|
+
def state
|
|
54
|
+
@open ? "open" : "closed"
|
|
64
55
|
end
|
|
65
56
|
end
|
|
66
57
|
end
|
|
@@ -10,19 +10,26 @@ module Shadcn
|
|
|
10
10
|
CommandEmptyComponent.new(**options)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
renders_many :
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
# Use polymorphic slots to preserve the order of groups, items, and separators
|
|
14
|
+
renders_many :list_items, types: {
|
|
15
|
+
group: {
|
|
16
|
+
renders: lambda { |heading: nil, **options, &block|
|
|
17
|
+
CommandGroupComponent.new(heading: heading, **options, &block)
|
|
18
|
+
},
|
|
19
|
+
as: :group
|
|
20
|
+
},
|
|
21
|
+
item: {
|
|
22
|
+
renders: lambda { |value: nil, disabled: false, **options, &block|
|
|
23
|
+
CommandItemComponent.new(value: value, disabled: disabled, **options, &block)
|
|
24
|
+
},
|
|
25
|
+
as: :item
|
|
26
|
+
},
|
|
27
|
+
separator: {
|
|
28
|
+
renders: lambda { |**options|
|
|
29
|
+
CommandSeparatorComponent.new(**options)
|
|
30
|
+
},
|
|
31
|
+
as: :separator
|
|
32
|
+
}
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
def call
|
|
@@ -32,7 +39,15 @@ module Shadcn
|
|
|
32
39
|
private
|
|
33
40
|
|
|
34
41
|
def list_content
|
|
35
|
-
|
|
42
|
+
# Trigger slot evaluation first by accessing content
|
|
43
|
+
raw_content = content
|
|
44
|
+
# If polymorphic slots were used, render them in order with empty at the start
|
|
45
|
+
if list_items.any?
|
|
46
|
+
safe_join([empty, list_items].flatten.compact)
|
|
47
|
+
else
|
|
48
|
+
# Otherwise render the raw block content (for backwards compatibility)
|
|
49
|
+
safe_join([empty, raw_content].flatten.compact)
|
|
50
|
+
end
|
|
36
51
|
end
|
|
37
52
|
end
|
|
38
53
|
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Shadcn
|
|
4
|
+
# Context Menu Checkbox Item component
|
|
5
|
+
# A menu item that can be checked/unchecked
|
|
6
|
+
class ContextMenuCheckboxItemComponent < BaseComponent
|
|
7
|
+
BASE_CLASSES = "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
|
|
8
|
+
|
|
9
|
+
renders_one :shortcut, lambda { |**options|
|
|
10
|
+
ContextMenuShortcutComponent.new(**options)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# @param checked [Boolean] Whether item is checked
|
|
14
|
+
# @param disabled [Boolean] Whether item is disabled
|
|
15
|
+
def initialize(checked: false, disabled: false, **options, &block)
|
|
16
|
+
super(**options, &block)
|
|
17
|
+
@checked = checked
|
|
18
|
+
@disabled = disabled
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
content_tag(:div, item_content, item_attributes)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def item_content
|
|
28
|
+
safe_join([
|
|
29
|
+
check_indicator,
|
|
30
|
+
content,
|
|
31
|
+
shortcut
|
|
32
|
+
].compact)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def check_indicator
|
|
36
|
+
content_tag(:span, check_icon, class: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def check_icon
|
|
40
|
+
return "" unless @checked
|
|
41
|
+
|
|
42
|
+
content_tag(:svg, check_svg_path, {
|
|
43
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
44
|
+
width: "16",
|
|
45
|
+
height: "16",
|
|
46
|
+
viewBox: "0 0 24 24",
|
|
47
|
+
fill: "none",
|
|
48
|
+
stroke: "currentColor",
|
|
49
|
+
"stroke-width": "2",
|
|
50
|
+
"stroke-linecap": "round",
|
|
51
|
+
"stroke-linejoin": "round",
|
|
52
|
+
class: "h-4 w-4"
|
|
53
|
+
})
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def check_svg_path
|
|
57
|
+
content_tag(:polyline, "", points: "20 6 9 17 4 12")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def item_attributes
|
|
61
|
+
attrs = {
|
|
62
|
+
class: cn(BASE_CLASSES, class_name),
|
|
63
|
+
role: "menuitemcheckbox",
|
|
64
|
+
"aria-checked": @checked.to_s,
|
|
65
|
+
tabindex: @disabled ? nil : "-1",
|
|
66
|
+
"data-disabled": @disabled ? "" : nil,
|
|
67
|
+
"data-state": @checked ? "checked" : "unchecked",
|
|
68
|
+
"data-shadcn--context-menu-target": "item",
|
|
69
|
+
"data-action": "click->shadcn--context-menu#selectItem"
|
|
70
|
+
}
|
|
71
|
+
attrs.merge!(html_options)
|
|
72
|
+
attrs.merge!(build_data)
|
|
73
|
+
attrs.compact
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<div class="<%= context_menu_classes %>"
|
|
2
|
+
data-controller="<%= context_menu_data_attrs[:controller] %>"
|
|
3
|
+
data-action="<%= context_menu_data_attrs[:action] %>"
|
|
4
|
+
<%= tag_attributes %>>
|
|
5
|
+
<% if trigger? %>
|
|
6
|
+
<div data-shadcn--context-menu-target="trigger" data-action="contextmenu->shadcn--context-menu#show:prevent">
|
|
7
|
+
<%= trigger %>
|
|
8
|
+
</div>
|
|
9
|
+
<% end %>
|
|
10
|
+
<%= menu if menu? %>
|
|
11
|
+
</div>
|